flipper-ui 0.20.3 → 0.22.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/docs/ui/README.md +1 -1
- data/examples/ui/authorization.ru +46 -0
- data/examples/ui/basic.ru +21 -34
- data/lib/flipper/ui.rb +2 -2
- data/lib/flipper/ui/action.rb +1 -1
- data/lib/flipper/ui/actions/actors_gate.rb +1 -1
- data/lib/flipper/ui/actions/features.rb +2 -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 +5 -0
- data/lib/flipper/ui/middleware.rb +2 -1
- data/lib/flipper/ui/public/images/logo.png +0 -0
- data/lib/flipper/ui/views/features.erb +1 -1
- data/lib/flipper/ui/views/layout.erb +15 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/actors_gate_spec.rb +19 -2
- data/spec/flipper/ui/actions/boolean_gate_spec.rb +18 -0
- data/spec/flipper/ui/actions/feature_spec.rb +18 -0
- data/spec/flipper/ui/actions/features_spec.rb +16 -3
- data/spec/flipper/ui/actions/groups_gate_spec.rb +20 -3
- data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +18 -1
- data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +18 -1
- data/spec/flipper/ui/configuration_spec.rb +11 -0
- data/spec/flipper/ui_spec.rb +0 -13
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3be7bbda3ec1a09b1cc1c421766ea520a9a933eeb1628ea27281c35a174725a2
|
4
|
+
data.tar.gz: ca0375faf5a0cfd4f2b6c04435c803e9414c2e41337918ad9f8bb9f95f579e46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: effdbdd4ab7c0646dcd1a50ebd0d96fd78dc2dabb2a87ae1e524a2a4b32ce43c1b01e6e96046d1e9bc4fe298a805a2c31c713b83ad1dbfb26c31dfb70f714e90
|
7
|
+
data.tar.gz: 8d1f5b3f946a0b5d9bb42db7a497484c490cc2a2e9f15562e046ddeb41be0dba1cdda19ef1016bb5f7966ab35c600b8b57418533797b3000cec927671079ff1a
|
data/docs/ui/README.md
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Usage:
|
3
|
+
# bundle exec rackup examples/ui/authorization.ru -p 9999
|
4
|
+
# bundle exec shotgun examples/ui/authorization.ru -p 9999
|
5
|
+
# http://localhost:9999/
|
6
|
+
#
|
7
|
+
require 'bundler/setup'
|
8
|
+
require "flipper/ui"
|
9
|
+
require "flipper/adapters/pstore"
|
10
|
+
|
11
|
+
Flipper.register(:admins) { |actor|
|
12
|
+
actor.respond_to?(:admin?) && actor.admin?
|
13
|
+
}
|
14
|
+
|
15
|
+
# Example middleware to allow reading the Flipper UI but nothing else.
|
16
|
+
class FlipperReadOnlyMiddleware
|
17
|
+
def initialize(app)
|
18
|
+
@app = app
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
request = Rack::Request.new(env)
|
23
|
+
|
24
|
+
if request.get?
|
25
|
+
@app.call(env)
|
26
|
+
else
|
27
|
+
[401, {}, ["You can only look"]]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# You can uncomment these to get some default data:
|
33
|
+
# Flipper.enable(:search_performance_another_long_thing)
|
34
|
+
# Flipper.disable(:gauges_tracking)
|
35
|
+
# Flipper.disable(:unused)
|
36
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('1'))
|
37
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('6'))
|
38
|
+
# Flipper.enable_group(:secrets, :admins)
|
39
|
+
# Flipper.enable_percentage_of_time(:logging, 5)
|
40
|
+
# Flipper.enable_percentage_of_actors(:new_cache, 15)
|
41
|
+
# Flipper.add("a/b")
|
42
|
+
|
43
|
+
run Flipper::UI.app { |builder|
|
44
|
+
builder.use Rack::Session::Cookie, secret: "_super_secret"
|
45
|
+
builder.use FlipperReadOnlyMiddleware
|
46
|
+
}
|
data/examples/ui/basic.ru
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
#
|
2
2
|
# Usage:
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# # if you want it to not reload and be really fast
|
4
|
+
# bin/rackup examples/ui/basic.ru -p 9999
|
5
|
+
#
|
6
|
+
# # if you want reloading
|
7
|
+
# bin/shotgun examples/ui/basic.ru -p 9999
|
8
|
+
#
|
5
9
|
# http://localhost:9999/
|
6
10
|
#
|
7
|
-
require
|
8
|
-
require "
|
9
|
-
require "pathname"
|
10
|
-
|
11
|
-
root_path = Pathname(__FILE__).dirname.join("..").expand_path
|
12
|
-
lib_path = root_path.join("lib")
|
13
|
-
$:.unshift(lib_path)
|
14
|
-
|
15
|
-
require "flipper-ui"
|
11
|
+
require 'bundler/setup'
|
12
|
+
require "flipper/ui"
|
16
13
|
require "flipper/adapters/pstore"
|
17
|
-
require "active_support/notifications"
|
18
14
|
|
19
15
|
Flipper.register(:admins) { |actor|
|
20
16
|
actor.respond_to?(:admin?) && actor.admin?
|
@@ -24,21 +20,12 @@ Flipper.register(:early_access) { |actor|
|
|
24
20
|
actor.respond_to?(:early?) && actor.early?
|
25
21
|
}
|
26
22
|
|
27
|
-
# Setup logging of flipper calls.
|
28
|
-
if ENV["LOG"] == "1"
|
29
|
-
$logger = Logger.new(STDOUT)
|
30
|
-
require "flipper/instrumentation/log_subscriber"
|
31
|
-
Flipper::Instrumentation::LogSubscriber.logger = $logger
|
32
|
-
end
|
33
|
-
|
34
|
-
adapter = Flipper::Adapters::PStore.new
|
35
|
-
flipper = Flipper.new(adapter, instrumenter: ActiveSupport::Notifications)
|
36
|
-
|
37
23
|
Flipper::UI.configure do |config|
|
38
24
|
# config.banner_text = 'Production Environment'
|
39
25
|
# config.banner_class = 'danger'
|
40
26
|
config.feature_creation_enabled = true
|
41
27
|
config.feature_removal_enabled = true
|
28
|
+
config.cloud_recommendation = true
|
42
29
|
# config.show_feature_description_in_list = true
|
43
30
|
config.descriptions_source = lambda do |_keys|
|
44
31
|
{
|
@@ -55,17 +42,17 @@ Flipper::UI.configure do |config|
|
|
55
42
|
end
|
56
43
|
|
57
44
|
# You can uncomment these to get some default data:
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
run Flipper::UI.app
|
45
|
+
# Flipper.enable(:search_performance_another_long_thing)
|
46
|
+
# Flipper.disable(:gauges_tracking)
|
47
|
+
# Flipper.disable(:unused)
|
48
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('1'))
|
49
|
+
# Flipper.enable_actor(:suits, Flipper::Actor.new('6'))
|
50
|
+
# Flipper.enable_group(:secrets, :admins)
|
51
|
+
# Flipper.enable_group(:secrets, :early_access)
|
52
|
+
# Flipper.enable_percentage_of_time(:logging, 5)
|
53
|
+
# Flipper.enable_percentage_of_actors(:new_cache, 15)
|
54
|
+
# Flipper.add("a/b")
|
55
|
+
|
56
|
+
run Flipper::UI.app { |builder|
|
70
57
|
builder.use Rack::Session::Cookie, secret: "_super_secret"
|
71
58
|
}
|
data/lib/flipper/ui.rb
CHANGED
@@ -39,14 +39,14 @@ module Flipper
|
|
39
39
|
def self.app(flipper = nil, options = {})
|
40
40
|
env_key = options.fetch(:env_key, 'flipper')
|
41
41
|
rack_protection_options = options.fetch(:rack_protection, use: :authenticity_token)
|
42
|
+
|
42
43
|
app = ->(_) { [200, { 'Content-Type' => 'text/html' }, ['']] }
|
43
44
|
builder = Rack::Builder.new
|
44
45
|
yield builder if block_given?
|
45
46
|
builder.use Rack::Protection, rack_protection_options
|
46
47
|
builder.use Rack::MethodOverride
|
47
48
|
builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key
|
48
|
-
builder.use Flipper::Middleware
|
49
|
-
builder.use Flipper::UI::Middleware, env_key: env_key
|
49
|
+
builder.use Flipper::UI::Middleware, flipper: flipper, env_key: env_key
|
50
50
|
builder.run app
|
51
51
|
klass = self
|
52
52
|
builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
|
data/lib/flipper/ui/action.rb
CHANGED
@@ -151,7 +151,7 @@ module Flipper
|
|
151
151
|
# location - The String location to set the Location header to.
|
152
152
|
def redirect_to(location)
|
153
153
|
status 302
|
154
|
-
header 'Location', "#{script_name}#{location}"
|
154
|
+
header 'Location', "#{script_name}#{Rack::Utils.escape_path(location)}"
|
155
155
|
halt [@code, @headers, ['']]
|
156
156
|
end
|
157
157
|
|
@@ -27,7 +27,7 @@ module Flipper
|
|
27
27
|
value = params['value'].to_s.strip
|
28
28
|
|
29
29
|
if Util.blank?(value)
|
30
|
-
error =
|
30
|
+
error = "#{value.inspect} is not a valid actor value."
|
31
31
|
redirect_to("/features/#{feature.key}/actors?error=#{error}")
|
32
32
|
end
|
33
33
|
|
@@ -49,14 +49,14 @@ module Flipper
|
|
49
49
|
value = params['value'].to_s.strip
|
50
50
|
|
51
51
|
if Util.blank?(value)
|
52
|
-
error =
|
52
|
+
error = "#{value.inspect} is not a valid feature name."
|
53
53
|
redirect_to("/features/new?error=#{error}")
|
54
54
|
end
|
55
55
|
|
56
56
|
feature = flipper[value]
|
57
57
|
feature.add
|
58
58
|
|
59
|
-
redirect_to "/features/#{
|
59
|
+
redirect_to "/features/#{value}"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -35,7 +35,7 @@ module Flipper
|
|
35
35
|
|
36
36
|
redirect_to("/features/#{feature.key}")
|
37
37
|
else
|
38
|
-
error =
|
38
|
+
error = "The group named #{value.inspect} has not been registered."
|
39
39
|
redirect_to("/features/#{feature.key}/groups?error=#{error}")
|
40
40
|
end
|
41
41
|
end
|
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
begin
|
17
17
|
feature.enable_percentage_of_actors params['value']
|
18
18
|
rescue ArgumentError => exception
|
19
|
-
error =
|
19
|
+
error = "Invalid percentage of actors value: #{exception.message}"
|
20
20
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
21
21
|
end
|
22
22
|
|
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
begin
|
17
17
|
feature.enable_percentage_of_time params['value']
|
18
18
|
rescue ArgumentError => exception
|
19
|
-
error =
|
19
|
+
error = "Invalid percentage of time value: #{exception.message}"
|
20
20
|
redirect_to("/features/#{@feature.key}?error=#{error}")
|
21
21
|
end
|
22
22
|
|
@@ -26,6 +26,10 @@ module Flipper
|
|
26
26
|
# won't see a videoclip of Taylor Swift when there aren't any features
|
27
27
|
attr_accessor :fun
|
28
28
|
|
29
|
+
# Public: Tired of seeing the awesome message about Cloud? Set this to
|
30
|
+
# false and it will go away. Defaults to true.
|
31
|
+
attr_accessor :cloud_recommendation
|
32
|
+
|
29
33
|
# Public: What should show up in the form to add actors. This can be
|
30
34
|
# different per application since flipper_id's can be whatever an
|
31
35
|
# application needs. Defaults to "a flipper id".
|
@@ -60,6 +64,7 @@ module Flipper
|
|
60
64
|
@feature_creation_enabled = true
|
61
65
|
@feature_removal_enabled = true
|
62
66
|
@fun = true
|
67
|
+
@cloud_recommendation = true
|
63
68
|
@add_actor_placeholder = "a flipper id"
|
64
69
|
@descriptions_source = DEFAULT_DESCRIPTIONS_SOURCE
|
65
70
|
@show_feature_description_in_list = false
|
@@ -12,6 +12,7 @@ module Flipper
|
|
12
12
|
def initialize(app, options = {})
|
13
13
|
@app = app
|
14
14
|
@env_key = options.fetch(:env_key, 'flipper')
|
15
|
+
@flipper = options.fetch(:flipper) { Flipper }
|
15
16
|
|
16
17
|
@action_collection = ActionCollection.new
|
17
18
|
|
@@ -43,7 +44,7 @@ module Flipper
|
|
43
44
|
if action_class.nil?
|
44
45
|
@app.call(env)
|
45
46
|
else
|
46
|
-
flipper = env.fetch(@env_key)
|
47
|
+
flipper = env.fetch(@env_key) { Flipper }
|
47
48
|
action_class.run(flipper, request)
|
48
49
|
end
|
49
50
|
end
|
Binary file
|
@@ -35,6 +35,21 @@
|
|
35
35
|
<div>
|
36
36
|
<%== yield %>
|
37
37
|
</div>
|
38
|
+
|
39
|
+
<% if Flipper::UI.configuration.cloud_recommendation %>
|
40
|
+
<div class="d-flex justify-content-center mt-5">
|
41
|
+
<div class="row" style="max-width: 350px;">
|
42
|
+
<div class="col-auto d-flex align-items-center">
|
43
|
+
<a href="https://www.flippercloud.io/?utm_source=oss&utm_medium=ui&utm_campaign=spread_the_love">
|
44
|
+
<img src="<%= script_name %>/images/logo.png" width="50" />
|
45
|
+
</a>
|
46
|
+
</div>
|
47
|
+
<div class="col text-muted p-0">
|
48
|
+
<small>For support, audit history, finer-grained permissions, multi-environment sync, and all your projects in one place check out <a href="https://www.flippercloud.io/?utm_source=oss&utm_medium=ui&utm_campaign=spread_the_love">Flipper Cloud</a>.</small>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
<% end %>
|
38
53
|
</div>
|
39
54
|
|
40
55
|
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
data/lib/flipper/version.rb
CHANGED
@@ -61,6 +61,23 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
61
61
|
expect(last_response.headers['Location']).to eq('/features/search')
|
62
62
|
end
|
63
63
|
|
64
|
+
context "when feature name contains space" do
|
65
|
+
before do
|
66
|
+
post 'features/sp%20ace/actors',
|
67
|
+
{ 'value' => value, 'operation' => 'enable', 'authenticity_token' => token },
|
68
|
+
'rack.session' => session
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'adds item to members' do
|
72
|
+
expect(flipper["sp ace"].actors_value).to include('User;6')
|
73
|
+
end
|
74
|
+
|
75
|
+
it "redirects back to feature" do
|
76
|
+
expect(last_response.status).to be(302)
|
77
|
+
expect(last_response.headers['Location']).to eq('/features/sp%20ace')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
64
81
|
context 'value contains whitespace' do
|
65
82
|
let(:value) { ' User;6 ' }
|
66
83
|
|
@@ -75,7 +92,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
75
92
|
|
76
93
|
it 'redirects back to feature' do
|
77
94
|
expect(last_response.status).to be(302)
|
78
|
-
expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22
|
95
|
+
expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22%20is%20not%20a%20valid%20actor%20value.')
|
79
96
|
end
|
80
97
|
end
|
81
98
|
|
@@ -84,7 +101,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
84
101
|
|
85
102
|
it 'redirects back to feature' do
|
86
103
|
expect(last_response.status).to be(302)
|
87
|
-
expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22
|
104
|
+
expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22%20is%20not%20a%20valid%20actor%20value.')
|
88
105
|
end
|
89
106
|
end
|
90
107
|
end
|
@@ -31,6 +31,24 @@ RSpec.describe Flipper::UI::Actions::BooleanGate do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
context "with space in feature name" do
|
35
|
+
before do
|
36
|
+
flipper.disable :search
|
37
|
+
post 'features/sp%20ace/boolean',
|
38
|
+
{ 'action' => 'Enable', 'authenticity_token' => token },
|
39
|
+
'rack.session' => session
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'updates feature' do
|
43
|
+
expect(flipper.enabled?("sp ace")).to be(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'redirects back to feature' do
|
47
|
+
expect(last_response.status).to be(302)
|
48
|
+
expect(last_response.headers['Location']).to eq('/features/sp%20ace')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
34
52
|
context 'with disable' do
|
35
53
|
before do
|
36
54
|
flipper.enable :search
|
@@ -29,6 +29,24 @@ RSpec.describe Flipper::UI::Actions::Feature do
|
|
29
29
|
expect(last_response.headers['Location']).to eq('/features')
|
30
30
|
end
|
31
31
|
|
32
|
+
context "with space in feature name" do
|
33
|
+
before do
|
34
|
+
flipper.enable "sp ace"
|
35
|
+
delete '/features/sp%20ace',
|
36
|
+
{ 'authenticity_token' => token },
|
37
|
+
'rack.session' => session
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'removes feature' do
|
41
|
+
expect(flipper.features.map(&:key)).not_to include('sp ace')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'redirects to features' do
|
45
|
+
expect(last_response.status).to be(302)
|
46
|
+
expect(last_response.headers['Location']).to eq('/features')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
32
50
|
context 'when feature_removal_enabled is set to false' do
|
33
51
|
around do |example|
|
34
52
|
begin
|
@@ -95,7 +95,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
95
95
|
expect(last_response.headers['Location']).to eq('/features/notifications_next')
|
96
96
|
end
|
97
97
|
|
98
|
-
context 'feature name
|
98
|
+
context 'feature name has whitespace at beginning and end' do
|
99
99
|
let(:feature_name) { ' notifications_next ' }
|
100
100
|
|
101
101
|
it 'adds feature without whitespace' do
|
@@ -103,6 +103,19 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
+
context 'feature name contains space' do
|
107
|
+
let(:feature_name) { 'notifications next' }
|
108
|
+
|
109
|
+
it 'adds feature with space' do
|
110
|
+
expect(flipper.features.map(&:key)).to include('notifications next')
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'redirects to feature' do
|
114
|
+
expect(last_response.status).to be(302)
|
115
|
+
expect(last_response.headers['Location']).to eq('/features/notifications%20next')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
106
119
|
context 'for an invalid feature name' do
|
107
120
|
context 'empty feature name' do
|
108
121
|
let(:feature_name) { '' }
|
@@ -113,7 +126,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
113
126
|
|
114
127
|
it 'redirects back to feature' do
|
115
128
|
expect(last_response.status).to be(302)
|
116
|
-
expect(last_response.headers['Location']).to eq('/features/new?error=%22%22
|
129
|
+
expect(last_response.headers['Location']).to eq('/features/new?error=%22%22%20is%20not%20a%20valid%20feature%20name.')
|
117
130
|
end
|
118
131
|
end
|
119
132
|
|
@@ -126,7 +139,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
126
139
|
|
127
140
|
it 'redirects back to feature' do
|
128
141
|
expect(last_response.status).to be(302)
|
129
|
-
expect(last_response.headers['Location']).to eq('/features/new?error=%22%22
|
142
|
+
expect(last_response.headers['Location']).to eq('/features/new?error=%22%22%20is%20not%20a%20valid%20feature%20name.')
|
130
143
|
end
|
131
144
|
end
|
132
145
|
end
|
@@ -60,6 +60,23 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
60
60
|
expect(last_response.headers['Location']).to eq('/features/search')
|
61
61
|
end
|
62
62
|
|
63
|
+
context 'feature name contains space' do
|
64
|
+
before do
|
65
|
+
post 'features/sp%20ace/groups',
|
66
|
+
{ 'value' => group_name, 'operation' => 'enable', 'authenticity_token' => token },
|
67
|
+
'rack.session' => session
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'adds item to members' do
|
71
|
+
expect(flipper["sp ace"].groups_value).to include('admins')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'redirects back to feature' do
|
75
|
+
expect(last_response.status).to be(302)
|
76
|
+
expect(last_response.headers['Location']).to eq('/features/sp%20ace')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
63
80
|
context 'group name contains whitespace' do
|
64
81
|
let(:group_name) { ' admins ' }
|
65
82
|
|
@@ -74,7 +91,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
74
91
|
|
75
92
|
it 'redirects back to feature' do
|
76
93
|
expect(last_response.status).to be(302)
|
77
|
-
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The
|
94
|
+
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22not_here%22%20has%20not%20been%20registered.')
|
78
95
|
end
|
79
96
|
end
|
80
97
|
|
@@ -83,7 +100,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
83
100
|
|
84
101
|
it 'redirects back to feature' do
|
85
102
|
expect(last_response.status).to be(302)
|
86
|
-
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The
|
103
|
+
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22%22%20has%20not%20been%20registered.')
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
@@ -92,7 +109,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
92
109
|
|
93
110
|
it 'redirects back to feature' do
|
94
111
|
expect(last_response.status).to be(302)
|
95
|
-
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The
|
112
|
+
expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22%22%20has%20not%20been%20registered.')
|
96
113
|
end
|
97
114
|
end
|
98
115
|
end
|
@@ -30,6 +30,23 @@ RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
context 'with space in feature name' do
|
34
|
+
before do
|
35
|
+
post 'features/sp%20ace/percentage_of_actors',
|
36
|
+
{ 'value' => '24', 'authenticity_token' => token },
|
37
|
+
'rack.session' => session
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'enables the feature' do
|
41
|
+
expect(flipper["sp ace"].percentage_of_actors_value).to be(24)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'redirects back to feature' do
|
45
|
+
expect(last_response.status).to be(302)
|
46
|
+
expect(last_response.headers['Location']).to eq('/features/sp%20ace')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
33
50
|
context 'with invalid value' do
|
34
51
|
before do
|
35
52
|
post 'features/search/percentage_of_actors',
|
@@ -43,7 +60,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do
|
|
43
60
|
|
44
61
|
it 'redirects back to feature' do
|
45
62
|
expect(last_response.status).to be(302)
|
46
|
-
expect(last_response.headers['Location']).to eq('/features/search?error=Invalid
|
63
|
+
expect(last_response.headers['Location']).to eq('/features/search?error=Invalid%20percentage%20of%20actors%20value:%20value%20must%20be%20a%20positive%20number%20less%20than%20or%20equal%20to%20100,%20but%20was%20555')
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
@@ -30,6 +30,23 @@ RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
context 'with space in feature name' do
|
34
|
+
before do
|
35
|
+
post 'features/sp%20ace/percentage_of_time',
|
36
|
+
{ 'value' => '24', 'authenticity_token' => token },
|
37
|
+
'rack.session' => session
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'enables the feature' do
|
41
|
+
expect(flipper["sp ace"].percentage_of_time_value).to be(24)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'redirects back to feature' do
|
45
|
+
expect(last_response.status).to be(302)
|
46
|
+
expect(last_response.headers['Location']).to eq('/features/sp%20ace')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
33
50
|
context 'with invalid value' do
|
34
51
|
before do
|
35
52
|
post 'features/search/percentage_of_time',
|
@@ -43,7 +60,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do
|
|
43
60
|
|
44
61
|
it 'redirects back to feature' do
|
45
62
|
expect(last_response.status).to be(302)
|
46
|
-
expect(last_response.headers['Location']).to eq('/features/search?error=Invalid
|
63
|
+
expect(last_response.headers['Location']).to eq('/features/search?error=Invalid%20percentage%20of%20time%20value:%20value%20must%20be%20a%20positive%20number%20less%20than%20or%20equal%20to%20100,%20but%20was%20555')
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
@@ -59,6 +59,17 @@ RSpec.describe Flipper::UI::Configuration do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
describe "#cloud_recommendation" do
|
63
|
+
it "has default value" do
|
64
|
+
expect(configuration.cloud_recommendation).to eq(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can be updated" do
|
68
|
+
configuration.cloud_recommendation = false
|
69
|
+
expect(configuration.cloud_recommendation).to eq(false)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
62
73
|
describe "#feature_removal_enabled" do
|
63
74
|
it "has default value" do
|
64
75
|
expect(configuration.feature_removal_enabled).to eq(true)
|
data/spec/flipper/ui_spec.rb
CHANGED
@@ -24,19 +24,6 @@ RSpec.describe Flipper::UI do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
describe 'Initializing middleware lazily with a block' do
|
28
|
-
let(:app) do
|
29
|
-
build_app(-> { flipper })
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'works' do
|
33
|
-
flipper.enable :some_great_feature
|
34
|
-
get '/features'
|
35
|
-
expect(last_response.status).to be(200)
|
36
|
-
expect(last_response.body).to include('some_great_feature')
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
27
|
describe 'Request method unsupported by action' do
|
41
28
|
it 'raises error' do
|
42
29
|
expect do
|
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: 0.
|
4
|
+
version: 0.22.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: 2021-
|
11
|
+
date: 2021-07-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: 0.
|
59
|
+
version: 0.22.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: 0.
|
66
|
+
version: 0.22.0
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: erubi
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- docs/ui/images/description.png
|
97
97
|
- docs/ui/images/feature.png
|
98
98
|
- docs/ui/images/features.png
|
99
|
+
- examples/ui/authorization.ru
|
99
100
|
- examples/ui/basic.ru
|
100
101
|
- flipper-ui.gemspec
|
101
102
|
- lib/flipper-ui.rb
|