flipper-ui 1.3.6 → 1.3.7.test
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/examples/ui/basic.ru +1 -0
- data/lib/flipper/ui/action.rb +8 -0
- data/lib/flipper/ui/actions/cloud_migrate.rb +26 -0
- data/lib/flipper/ui/middleware.rb +1 -0
- data/lib/flipper/ui/public/js/application.js +2 -2
- data/lib/flipper/ui/views/feature.erb +14 -16
- data/lib/flipper/ui/views/features.erb +1 -1
- data/lib/flipper/ui/views/settings.erb +27 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/cloud_migrate_spec.rb +54 -0
- data/spec/flipper/ui/actions/feature_spec.rb +14 -0
- data/spec/flipper/ui/actions/features_spec.rb +34 -0
- data/spec/flipper/ui/actions/settings_spec.rb +8 -0
- metadata +13 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e92c4665d60234e6b2942d61dbe7a860e747b573e91a4f4f107975e5c98f27ff
|
|
4
|
+
data.tar.gz: 0d289545c59f59c68b893b090e4192e993b0f1c469d850d3195deaa70cce4864
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6dec25435c957b97c45075942889c435ee97240cf1fac3b6d157674c8a32e3cccf5b4cb620d3b49e6f520e57591c0f30d4efbbaf2b89a4613d8f2ac828289c89
|
|
7
|
+
data.tar.gz: 72c321f75fef4300ea37bc6cd1946f8fd20d597b5f0b24a514dd308c92416629e94714cf5b6f23fdc51e362c3e4515cc87f83d84f6e9991dab91b458720ce628
|
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.confirm_disable = false
|
|
28
29
|
config.read_only = false
|
|
29
30
|
# config.show_feature_description_in_list = true
|
|
30
31
|
config.descriptions_source = lambda do |_keys|
|
data/lib/flipper/ui/action.rb
CHANGED
|
@@ -7,6 +7,14 @@ require 'sanitize'
|
|
|
7
7
|
|
|
8
8
|
module Flipper
|
|
9
9
|
module UI
|
|
10
|
+
# Sanitize config for descriptions in list view. Removes anchor tags to
|
|
11
|
+
# avoid nested links (the feature row is wrapped in an <a> tag).
|
|
12
|
+
# See: https://github.com/flippercloud/flipper/issues/939
|
|
13
|
+
SANITIZE_LIST = Sanitize::Config.merge(
|
|
14
|
+
Sanitize::Config::BASIC,
|
|
15
|
+
elements: Sanitize::Config::BASIC[:elements] - ['a']
|
|
16
|
+
)
|
|
17
|
+
|
|
10
18
|
class Action
|
|
11
19
|
module FeatureNameFromRoute
|
|
12
20
|
def feature_name
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'flipper/ui/action'
|
|
2
|
+
require 'flipper/ui/util'
|
|
3
|
+
|
|
4
|
+
module Flipper
|
|
5
|
+
module UI
|
|
6
|
+
module Actions
|
|
7
|
+
class CloudMigrate < UI::Action
|
|
8
|
+
route %r{\A/settings\/cloud/?\Z}
|
|
9
|
+
|
|
10
|
+
def post
|
|
11
|
+
result = Flipper::Cloud.migrate(flipper)
|
|
12
|
+
|
|
13
|
+
if result.url
|
|
14
|
+
status 302
|
|
15
|
+
header 'location', result.url
|
|
16
|
+
halt [@code, @headers, ['']]
|
|
17
|
+
else
|
|
18
|
+
message = "Migration failed (HTTP #{result.code})"
|
|
19
|
+
message << ": #{result.message}" if result.message
|
|
20
|
+
redirect_to "/settings?error=#{Flipper::UI::Util.escape(message)}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -27,6 +27,7 @@ module Flipper
|
|
|
27
27
|
@action_collection.add UI::Actions::Features
|
|
28
28
|
@action_collection.add UI::Actions::Export
|
|
29
29
|
@action_collection.add UI::Actions::Import
|
|
30
|
+
@action_collection.add UI::Actions::CloudMigrate
|
|
30
31
|
@action_collection.add UI::Actions::Settings
|
|
31
32
|
|
|
32
33
|
# Static Assets/Files
|
|
@@ -12,8 +12,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|
|
12
12
|
|
|
13
13
|
document.querySelectorAll("*[data-confirmation-text]").forEach(function (element) {
|
|
14
14
|
element.addEventListener("click", function (e) {
|
|
15
|
-
var expected =
|
|
16
|
-
var actual = prompt(
|
|
15
|
+
var expected = element.getAttribute("data-confirmation-text");
|
|
16
|
+
var actual = prompt(element.getAttribute("data-confirmation-prompt"));
|
|
17
17
|
|
|
18
18
|
if (expected !== actual) {
|
|
19
19
|
e.preventDefault();
|
|
@@ -255,14 +255,13 @@
|
|
|
255
255
|
<div class="row">
|
|
256
256
|
<% unless @feature.boolean_value %>
|
|
257
257
|
<div class="col d-grid">
|
|
258
|
-
<button type="submit" name="action" value="Enable" class="btn btn-outline-success"
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
>
|
|
258
|
+
<button type="submit" name="action" value="Enable" class="btn btn-outline-success"
|
|
259
|
+
<% if Flipper::UI.configuration.confirm_fully_enable %>
|
|
260
|
+
data-confirmation-prompt="Are you sure you want to fully enable this feature for everyone? Please enter the name of the feature to confirm it: <%= feature_name %>"
|
|
261
|
+
data-confirmation-text="<%= feature_name %>"
|
|
262
|
+
<% end %>
|
|
263
|
+
>
|
|
264
|
+
<span class="d-block" data-bs-toggle="tooltip" title="Enable for everyone">
|
|
266
265
|
Fully Enable
|
|
267
266
|
</span>
|
|
268
267
|
</button>
|
|
@@ -271,14 +270,13 @@
|
|
|
271
270
|
|
|
272
271
|
<% unless @feature.off? %>
|
|
273
272
|
<div class="col d-grid">
|
|
274
|
-
<button type="submit" name="action" value="Disable" class="btn btn-outline-danger"
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
>
|
|
273
|
+
<button type="submit" name="action" value="Disable" class="btn btn-outline-danger"
|
|
274
|
+
<% if Flipper::UI.configuration.confirm_disable %>
|
|
275
|
+
data-confirmation-prompt="Are you sure you want to disable this feature for everyone? Please enter the name of the feature to confirm it: <%= feature_name %>"
|
|
276
|
+
data-confirmation-text="<%= feature_name %>"
|
|
277
|
+
<% end %>
|
|
278
|
+
>
|
|
279
|
+
<span class="d-block" data-bs-toggle="tooltip" title="Disable for everyone by clearing all percentages, groups and actors.">
|
|
282
280
|
Disable
|
|
283
281
|
</span>
|
|
284
282
|
</button>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<div class="text-truncate" style="font-weight: 500"><%= feature.key %></div>
|
|
46
46
|
<% if Flipper::UI.configuration.show_feature_description_in_list? && Flipper::UI::Util.present?(feature.description) %>
|
|
47
47
|
<div class="text-muted fw-light" style="line-height: 1.4; white-space: initial; padding: 8px 0">
|
|
48
|
-
<%== Sanitize.fragment(feature.description,
|
|
48
|
+
<%== Sanitize.fragment(feature.description, Flipper::UI::SANITIZE_LIST) %>
|
|
49
49
|
</div>
|
|
50
50
|
<% end %>
|
|
51
51
|
<div class="text-muted text-truncate">
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
<% if params.key?("error") %>
|
|
2
|
+
<div class="alert alert-danger"><%= params["error"] %></div>
|
|
3
|
+
<% end %>
|
|
4
|
+
|
|
5
|
+
<div class="card mb-4">
|
|
6
|
+
<div class="card-header">
|
|
7
|
+
<h4 class="m-0">Migrate to Flipper Cloud</h4>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="card-body">
|
|
10
|
+
<p>Flipper Cloud gives you audit history, rollback, finer-grained permissions, and multi-environment sync. We even have a free tier!</p>
|
|
11
|
+
|
|
12
|
+
<ul class="mb-3">
|
|
13
|
+
<li>Audit log of every feature change</li>
|
|
14
|
+
<li>Instant rollback to any previous state</li>
|
|
15
|
+
<li>Team permissions and approval workflows</li>
|
|
16
|
+
<li>Sync flags across environments</li>
|
|
17
|
+
</ul>
|
|
18
|
+
|
|
19
|
+
<p>You have <strong><%= flipper.features.count %></strong> <%= flipper.features.count == 1 ? 'feature' : 'features' %> ready to migrate.</p>
|
|
20
|
+
|
|
21
|
+
<form action="<%= script_name %>/settings/cloud" method="post">
|
|
22
|
+
<%== csrf_input_tag %>
|
|
23
|
+
<input type="submit" value="Migrate to Flipper Cloud" class="btn btn-primary">
|
|
24
|
+
</form>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
1
28
|
<div class="card mb-4 ">
|
|
2
29
|
<div class="card-header">
|
|
3
30
|
<h4 class="m-0">Export</h4>
|
data/lib/flipper/version.rb
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'flipper/cloud/migrate'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Flipper::UI::Actions::CloudMigrate do
|
|
4
|
+
let(:token) do
|
|
5
|
+
if Rack::Protection::AuthenticityToken.respond_to?(:random_token)
|
|
6
|
+
Rack::Protection::AuthenticityToken.random_token
|
|
7
|
+
else
|
|
8
|
+
'a'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:session) do
|
|
13
|
+
{ :csrf => token, 'csrf' => token, '_csrf_token' => token }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "POST /settings/cloud" do
|
|
17
|
+
context "when migration succeeds" do
|
|
18
|
+
before do
|
|
19
|
+
flipper.enable :search
|
|
20
|
+
allow(Flipper::Cloud).to receive(:migrate).and_return(
|
|
21
|
+
Flipper::Cloud::MigrateResult.new(code: 200, url: "https://www.flippercloud.io/cloud/setup/abc123")
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
post '/settings/cloud',
|
|
25
|
+
{'authenticity_token' => token},
|
|
26
|
+
'rack.session' => session
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'redirects to the cloud URL' do
|
|
30
|
+
expect(last_response.status).to be(302)
|
|
31
|
+
expect(last_response.headers['location']).to eq('https://www.flippercloud.io/cloud/setup/abc123')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "when migration fails" do
|
|
36
|
+
before do
|
|
37
|
+
flipper.enable :search
|
|
38
|
+
allow(Flipper::Cloud).to receive(:migrate).and_return(
|
|
39
|
+
Flipper::Cloud::MigrateResult.new(code: 500, url: nil)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
post '/settings/cloud',
|
|
43
|
+
{'authenticity_token' => token},
|
|
44
|
+
'rack.session' => session
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'redirects back to settings with error' do
|
|
48
|
+
expect(last_response.status).to be(302)
|
|
49
|
+
expect(last_response.headers['location']).to include('/settings')
|
|
50
|
+
expect(last_response.headers['location']).to include('error=')
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -211,4 +211,18 @@ RSpec.describe Flipper::UI::Actions::Feature do
|
|
|
211
211
|
expect(last_response.body).to include('../../../../blah')
|
|
212
212
|
end
|
|
213
213
|
end
|
|
214
|
+
|
|
215
|
+
describe 'GET /features/:feature with question mark in feature name' do
|
|
216
|
+
before do
|
|
217
|
+
get '/features/can_do_stuff%3F'
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'responds with success' do
|
|
221
|
+
expect(last_response.status).to be(200)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it 'renders template with question mark in feature name' do
|
|
225
|
+
expect(last_response.body).to include('can_do_stuff?')
|
|
226
|
+
end
|
|
227
|
+
end
|
|
214
228
|
end
|
|
@@ -32,10 +32,12 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
|
32
32
|
flipper.add("../../../../blah")
|
|
33
33
|
flipper.add("this that")
|
|
34
34
|
flipper.add("foo/bar")
|
|
35
|
+
flipper.add("can_do_stuff?")
|
|
35
36
|
get '/features'
|
|
36
37
|
expect(last_response.body).to include("..%2F..%2F..%2F..%2Fblah")
|
|
37
38
|
expect(last_response.body).to include("this+that")
|
|
38
39
|
expect(last_response.body).to include("foo%2Fbar")
|
|
40
|
+
expect(last_response.body).to include("can_do_stuff%3F")
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
context "when there are no features to list" do
|
|
@@ -88,6 +90,25 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
|
88
90
|
expect(last_response.body).not_to include('<a class="btn btn-primary btn-sm" href="/features/new">Add Feature</a>')
|
|
89
91
|
end
|
|
90
92
|
end
|
|
93
|
+
|
|
94
|
+
context 'when descriptions have links' do
|
|
95
|
+
before do
|
|
96
|
+
Flipper::UI.configuration.show_feature_description_in_list = true
|
|
97
|
+
Flipper::UI.configuration.descriptions_source = lambda { |_keys|
|
|
98
|
+
{ 'test_feature' => 'Check <a href="https://example.com">this link</a> for more info' }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
flipper[:test_feature].enable
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'strips anchor tags from descriptions to avoid nested links' do
|
|
105
|
+
get '/features'
|
|
106
|
+
|
|
107
|
+
expect(last_response.status).to eq(200)
|
|
108
|
+
expect(last_response.body).to include('Check this link for more info')
|
|
109
|
+
expect(last_response.body).not_to include('<a href="https://example.com">this link</a>')
|
|
110
|
+
end
|
|
111
|
+
end
|
|
91
112
|
end
|
|
92
113
|
|
|
93
114
|
describe 'POST /features' do
|
|
@@ -151,6 +172,19 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
|
151
172
|
end
|
|
152
173
|
end
|
|
153
174
|
|
|
175
|
+
context 'feature name contains question mark' do
|
|
176
|
+
let(:feature_name) { 'can_do_stuff?' }
|
|
177
|
+
|
|
178
|
+
it 'adds feature with question mark' do
|
|
179
|
+
expect(flipper.features.map(&:key)).to include('can_do_stuff?')
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'redirects to feature with encoded question mark' do
|
|
183
|
+
expect(last_response.status).to be(302)
|
|
184
|
+
expect(last_response.headers['location']).to eq('/features/can_do_stuff%3F')
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
154
188
|
context 'for an invalid feature name' do
|
|
155
189
|
context 'empty feature name' do
|
|
156
190
|
let(:feature_name) { '' }
|
|
@@ -23,5 +23,13 @@ RSpec.describe Flipper::UI::Actions::Settings do
|
|
|
23
23
|
it 'renders template' do
|
|
24
24
|
expect(last_response.body).to include('Download')
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
it 'includes migrate to Flipper Cloud card' do
|
|
28
|
+
expect(last_response.body).to include('Migrate to Flipper Cloud')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'shows feature count' do
|
|
32
|
+
expect(last_response.body).to include('features ready to migrate')
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flipper-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.7.test
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Nunemaker
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-02-25 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: rack
|
|
@@ -75,14 +76,14 @@ dependencies:
|
|
|
75
76
|
requirements:
|
|
76
77
|
- - "~>"
|
|
77
78
|
- !ruby/object:Gem::Version
|
|
78
|
-
version: 1.3.
|
|
79
|
+
version: 1.3.7.test
|
|
79
80
|
type: :runtime
|
|
80
81
|
prerelease: false
|
|
81
82
|
version_requirements: !ruby/object:Gem::Requirement
|
|
82
83
|
requirements:
|
|
83
84
|
- - "~>"
|
|
84
85
|
- !ruby/object:Gem::Version
|
|
85
|
-
version: 1.3.
|
|
86
|
+
version: 1.3.7.test
|
|
86
87
|
- !ruby/object:Gem::Dependency
|
|
87
88
|
name: erubi
|
|
88
89
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,6 +118,7 @@ dependencies:
|
|
|
117
118
|
- - "<"
|
|
118
119
|
- !ruby/object:Gem::Version
|
|
119
120
|
version: '8'
|
|
121
|
+
description:
|
|
120
122
|
email: support@flippercloud.io
|
|
121
123
|
executables: []
|
|
122
124
|
extensions: []
|
|
@@ -134,6 +136,7 @@ files:
|
|
|
134
136
|
- lib/flipper/ui/actions/actors_gate.rb
|
|
135
137
|
- lib/flipper/ui/actions/add_feature.rb
|
|
136
138
|
- lib/flipper/ui/actions/boolean_gate.rb
|
|
139
|
+
- lib/flipper/ui/actions/cloud_migrate.rb
|
|
137
140
|
- lib/flipper/ui/actions/export.rb
|
|
138
141
|
- lib/flipper/ui/actions/feature.rb
|
|
139
142
|
- lib/flipper/ui/actions/features.rb
|
|
@@ -175,6 +178,7 @@ files:
|
|
|
175
178
|
- spec/flipper/ui/actions/actors_gate_spec.rb
|
|
176
179
|
- spec/flipper/ui/actions/add_feature_spec.rb
|
|
177
180
|
- spec/flipper/ui/actions/boolean_gate_spec.rb
|
|
181
|
+
- spec/flipper/ui/actions/cloud_migrate_spec.rb
|
|
178
182
|
- spec/flipper/ui/actions/export_spec.rb
|
|
179
183
|
- spec/flipper/ui/actions/feature_spec.rb
|
|
180
184
|
- spec/flipper/ui/actions/features_spec.rb
|
|
@@ -198,8 +202,9 @@ metadata:
|
|
|
198
202
|
homepage_uri: https://www.flippercloud.io
|
|
199
203
|
source_code_uri: https://github.com/flippercloud/flipper
|
|
200
204
|
bug_tracker_uri: https://github.com/flippercloud/flipper/issues
|
|
201
|
-
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.
|
|
205
|
+
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.7.test
|
|
202
206
|
funding_uri: https://github.com/sponsors/flippercloud
|
|
207
|
+
post_install_message:
|
|
203
208
|
rdoc_options: []
|
|
204
209
|
require_paths:
|
|
205
210
|
- lib
|
|
@@ -214,26 +219,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
214
219
|
- !ruby/object:Gem::Version
|
|
215
220
|
version: '0'
|
|
216
221
|
requirements: []
|
|
217
|
-
rubygems_version: 3.
|
|
222
|
+
rubygems_version: 3.5.22
|
|
223
|
+
signing_key:
|
|
218
224
|
specification_version: 4
|
|
219
225
|
summary: Feature flag UI for the Flipper gem
|
|
220
|
-
test_files:
|
|
221
|
-
- spec/flipper/ui/action_spec.rb
|
|
222
|
-
- spec/flipper/ui/actions/actors_gate_spec.rb
|
|
223
|
-
- spec/flipper/ui/actions/add_feature_spec.rb
|
|
224
|
-
- spec/flipper/ui/actions/boolean_gate_spec.rb
|
|
225
|
-
- spec/flipper/ui/actions/export_spec.rb
|
|
226
|
-
- spec/flipper/ui/actions/feature_spec.rb
|
|
227
|
-
- spec/flipper/ui/actions/features_spec.rb
|
|
228
|
-
- spec/flipper/ui/actions/file_spec.rb
|
|
229
|
-
- spec/flipper/ui/actions/groups_gate_spec.rb
|
|
230
|
-
- spec/flipper/ui/actions/home_spec.rb
|
|
231
|
-
- spec/flipper/ui/actions/import_spec.rb
|
|
232
|
-
- spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb
|
|
233
|
-
- spec/flipper/ui/actions/percentage_of_time_gate_spec.rb
|
|
234
|
-
- spec/flipper/ui/actions/settings_spec.rb
|
|
235
|
-
- spec/flipper/ui/configuration_spec.rb
|
|
236
|
-
- spec/flipper/ui/decorators/feature_spec.rb
|
|
237
|
-
- spec/flipper/ui/decorators/gate_spec.rb
|
|
238
|
-
- spec/flipper/ui/util_spec.rb
|
|
239
|
-
- spec/flipper/ui_spec.rb
|
|
226
|
+
test_files: []
|