flipper-ui 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/ui/basic.ru +8 -0
- data/lib/flipper/ui/action.rb +10 -2
- data/lib/flipper/ui/actions/actors_gate.rb +1 -1
- data/lib/flipper/ui/actions/add_feature.rb +1 -1
- data/lib/flipper/ui/actions/boolean_gate.rb +1 -1
- data/lib/flipper/ui/actions/feature.rb +2 -1
- data/lib/flipper/ui/actions/features.rb +1 -1
- data/lib/flipper/ui/actions/file.rb +7 -2
- data/lib/flipper/ui/actions/groups_gate.rb +1 -1
- data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +1 -1
- data/lib/flipper/ui/actions/percentage_of_time_gate.rb +1 -1
- data/lib/flipper/ui/configuration.rb +7 -0
- data/lib/flipper/ui/decorators/feature.rb +4 -0
- data/lib/flipper/ui/views/feature.erb +48 -49
- data/lib/flipper/ui/views/features.erb +3 -3
- data/lib/flipper/ui/views/layout.erb +1 -1
- data/lib/flipper/ui/views/read_only.erb +9 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/actors_gate_spec.rb +17 -0
- data/spec/flipper/ui/actions/add_feature_spec.rb +15 -0
- data/spec/flipper/ui/actions/export_spec.rb +5 -3
- data/spec/flipper/ui/actions/feature_spec.rb +35 -0
- data/spec/flipper/ui/actions/features_spec.rb +14 -0
- data/spec/flipper/ui/configuration_spec.rb +20 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2680cc3ed0d05e2a1b100b0f37013e555f44bb3bbc4331a5db58c07a649c27e5
|
4
|
+
data.tar.gz: 4ff283072612faf8b9af38da0c30117e85379b241e8da74b652fe218437460f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 453c8df0b09edd877b2569b4bdb2b8a1aedb81c6c1822c33125b6676a40fa45b25b1fe6b53f048717215b3950729256bc1f9224486e060b6bd3fa3eec0fbcdf1
|
7
|
+
data.tar.gz: 251f22f42d786857ac494d66cf5e75bce1c20236850ae2f07740a9ebd94a7cf281327bdf2789437e6aa1856f6c09e06bfd621fd8ed3e9fc06acdb03a01dd06c3
|
data/examples/ui/basic.ru
CHANGED
@@ -25,6 +25,7 @@ Flipper::UI.configure do |config|
|
|
25
25
|
config.feature_removal_enabled = true
|
26
26
|
config.cloud_recommendation = true
|
27
27
|
config.confirm_fully_enable = false
|
28
|
+
config.read_only = false
|
28
29
|
# config.show_feature_description_in_list = true
|
29
30
|
config.descriptions_source = lambda do |_keys|
|
30
31
|
{
|
@@ -38,6 +39,13 @@ Flipper::UI.configure do |config|
|
|
38
39
|
"a/b" => "Why would someone use a slash? I don't know but someone did. Let's make this really long so they regret using slashes. Please don't use slashes.",
|
39
40
|
}
|
40
41
|
end
|
42
|
+
|
43
|
+
config.actor_names_source = lambda do |_keys|
|
44
|
+
{
|
45
|
+
'1' => 'John',
|
46
|
+
'6' => 'Brandon',
|
47
|
+
}
|
48
|
+
end
|
41
49
|
end
|
42
50
|
|
43
51
|
# You can uncomment these to get some default data:
|
data/lib/flipper/ui/action.rb
CHANGED
@@ -176,7 +176,7 @@ module Flipper
|
|
176
176
|
when String
|
177
177
|
object
|
178
178
|
else
|
179
|
-
|
179
|
+
Typecast.to_json(object)
|
180
180
|
end
|
181
181
|
halt [@code, @headers, [body]]
|
182
182
|
end
|
@@ -276,7 +276,7 @@ module Flipper
|
|
276
276
|
|
277
277
|
# Internal: Method to call when the UI is in read only mode and you want
|
278
278
|
# to inform people of that fact.
|
279
|
-
def
|
279
|
+
def render_read_only
|
280
280
|
status 403
|
281
281
|
|
282
282
|
breadcrumb 'Home', '/'
|
@@ -286,6 +286,14 @@ module Flipper
|
|
286
286
|
halt view_response(:read_only)
|
287
287
|
end
|
288
288
|
|
289
|
+
def read_only?
|
290
|
+
Flipper::UI.configuration.read_only || flipper.read_only?
|
291
|
+
end
|
292
|
+
|
293
|
+
def write_allowed?
|
294
|
+
!read_only?
|
295
|
+
end
|
296
|
+
|
289
297
|
def bootstrap_css
|
290
298
|
SOURCES[:bootstrap_css]
|
291
299
|
end
|
@@ -14,6 +14,7 @@ module Flipper
|
|
14
14
|
@feature = Decorators::Feature.new(flipper_feature)
|
15
15
|
descriptions = Flipper::UI.configuration.descriptions_source.call([flipper_feature.key])
|
16
16
|
@feature.description = descriptions[@feature.key]
|
17
|
+
@feature.actor_names = Flipper::UI.configuration.actor_names_source.call(@feature.actors_value)
|
17
18
|
@page_title = "#{@feature.key} // Features"
|
18
19
|
@percentages = [0, 1, 5, 10, 25, 50, 100]
|
19
20
|
|
@@ -25,7 +26,7 @@ module Flipper
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def delete
|
28
|
-
|
29
|
+
render_read_only if read_only?
|
29
30
|
|
30
31
|
unless Flipper::UI.configuration.feature_removal_enabled
|
31
32
|
status 403
|
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
if Rack.release >= "2.1"
|
2
|
+
require 'rack/files'
|
3
|
+
else
|
4
|
+
require 'rack/file'
|
5
|
+
end
|
2
6
|
require 'flipper/ui/action'
|
3
7
|
|
4
8
|
module Flipper
|
@@ -8,7 +12,8 @@ module Flipper
|
|
8
12
|
route %r{(images|css|js)/.*\Z}
|
9
13
|
|
10
14
|
def get
|
11
|
-
Rack::File
|
15
|
+
klass = Rack.release >= "2.1" ? Rack::Files : Rack::File
|
16
|
+
klass.new(public_path).call(request.env)
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -10,7 +10,7 @@ module Flipper
|
|
10
10
|
route %r{\A/features/(?<feature_name>.*)/percentage_of_actors/?\Z}
|
11
11
|
|
12
12
|
def post
|
13
|
-
|
13
|
+
render_read_only if read_only?
|
14
14
|
|
15
15
|
feature = flipper[feature_name]
|
16
16
|
@feature = Decorators::Feature.new(feature)
|
@@ -10,7 +10,7 @@ module Flipper
|
|
10
10
|
route %r{\A/features/(?<feature_name>.*)/percentage_of_time/?\Z}
|
11
11
|
|
12
12
|
def post
|
13
|
-
|
13
|
+
render_read_only if read_only?
|
14
14
|
|
15
15
|
feature = flipper[feature_name]
|
16
16
|
@feature = Decorators::Feature.new(feature)
|
@@ -45,6 +45,11 @@ module Flipper
|
|
45
45
|
# page, and optionally the `features` pages. Defaults to empty block.
|
46
46
|
attr_accessor :descriptions_source
|
47
47
|
|
48
|
+
# Public: If you set this, Flipper::UI will fetch actor names
|
49
|
+
# from your external source. Descriptions for `actors` will be shown on `feature`
|
50
|
+
# page. Defaults to empty block.
|
51
|
+
attr_accessor :actor_names_source
|
52
|
+
|
48
53
|
# Public: Should feature descriptions be show on the `features` list page.
|
49
54
|
# Default false. Only works when using descriptions.
|
50
55
|
attr_accessor :show_feature_description_in_list
|
@@ -70,6 +75,7 @@ module Flipper
|
|
70
75
|
).freeze
|
71
76
|
|
72
77
|
DEFAULT_DESCRIPTIONS_SOURCE = ->(_keys) { {} }
|
78
|
+
DEFAULT_ACTOR_NAMES_SOURCE = ->(_keys) { {} }
|
73
79
|
|
74
80
|
def initialize
|
75
81
|
@delete = Option.new("Danger Zone", "Deleting a feature removes it from the list of features and disables it for everyone.")
|
@@ -81,6 +87,7 @@ module Flipper
|
|
81
87
|
@cloud_recommendation = true
|
82
88
|
@add_actor_placeholder = "a flipper id"
|
83
89
|
@descriptions_source = DEFAULT_DESCRIPTIONS_SOURCE
|
90
|
+
@actor_names_source = DEFAULT_ACTOR_NAMES_SOURCE
|
84
91
|
@show_feature_description_in_list = false
|
85
92
|
@actors_separator = ','
|
86
93
|
@confirm_fully_enable = false
|
@@ -15,6 +15,10 @@ module Flipper
|
|
15
15
|
# configured for Flipper::UI.
|
16
16
|
attr_accessor :description
|
17
17
|
|
18
|
+
# Internal: Used to preload actor names if actor_names_source is
|
19
|
+
# configured for Flipper::UI.
|
20
|
+
attr_accessor :actor_names
|
21
|
+
|
18
22
|
# Public: Returns name titleized.
|
19
23
|
def pretty_name
|
20
24
|
@pretty_name ||= Util.titleize(name)
|
@@ -40,7 +40,7 @@
|
|
40
40
|
</strong>
|
41
41
|
</h6>
|
42
42
|
</div>
|
43
|
-
<%
|
43
|
+
<% if write_allowed? %>
|
44
44
|
<div class="col col-auto toggle-block-when-off">
|
45
45
|
<button class="btn btn-outline-secondary js-toggle-trigger" data-toggle="collapse" data-target="#add-actor">Add an actor</button>
|
46
46
|
</div>
|
@@ -64,10 +64,16 @@
|
|
64
64
|
<div class="card-body bg-lightest border-bottom py-3">
|
65
65
|
<div class="row align-items-center">
|
66
66
|
<div class="col col-mr-auto pl-md-5">
|
67
|
-
<h6 class="m-0"
|
67
|
+
<h6 class="m-0">
|
68
|
+
<% if Flipper::UI::Util.present?(@feature.actor_names[item]) %>
|
69
|
+
<%= "#{@feature.actor_names[item]} (#{item})" %>
|
70
|
+
<% else %>
|
71
|
+
<%= item %>
|
72
|
+
<% end %>
|
73
|
+
</h6>
|
68
74
|
</div>
|
69
75
|
<div class="col col-auto">
|
70
|
-
<%
|
76
|
+
<% if write_allowed? %>
|
71
77
|
<form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post">
|
72
78
|
<%== csrf_input_tag %>
|
73
79
|
<input type="hidden" name="operation" value="disable">
|
@@ -96,7 +102,7 @@
|
|
96
102
|
</strong>
|
97
103
|
</h6>
|
98
104
|
</div>
|
99
|
-
<%
|
105
|
+
<% if write_allowed? %>
|
100
106
|
<div class="col col-auto toggle-block-when-off">
|
101
107
|
<button class="btn btn-outline-secondary js-toggle-trigger" data-toggle="collapse" data-target="#add-actor">Add a group</button>
|
102
108
|
</div>
|
@@ -132,7 +138,7 @@
|
|
132
138
|
<h6 class="m-0"><%= item %></h6>
|
133
139
|
</div>
|
134
140
|
<div class="col col-auto">
|
135
|
-
<%
|
141
|
+
<% if write_allowed? %>
|
136
142
|
<form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post">
|
137
143
|
<%== csrf_input_tag %>
|
138
144
|
<input type="hidden" name="operation" value="disable">
|
@@ -154,7 +160,7 @@
|
|
154
160
|
<div class="col col-mr-auto">
|
155
161
|
<h6 class="m-0"><strong class="<%= "text-muted" unless @feature.percentage_of_actors_value > 0 %>">Enabled for <%= @feature.percentage_of_actors_value %>% of actors</strong></h6>
|
156
162
|
</div>
|
157
|
-
<%
|
163
|
+
<% if write_allowed? %>
|
158
164
|
<div class="col col-auto toggle-block-when-off">
|
159
165
|
<button class="btn btn-outline-secondary js-toggle-trigger">Edit</button>
|
160
166
|
</div>
|
@@ -165,7 +171,7 @@
|
|
165
171
|
</div>
|
166
172
|
</div>
|
167
173
|
|
168
|
-
<%
|
174
|
+
<% if write_allowed? %>
|
169
175
|
<div class="card-body border-bottom toggle-block-when-on bg-lightest">
|
170
176
|
<div class="row">
|
171
177
|
<div class="col-12 col-md-6 mb-3 mb-md-0">
|
@@ -197,7 +203,7 @@
|
|
197
203
|
<div class="col col-mr-auto">
|
198
204
|
<h6 class="m-0"><strong class="<%= "text-muted" unless @feature.percentage_of_time_value > 0 %>">Enabled for <%= @feature.percentage_of_time_value %>% of time</strong></h6>
|
199
205
|
</div>
|
200
|
-
<%
|
206
|
+
<% if write_allowed? %>
|
201
207
|
<div class="col col-auto toggle-block-when-off">
|
202
208
|
<button class="btn btn-outline-secondary js-toggle-trigger">Edit</button>
|
203
209
|
</div>
|
@@ -208,7 +214,7 @@
|
|
208
214
|
</div>
|
209
215
|
</div>
|
210
216
|
|
211
|
-
<%
|
217
|
+
<% if write_allowed? %>
|
212
218
|
<div class="card-body border-bottom toggle-block-when-on bg-lightest">
|
213
219
|
<div class="row">
|
214
220
|
<div class="col-12 col-md-6 mb-3 mb-md-0">
|
@@ -234,52 +240,45 @@
|
|
234
240
|
</div>
|
235
241
|
<% end %>
|
236
242
|
|
237
|
-
|
238
|
-
<
|
239
|
-
|
243
|
+
<% if write_allowed? %>
|
244
|
+
<div class="card-body">
|
245
|
+
<form action="<%= script_name %>/features/<%= @feature.key %>/boolean" method="post">
|
246
|
+
<%== csrf_input_tag %>
|
240
247
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
</span>
|
257
|
-
</button>
|
258
|
-
</div>
|
259
|
-
<% end %>
|
248
|
+
<div class="row">
|
249
|
+
<% unless @feature.boolean_value %>
|
250
|
+
<div class="col">
|
251
|
+
<button type="submit" name="action" value="Enable" <% if Flipper::UI.configuration.confirm_fully_enable %>id="enable_feature__button"<% end %> class="btn btn-outline-success btn-block">
|
252
|
+
<span class="d-block" data-toggle="tooltip"
|
253
|
+
<% if Flipper::UI.configuration.confirm_fully_enable %>
|
254
|
+
data-confirmation-text="<%= feature_name %>"
|
255
|
+
<% end %>
|
256
|
+
title="Enable for everyone"
|
257
|
+
>
|
258
|
+
Fully Enable
|
259
|
+
</span>
|
260
|
+
</button>
|
261
|
+
</div>
|
262
|
+
<% end %>
|
260
263
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
<% end %>
|
275
|
-
</div>
|
276
|
-
</form>
|
277
|
-
</div>
|
264
|
+
<% unless @feature.off? %>
|
265
|
+
<div class="col">
|
266
|
+
<button type="submit" name="action" value="Disable" class="btn btn-outline-danger btn-block">
|
267
|
+
<span class="d-block" data-toggle="tooltip" title="Disable for everyone by clearing all percentages, groups and actors.">
|
268
|
+
Disable
|
269
|
+
</span>
|
270
|
+
</button>
|
271
|
+
</div>
|
272
|
+
<% end %>
|
273
|
+
</div>
|
274
|
+
</form>
|
275
|
+
</div>
|
276
|
+
<% end %>
|
278
277
|
</div>
|
279
278
|
</div>
|
280
279
|
</div>
|
281
280
|
|
282
|
-
<% if
|
281
|
+
<% if write_allowed? && Flipper::UI.configuration.feature_removal_enabled %>
|
283
282
|
<div class="row">
|
284
283
|
<div class="col">
|
285
284
|
<div class="card border">
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<% if Flipper::UI.configuration.fun %>
|
4
4
|
<h4>But I've got a blank space baby...</h4>
|
5
5
|
<p>And I'll flip your features.</p>
|
6
|
-
<%- if
|
6
|
+
<%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
|
7
7
|
<p>
|
8
8
|
<a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
|
9
9
|
</p>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<% else %>
|
12
12
|
<h4>Getting Started</h4>
|
13
13
|
<p class="mb-1">You have not added any features to configure yet.</p>
|
14
|
-
<%- if
|
14
|
+
<%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
|
15
15
|
<p class="mt-2">
|
16
16
|
<a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
|
17
17
|
</p>
|
@@ -26,7 +26,7 @@
|
|
26
26
|
<% else %>
|
27
27
|
<div class="card">
|
28
28
|
<div class="card-header">
|
29
|
-
<%- if
|
29
|
+
<%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
|
30
30
|
<div class="float-right">
|
31
31
|
<a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
|
32
32
|
</div>
|
@@ -50,7 +50,7 @@
|
|
50
50
|
</a>
|
51
51
|
</div>
|
52
52
|
<div class="col text-muted p-0">
|
53
|
-
<small>For
|
53
|
+
<small>For audit history, rollback, finer-grained permissions, and multi-environment sync check out <a href="https://www.flippercloud.io/?utm_source=oss&utm_medium=ui&utm_campaign=spread_the_love">Flipper Cloud</a>. We even have a free tier!</small>
|
54
54
|
</div>
|
55
55
|
</div>
|
56
56
|
</div>
|
@@ -1,3 +1,11 @@
|
|
1
1
|
<div class="alert alert-danger">
|
2
|
-
The UI is currently in read only mode
|
2
|
+
<p>The UI is currently in read only mode.</p>
|
3
|
+
|
4
|
+
<p>
|
5
|
+
To change this, you'll need to set <code>Flipper::UI.configuration.read_only = false</code> wherever flipper is running from.
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
Alternatively, you may be using the <code>Flipper::Adapters::ReadOnly</code> adapter. This will also set Flipper into read only mode.
|
10
|
+
</p>
|
3
11
|
</div>
|
data/lib/flipper/version.rb
CHANGED
@@ -129,6 +129,23 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
context "when a readonly adapter is configured" do
|
133
|
+
let(:value) { 'User;6' }
|
134
|
+
|
135
|
+
before do
|
136
|
+
allow(flipper).to receive(:read_only?) { true }
|
137
|
+
end
|
138
|
+
|
139
|
+
it "does not allow an actor to be added" do
|
140
|
+
post 'features/search/actors',
|
141
|
+
{ 'value' => value, 'operation' => 'enable', 'authenticity_token' => token },
|
142
|
+
'rack.session' => session
|
143
|
+
|
144
|
+
expect(flipper[:search].actors_value).not_to include('User;6')
|
145
|
+
expect(last_response.body).to include("The UI is currently in read only mode.")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
132
149
|
context 'disabling an actor' do
|
133
150
|
let(:value) { 'User;6' }
|
134
151
|
let(:multi_value) { 'User;5, User;7, User;9, User;12' }
|
@@ -39,4 +39,19 @@ RSpec.describe Flipper::UI::Actions::AddFeature do
|
|
39
39
|
expect(last_response.body).to include('Feature creation is disabled.')
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
describe 'GET /features/new when an adpter is set to readonly' do
|
44
|
+
before do
|
45
|
+
allow(flipper).to receive(:read_only?) { true }
|
46
|
+
get '/features/new'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns 403' do
|
50
|
+
expect(last_response.status).to be(403)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'shows that the UI is currently read-only mode' do
|
54
|
+
expect(last_response.body).to include('The UI is currently in read only mode.')
|
55
|
+
end
|
56
|
+
end
|
42
57
|
end
|
@@ -21,6 +21,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
21
21
|
flipper.enable_group :search, :employees
|
22
22
|
flipper.enable :plausible
|
23
23
|
flipper.disable :google_analytics
|
24
|
+
flipper.enable :analytics, Flipper.property(:plan).eq("basic")
|
24
25
|
|
25
26
|
post '/settings/export',
|
26
27
|
{'authenticity_token' => token},
|
@@ -40,9 +41,10 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
40
41
|
expect(last_response.headers['Content-Type']).to eq('application/json')
|
41
42
|
expect(data['version']).to eq(1)
|
42
43
|
expect(data['features']).to eq({
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
44
|
+
"analytics" => {"boolean"=>nil, "expression"=>{"Equal"=>[{"Property"=>["plan"]}, "basic"]}, "groups"=>[], "actors"=>[], "percentage_of_actors"=>nil, "percentage_of_time"=>nil},
|
45
|
+
"search"=> {"boolean"=>nil, "expression"=>nil, "groups"=>["admins", "employees"], "actors"=>["User;1", "User;100"], "percentage_of_actors"=>"10", "percentage_of_time"=>"15"},
|
46
|
+
"plausible"=> {"boolean"=>"true", "expression"=>nil, "groups"=>[], "actors"=>[], "percentage_of_actors"=>nil, "percentage_of_time"=>nil},
|
47
|
+
"google_analytics"=> {"boolean"=>nil, "expression"=>nil, "groups"=>[], "actors"=>[], "percentage_of_actors"=>nil, "percentage_of_time"=>nil},
|
46
48
|
})
|
47
49
|
end
|
48
50
|
end
|
@@ -112,6 +112,41 @@ RSpec.describe Flipper::UI::Actions::Feature do
|
|
112
112
|
expect(last_response.body).to include('Enabled for 0% of actors')
|
113
113
|
expect(last_response.body).to include('Most in-depth search')
|
114
114
|
end
|
115
|
+
|
116
|
+
context "when in read-only mode" do
|
117
|
+
before do
|
118
|
+
allow(flipper).to receive(:read_only?) { true }
|
119
|
+
end
|
120
|
+
|
121
|
+
before { get '/features' }
|
122
|
+
|
123
|
+
it 'renders template with no buttons or ways to modify a feature' do
|
124
|
+
expect(last_response.body).not_to include("Fully Enable")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'custom actor names' do
|
129
|
+
before do
|
130
|
+
actor = Flipper::Actor.new('some_actor_name')
|
131
|
+
flipper['search'].enable_actor(actor)
|
132
|
+
|
133
|
+
Flipper::UI.configure do |config|
|
134
|
+
config.actor_names_source = lambda { |_keys|
|
135
|
+
{
|
136
|
+
"some_actor_name" => "Some Actor Name",
|
137
|
+
"some_other_actor_name" => "Some Other Actor Name",
|
138
|
+
}
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
get '/features/search'
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'renders template with custom actor names' do
|
146
|
+
expect(last_response.body).to include('Some Actor Name (some_actor_name)')
|
147
|
+
expect(last_response.body).not_to include('Some Other Actor Name')
|
148
|
+
end
|
149
|
+
end
|
115
150
|
end
|
116
151
|
|
117
152
|
describe 'GET /features/:feature with _features in feature name' do
|
@@ -64,6 +64,20 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
68
|
+
context "when in read-only mode" do
|
69
|
+
before { allow(flipper).to receive(:read_only?) { true } }
|
70
|
+
|
71
|
+
before { get '/features' }
|
72
|
+
|
73
|
+
it 'responds with success' do
|
74
|
+
expect(last_response.status).to be(200)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'renders template with no button' do
|
78
|
+
expect(last_response.body).not_to include('<a class="btn btn-primary btn-sm" href="/features/new">Add Feature</a>')
|
79
|
+
end
|
80
|
+
end
|
67
81
|
end
|
68
82
|
|
69
83
|
describe 'POST /features' do
|
@@ -110,6 +110,26 @@ RSpec.describe Flipper::UI::Configuration do
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
+
describe "#actor_names_source" do
|
114
|
+
it "has default value" do
|
115
|
+
expect(configuration.actor_names_source.call(%w[foo bar])).to eq({})
|
116
|
+
end
|
117
|
+
|
118
|
+
context "actor names source is provided" do
|
119
|
+
it "can be updated" do
|
120
|
+
configuration.actor_names_source = lambda do |_keys|
|
121
|
+
YAML.load_file(FlipperRoot.join('spec/support/actor_names.yml'))
|
122
|
+
end
|
123
|
+
keys = %w[actor_1 foo]
|
124
|
+
result = configuration.actor_names_source.call(keys)
|
125
|
+
expected = {
|
126
|
+
"actor_name_1" => "Actor #1",
|
127
|
+
}
|
128
|
+
expect(result).to eq(expected)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
113
133
|
describe "#confirm_fully_enable" do
|
114
134
|
it "has default value" do
|
115
135
|
expect(configuration.confirm_fully_enable).to eq(false)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper-ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08
|
11
|
+
date: 2023-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 1.
|
59
|
+
version: 1.1.0
|
60
60
|
type: :runtime
|
61
61
|
prerelease: false
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 1.
|
66
|
+
version: 1.1.0
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: erubi
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|