growthbook 0.0.1 → 0.3.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/lib/growthbook/conditions.rb +174 -0
- data/lib/growthbook/context.rb +233 -0
- data/lib/growthbook/feature.rb +41 -0
- data/lib/growthbook/feature_result.rb +61 -0
- data/lib/growthbook/feature_rule.rb +78 -0
- data/lib/growthbook/inline_experiment.rb +80 -0
- data/lib/growthbook/inline_experiment_result.rb +62 -0
- data/lib/growthbook/util.rb +116 -15
- data/lib/growthbook.rb +11 -2
- data/spec/cases.json +2923 -0
- data/spec/client_spec.rb +57 -0
- data/spec/context_spec.rb +124 -0
- data/spec/json_spec.rb +160 -0
- data/spec/user_spec.rb +213 -0
- data/spec/util_spec.rb +154 -0
- metadata +22 -3
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'growthbook'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
describe 'client' do
|
5
|
+
describe ".enabled" do
|
6
|
+
it "chooses variation -1 when client is disabled" do
|
7
|
+
client = Growthbook::Client.new(enabled: false)
|
8
|
+
user = client.user(id: "1")
|
9
|
+
experiment = Growthbook::Experiment.new("my-test", 2)
|
10
|
+
expect(user.experiment(experiment).variation).to eq(-1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
describe ".importExperimentsHash" do
|
14
|
+
it("imports correctly") do
|
15
|
+
client = Growthbook::Client.new
|
16
|
+
|
17
|
+
# Example JSON response from the GrowthBook API
|
18
|
+
json = '{
|
19
|
+
"status": 200,
|
20
|
+
"experiments": {
|
21
|
+
"my-test": {
|
22
|
+
"variations": 2,
|
23
|
+
"coverage": 0.6,
|
24
|
+
"weights": [0.8, 0.2],
|
25
|
+
"anon": true,
|
26
|
+
"force": 1,
|
27
|
+
"targeting": [
|
28
|
+
"source = google"
|
29
|
+
],
|
30
|
+
"data": {
|
31
|
+
"color": ["blue", "green"]
|
32
|
+
}
|
33
|
+
},
|
34
|
+
"my-stopped-test": {
|
35
|
+
"variations": 3,
|
36
|
+
"force": 1
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}'
|
40
|
+
|
41
|
+
parsed = JSON.parse(json)
|
42
|
+
client.importExperimentsHash(parsed["experiments"])
|
43
|
+
|
44
|
+
expect(client.experiments.length).to eq(2)
|
45
|
+
|
46
|
+
experiment = client.experiments[0]
|
47
|
+
expect(experiment.id).to eq("my-test")
|
48
|
+
expect(experiment.variations).to eq(2)
|
49
|
+
expect(experiment.coverage).to eq(0.6)
|
50
|
+
expect(experiment.weights[0]).to eq(0.8)
|
51
|
+
expect(experiment.anon).to eq(true)
|
52
|
+
expect(experiment.force).to eq(1)
|
53
|
+
expect(experiment.targeting[0]).to eq("source = google")
|
54
|
+
expect(experiment.data["color"][0]).to eq("blue")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'growthbook'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
describe 'context' do
|
5
|
+
describe "feature helper methods" do
|
6
|
+
gb = Growthbook::Context.new(
|
7
|
+
features: {
|
8
|
+
feature1: {
|
9
|
+
defaultValue: 1
|
10
|
+
},
|
11
|
+
feature2: {
|
12
|
+
defaultValue: 0
|
13
|
+
}
|
14
|
+
}
|
15
|
+
)
|
16
|
+
|
17
|
+
it ".on?" do
|
18
|
+
expect(gb.on?(:feature1)).to eq(true)
|
19
|
+
expect(gb.on?(:feature2)).to eq(false)
|
20
|
+
end
|
21
|
+
it ".off?" do
|
22
|
+
expect(gb.off?(:feature1)).to eq(false)
|
23
|
+
expect(gb.off?(:feature2)).to eq(true)
|
24
|
+
end
|
25
|
+
it ".feature_value" do
|
26
|
+
expect(gb.feature_value(:feature1)).to eq(1)
|
27
|
+
expect(gb.feature_value(:feature2)).to eq(0)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "forced feature values" do
|
32
|
+
it "uses forced values" do
|
33
|
+
gb = Growthbook::Context.new(
|
34
|
+
features: {
|
35
|
+
feature: {
|
36
|
+
defaultValue: "a"
|
37
|
+
},
|
38
|
+
feature2: {
|
39
|
+
defaultValue: true
|
40
|
+
}
|
41
|
+
}
|
42
|
+
)
|
43
|
+
|
44
|
+
gb.forced_features = {
|
45
|
+
feature: "b",
|
46
|
+
another: 2
|
47
|
+
}
|
48
|
+
|
49
|
+
expect(gb.feature_value(:feature)).to eq("b")
|
50
|
+
expect(gb.feature_value(:feature2)).to eq(true)
|
51
|
+
expect(gb.feature_value(:another)).to eq(2)
|
52
|
+
expect(gb.feature_value(:unknown)).to eq(nil)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "tracking" do
|
57
|
+
it "queues up impressions" do
|
58
|
+
class MyImpressionListener
|
59
|
+
attr_accessor :tracked
|
60
|
+
def on_experiment_viewed(exp, res)
|
61
|
+
@tracked = [exp.to_json, res.to_json]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
listener = MyImpressionListener.new
|
66
|
+
|
67
|
+
gb = Growthbook::Context.new(
|
68
|
+
attributes: {
|
69
|
+
id: "123"
|
70
|
+
},
|
71
|
+
features: {
|
72
|
+
feature1: {
|
73
|
+
defaultValue: 1,
|
74
|
+
rules: [
|
75
|
+
{
|
76
|
+
variations: [2, 3]
|
77
|
+
}
|
78
|
+
]
|
79
|
+
},
|
80
|
+
feature2: {
|
81
|
+
defaultValue: 0,
|
82
|
+
rules: [
|
83
|
+
{
|
84
|
+
variations: [4, 5]
|
85
|
+
}
|
86
|
+
]
|
87
|
+
}
|
88
|
+
},
|
89
|
+
listener: listener
|
90
|
+
)
|
91
|
+
|
92
|
+
expect(gb.impressions).to eq({})
|
93
|
+
expect(listener.tracked).to eq(nil)
|
94
|
+
|
95
|
+
gb.on? :feature1
|
96
|
+
|
97
|
+
expect(gb.impressions["feature1"].to_json).to eq({
|
98
|
+
"featureId" => "feature1",
|
99
|
+
"hashAttribute" => "id",
|
100
|
+
"hashValue" => "123",
|
101
|
+
"inExperiment" => true,
|
102
|
+
"hashUsed" => true,
|
103
|
+
"value" => 2,
|
104
|
+
"variationId" => 0,
|
105
|
+
})
|
106
|
+
|
107
|
+
expect(listener.tracked).to eq([
|
108
|
+
{
|
109
|
+
"key" => "feature1",
|
110
|
+
"variations" => [2, 3]
|
111
|
+
},
|
112
|
+
{
|
113
|
+
"featureId" => "feature1",
|
114
|
+
"hashAttribute" => "id",
|
115
|
+
"hashValue" => "123",
|
116
|
+
"inExperiment" => true,
|
117
|
+
"hashUsed" => true,
|
118
|
+
"value" => 2,
|
119
|
+
"variationId" => 0,
|
120
|
+
}
|
121
|
+
])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/spec/json_spec.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'growthbook'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
file = File.read(File.join(File.dirname(__FILE__), 'cases.json'))
|
7
|
+
test_cases = JSON.parse(file)
|
8
|
+
|
9
|
+
def roundArray(arr)
|
10
|
+
arr.map do |v|
|
11
|
+
v.is_a?(Float) || v.is_a?(Integer) ? v.round(5) : roundArray(v)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'test suite' do
|
16
|
+
describe 'hash' do
|
17
|
+
test_cases['hash'].each do |test_case|
|
18
|
+
value, expected = test_case
|
19
|
+
|
20
|
+
it value do
|
21
|
+
result = Growthbook::Util.hash(value)
|
22
|
+
expect(result.round(5)).to eq expected.round(5)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'getBucketRanges' do
|
28
|
+
# Loop through each test case in the JSON file
|
29
|
+
test_cases['getBucketRange'].each do |test_case|
|
30
|
+
# Extract data about the test case
|
31
|
+
test_name, args, expected = test_case
|
32
|
+
num_variations, coverage, weights = args
|
33
|
+
|
34
|
+
# Run the actual test case
|
35
|
+
it test_name do
|
36
|
+
result = Growthbook::Util.get_bucket_ranges(
|
37
|
+
num_variations,
|
38
|
+
coverage,
|
39
|
+
weights
|
40
|
+
)
|
41
|
+
|
42
|
+
expect(roundArray(result)).to eq(roundArray(expected))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'chooseVariation' do
|
48
|
+
# Loop through each test case in the JSON file
|
49
|
+
test_cases['chooseVariation'].each do |test_case|
|
50
|
+
# Extract data about the test case
|
51
|
+
test_name, n, ranges, expected = test_case
|
52
|
+
|
53
|
+
# Run the actual test case
|
54
|
+
it test_name do
|
55
|
+
result = Growthbook::Util.choose_variation(n, ranges)
|
56
|
+
expect(result).to eq(expected)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'getQueryStringOverride' do
|
62
|
+
# Loop through each test case in the JSON file
|
63
|
+
test_cases['getQueryStringOverride'].each do |test_case|
|
64
|
+
# Extract data about the test case
|
65
|
+
test_name, key, url, num_variations, expected = test_case
|
66
|
+
|
67
|
+
# Run the actual test case
|
68
|
+
it test_name do
|
69
|
+
result = Growthbook::Util.get_query_string_override(
|
70
|
+
key,
|
71
|
+
url,
|
72
|
+
num_variations
|
73
|
+
)
|
74
|
+
expect(result).to eq(expected)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'inNamespace' do
|
80
|
+
# Loop through each test case in the JSON file
|
81
|
+
test_cases['inNamespace'].each do |test_case|
|
82
|
+
# Extract data about the test case
|
83
|
+
test_name, id, namespace, expected = test_case
|
84
|
+
|
85
|
+
# Run the actual test case
|
86
|
+
it test_name do
|
87
|
+
result = Growthbook::Util.in_namespace(
|
88
|
+
id,
|
89
|
+
namespace
|
90
|
+
)
|
91
|
+
expect(result).to eq(expected)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'getEqualWeights' do
|
97
|
+
# Loop through each test case in the JSON file
|
98
|
+
test_cases['getEqualWeights'].each do |test_case|
|
99
|
+
# Extract data about the test case
|
100
|
+
num_variations, expected = test_case
|
101
|
+
|
102
|
+
# Run the actual test case
|
103
|
+
it num_variations.to_s do
|
104
|
+
result = Growthbook::Util.get_equal_weights(
|
105
|
+
num_variations
|
106
|
+
)
|
107
|
+
expect(roundArray(result)).to eq(roundArray(expected))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'evalCondition' do
|
113
|
+
# Loop through each test case in the JSON file
|
114
|
+
test_cases['evalCondition'].each do |test_case|
|
115
|
+
# Extract data about the test case
|
116
|
+
test_name, condition, attributes, expected = test_case
|
117
|
+
|
118
|
+
# Run the actual test case
|
119
|
+
it test_name do
|
120
|
+
result = Growthbook::Conditions.eval_condition(
|
121
|
+
attributes,
|
122
|
+
condition
|
123
|
+
)
|
124
|
+
expect(result).to eq(expected)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
describe 'feature' do
|
129
|
+
# Loop through each test case in the JSON file
|
130
|
+
test_cases['feature'].each do |test_case|
|
131
|
+
# Extract data about the test case
|
132
|
+
test_name, context, key, expected = test_case
|
133
|
+
|
134
|
+
# Run the actual test case
|
135
|
+
it test_name do
|
136
|
+
gb = Growthbook::Context.new(context)
|
137
|
+
result = gb.eval_feature(key)
|
138
|
+
expect(result.to_json).to eq(expected)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'run' do
|
144
|
+
# Loop through each test case in the JSON file
|
145
|
+
test_cases['run'].each do |test_case|
|
146
|
+
# Extract data about the test case
|
147
|
+
test_name, context, experiment, value, in_experiment, hash_used = test_case
|
148
|
+
|
149
|
+
# Run the actual test case
|
150
|
+
it test_name do
|
151
|
+
gb = Growthbook::Context.new(context)
|
152
|
+
exp = Growthbook::InlineExperiment.new(experiment)
|
153
|
+
result = gb.run(exp)
|
154
|
+
expect(result.value).to eq(value)
|
155
|
+
expect(result.in_experiment).to eq(in_experiment)
|
156
|
+
expect(result.hash_used).to eq(hash_used)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/spec/user_spec.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'growthbook'
|
2
|
+
|
3
|
+
describe 'user' do
|
4
|
+
describe ".experiment" do
|
5
|
+
it "uses experiment overrides in client first" do
|
6
|
+
client = Growthbook::Client.new
|
7
|
+
override = Growthbook::Experiment.new("my-test", 2)
|
8
|
+
client.experiments << override
|
9
|
+
|
10
|
+
experiment = Growthbook::Experiment.new("my-test", 2)
|
11
|
+
user = client.user(id: "1")
|
12
|
+
result = user.experiment(experiment)
|
13
|
+
|
14
|
+
expect(result.experiment).to eq(override)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "assigns properly with both user id and anonymous ids" do
|
18
|
+
client = Growthbook::Client.new
|
19
|
+
userOnly = client.user(id: "1")
|
20
|
+
anonOnly = client.user(anonId: "2")
|
21
|
+
both = client.user(id: "1", anonId: "2")
|
22
|
+
|
23
|
+
experimentAnon = Growthbook::Experiment.new("my-test", 2, anon:true)
|
24
|
+
experimentUser = Growthbook::Experiment.new("my-test", 2, anon:false)
|
25
|
+
|
26
|
+
expect(userOnly.experiment(experimentUser).variation).to eq(1)
|
27
|
+
expect(both.experiment(experimentUser).variation).to eq(1)
|
28
|
+
expect(anonOnly.experiment(experimentUser).variation).to eq(-1)
|
29
|
+
|
30
|
+
expect(userOnly.experiment(experimentAnon).variation).to eq(-1)
|
31
|
+
expect(both.experiment(experimentAnon).variation).to eq(0)
|
32
|
+
expect(anonOnly.experiment(experimentAnon).variation).to eq(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns variation config data" do
|
36
|
+
client = Growthbook::Client.new
|
37
|
+
user = client.user(id: "1")
|
38
|
+
experiment = Growthbook::Experiment.new("my-test", 2, data: {
|
39
|
+
:color => ["blue", "green"],
|
40
|
+
:size => ["small", "large"]
|
41
|
+
})
|
42
|
+
|
43
|
+
# Get correct config data
|
44
|
+
result = user.experiment(experiment)
|
45
|
+
expect(result.data[:color]).to eq("green")
|
46
|
+
expect(result.data[:size]).to eq("large")
|
47
|
+
|
48
|
+
# Fallback to control config data if not in test
|
49
|
+
experiment.coverage = 0.01
|
50
|
+
result = user.experiment(experiment)
|
51
|
+
expect(result.data[:color]).to eq("blue")
|
52
|
+
expect(result.data[:size]).to eq("small")
|
53
|
+
|
54
|
+
# Null for undefined keys
|
55
|
+
expect(result.data[:unknown]).to eq(nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "uses forced variations properly" do
|
59
|
+
client = Growthbook::Client.new
|
60
|
+
experiment = Growthbook::Experiment.new("my-test", 2, force: -1)
|
61
|
+
user = client.user(id: "1")
|
62
|
+
|
63
|
+
expect(user.experiment(experiment).variation).to eq(-1)
|
64
|
+
experiment.force = 0
|
65
|
+
expect(user.experiment(experiment).variation).to eq(0)
|
66
|
+
experiment.force = 1
|
67
|
+
expect(user.experiment(experiment).variation).to eq(1)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "evaluates targeting before forced variation" do
|
71
|
+
client = Growthbook::Client.new
|
72
|
+
experiment = Growthbook::Experiment.new("my-test", 2, force: 1, targeting: ["age > 18"])
|
73
|
+
user = client.user(id: "1")
|
74
|
+
|
75
|
+
expect(user.experiment(experiment).variation).to eq(-1)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "sets the shouldTrack flag on results" do
|
79
|
+
client = Growthbook::Client.new
|
80
|
+
experiment = Growthbook::Experiment.new("my-test", 2, data: {"color" => ["blue", "green"]})
|
81
|
+
client.experiments << experiment
|
82
|
+
user = client.user(id: "1")
|
83
|
+
|
84
|
+
# Normal
|
85
|
+
expect(user.experiment("my-test").shouldTrack?).to eq(true)
|
86
|
+
expect(user.experiment("my-test").forced?).to eq(false)
|
87
|
+
expect(user.lookupByDataKey("color").shouldTrack?).to eq(true)
|
88
|
+
expect(user.lookupByDataKey("color").forced?).to eq(false)
|
89
|
+
|
90
|
+
# Failed coverage
|
91
|
+
experiment.coverage = 0.01
|
92
|
+
expect(user.experiment("my-test").shouldTrack?).to eq(false)
|
93
|
+
expect(user.experiment("my-test").forced?).to eq(false)
|
94
|
+
expect(user.lookupByDataKey("color")).to eq(nil)
|
95
|
+
|
96
|
+
# Forced variation
|
97
|
+
experiment.coverage = 1.0
|
98
|
+
experiment.force = 1
|
99
|
+
expect(user.experiment("my-test").shouldTrack?).to eq(false)
|
100
|
+
expect(user.experiment("my-test").forced?).to eq(true)
|
101
|
+
expect(user.lookupByDataKey("color").shouldTrack?).to eq(false)
|
102
|
+
expect(user.lookupByDataKey("color").forced?).to eq(true)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can target an experiment given rules and attributes" do
|
106
|
+
client = Growthbook::Client.new
|
107
|
+
experiment = Growthbook::Experiment.new("my-test", 2, targeting: [
|
108
|
+
"member = true",
|
109
|
+
"age > 18",
|
110
|
+
"source ~ (google|yahoo)",
|
111
|
+
"name != matt",
|
112
|
+
"email !~ ^.*@exclude.com$"
|
113
|
+
])
|
114
|
+
|
115
|
+
attributes = {
|
116
|
+
:member => true,
|
117
|
+
:age => 21,
|
118
|
+
:source => 'yahoo',
|
119
|
+
:name => 'george',
|
120
|
+
:email => 'test@example.com'
|
121
|
+
}
|
122
|
+
|
123
|
+
# Matches all
|
124
|
+
user = client.user(id: "1", attributes: attributes)
|
125
|
+
expect(user.experiment(experiment).variation).to eq(1)
|
126
|
+
|
127
|
+
# Missing negative checks
|
128
|
+
user.attributes={
|
129
|
+
:member => true,
|
130
|
+
:age => 21,
|
131
|
+
:source => "yahoo"
|
132
|
+
}
|
133
|
+
expect(user.experiment(experiment).variation).to eq(1)
|
134
|
+
|
135
|
+
# Fails boolean
|
136
|
+
user.attributes=attributes.merge({
|
137
|
+
:member => false
|
138
|
+
})
|
139
|
+
expect(user.experiment(experiment).variation).to eq(-1)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "resultsToTrack" do
|
144
|
+
it "queues up results" do
|
145
|
+
client = Growthbook::Client.new
|
146
|
+
user = client.user(id: "1")
|
147
|
+
|
148
|
+
user.experiment(Growthbook::Experiment.new("my-test", 2))
|
149
|
+
user.experiment(Growthbook::Experiment.new("my-test2", 2))
|
150
|
+
user.experiment(Growthbook::Experiment.new("my-test3", 2))
|
151
|
+
|
152
|
+
expect(user.resultsToTrack.length).to eq(3)
|
153
|
+
end
|
154
|
+
it "ignores duplicates" do
|
155
|
+
client = Growthbook::Client.new
|
156
|
+
user = client.user(id: "1")
|
157
|
+
user.experiment(Growthbook::Experiment.new("my-test", 2))
|
158
|
+
user.experiment(Growthbook::Experiment.new("my-test", 2))
|
159
|
+
|
160
|
+
expect(user.resultsToTrack.length).to eq(1)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe ".lookupByDataKey" do
|
165
|
+
before(:all) do
|
166
|
+
@client = Growthbook::Client.new
|
167
|
+
@client.experiments << Growthbook::Experiment.new(
|
168
|
+
"button-color-size-chrome",
|
169
|
+
2,
|
170
|
+
:targeting => ["browser = chrome"],
|
171
|
+
:data => {
|
172
|
+
"button.color" => ["blue", "green"],
|
173
|
+
"button.size" => ["small", "large"]
|
174
|
+
}
|
175
|
+
)
|
176
|
+
@client.experiments << Growthbook::Experiment.new(
|
177
|
+
"button-color-safari",
|
178
|
+
2,
|
179
|
+
:targeting => ["browser = safari"],
|
180
|
+
:data => {
|
181
|
+
"button.color" => ["blue", "green"]
|
182
|
+
}
|
183
|
+
)
|
184
|
+
end
|
185
|
+
it "returns nil when there are no matches" do
|
186
|
+
user = @client.user(id: "1")
|
187
|
+
|
188
|
+
# No matches
|
189
|
+
expect(user.lookupByDataKey("button.unknown")).to eq(nil)
|
190
|
+
end
|
191
|
+
it "returns the first matching experiment" do
|
192
|
+
user = @client.user(id: "1", attributes: {:browser => "chrome"})
|
193
|
+
|
194
|
+
color = user.lookupByDataKey("button.color")
|
195
|
+
expect(color.value).to eq("blue")
|
196
|
+
expect(color.experiment.id).to eq("button-color-size-chrome")
|
197
|
+
|
198
|
+
size = user.lookupByDataKey("button.size")
|
199
|
+
expect(size.value).to eq("small")
|
200
|
+
expect(size.experiment.id).to eq("button-color-size-chrome")
|
201
|
+
end
|
202
|
+
it "skips experiments that fail targeting rules" do
|
203
|
+
user = @client.user(id: "1", attributes: {:browser => "safari"})
|
204
|
+
|
205
|
+
color = user.lookupByDataKey("button.color")
|
206
|
+
expect(color.value).to eq("blue")
|
207
|
+
expect(color.experiment.id).to eq("button-color-safari")
|
208
|
+
|
209
|
+
size = user.lookupByDataKey("button.size")
|
210
|
+
expect(size).to eq(nil)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|