flipflop 2.2.1 → 2.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.
Files changed (50) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +0 -1
  3. data/.travis.yml +23 -27
  4. data/CHANGES.md +7 -0
  5. data/Gemfile +14 -8
  6. data/README.md +27 -1
  7. data/Rakefile +20 -8
  8. data/app/controllers/flipflop/features_controller.rb +20 -10
  9. data/app/controllers/flipflop/strategies_controller.rb +4 -4
  10. data/app/views/flipflop/features/index.html.erb +64 -41
  11. data/app/views/flipflop/stylesheets/_flipflop.css +1 -0
  12. data/app/views/layouts/flipflop.html.erb +8 -1
  13. data/config/features.rb +7 -0
  14. data/config/locales/en.yml +34 -0
  15. data/flipflop.gemspec +0 -5
  16. data/lib/flipflop/configurable.rb +10 -0
  17. data/lib/flipflop/engine.rb +10 -15
  18. data/lib/flipflop/feature_definition.rb +5 -6
  19. data/lib/flipflop/feature_loader.rb +40 -0
  20. data/lib/flipflop/feature_set.rb +7 -9
  21. data/lib/flipflop/group_definition.rb +11 -0
  22. data/lib/flipflop/strategies/abstract_strategy.rb +2 -1
  23. data/lib/flipflop/version.rb +1 -1
  24. data/lib/flipflop.rb +2 -0
  25. data/lib/test_engine/config/features.rb +3 -0
  26. data/lib/test_engine/test_engine.rb +7 -0
  27. data/src/stylesheets/_flipflop.scss +160 -0
  28. data/test/integration/app_test.rb +50 -21
  29. data/test/integration/dashboard_test.rb +395 -52
  30. data/test/templates/nl.yml +30 -0
  31. data/test/templates/test_app_features.rb +7 -0
  32. data/test/templates/test_engine.rb +7 -0
  33. data/test/templates/test_engine_features.rb +3 -0
  34. data/test/test_helper.rb +83 -10
  35. data/test/unit/configurable_test.rb +1 -1
  36. data/test/unit/feature_definition_test.rb +27 -2
  37. data/test/unit/feature_set_test.rb +7 -19
  38. data/test/unit/flipflop_test.rb +16 -11
  39. data/test/unit/group_definition_test.rb +21 -0
  40. data/test/unit/strategies/abstract_strategy_test.rb +14 -25
  41. data/test/unit/strategies/active_record_strategy_test.rb +4 -0
  42. data/test/unit/strategies/cookie_strategy_test.rb +4 -0
  43. data/test/unit/strategies/default_strategy_test.rb +10 -4
  44. data/test/unit/strategies/lambda_strategy_test.rb +11 -27
  45. data/test/unit/strategies/query_string_strategy_test.rb +4 -0
  46. data/test/unit/strategies/redis_strategy_test.rb +4 -0
  47. data/test/unit/strategies/session_strategy_test.rb +4 -0
  48. data/test/unit/strategies/test_strategy_test.rb +4 -0
  49. metadata +22 -9
  50. data/app/assets/stylesheets/flipflop.css +0 -1
@@ -5,59 +5,123 @@ require "capybara/dsl"
5
5
  describe Flipflop do
6
6
  include Capybara::DSL
7
7
 
8
- before do
9
- @app = TestApp.new
10
- end
11
-
12
- subject do
13
- @app
14
- end
15
-
16
- after do
17
- Flipflop::Strategies::AbstractStrategy::RequestInterceptor.request = nil
18
- end
19
-
20
- describe "without features" do
8
+ describe "without translations" do
21
9
  before do
22
- visit "/flipflop"
10
+ @app = TestApp.new
23
11
  end
24
12
 
25
- it "should show feature table with header" do
26
- assert_equal ["Cookie", "Active record", "Default"],
27
- all("thead th").map(&:text)[3..-1]
13
+ after do
14
+ @app.unload!
28
15
  end
29
16
 
30
- it "should show no features" do
31
- assert all("tbody tr").empty?
17
+ subject do
18
+ @app
32
19
  end
33
- end
34
20
 
35
- describe "with features" do
36
- before do
37
- Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
38
- Module.new do
39
- extend Flipflop::Configurable
40
- feature :world_domination, description: "Try and take over the world!"
41
- feature :shiny_things, default: true
21
+ describe "without features" do
22
+ before do
23
+ visit "/flipflop"
24
+ end
25
+
26
+ it "should show feature header" do
27
+ assert_equal "My Test App Features", first("h1").text
42
28
  end
43
29
 
44
- Capybara.current_session.driver.browser.clear_cookies
45
- Flipflop::Feature.delete_all
30
+ it "should show feature table with header" do
31
+ assert_equal ["Cookie", "Active record", "Default"],
32
+ all("thead th").map(&:text)[3..-1]
33
+ end
46
34
 
47
- visit "/flipflop"
35
+ it "should show no features" do
36
+ assert all("tbody tr").empty?
37
+ end
48
38
  end
49
39
 
50
- it "should show feature rows" do
51
- assert_equal ["World domination", "Shiny things"],
52
- all("tr[data-feature] td.name").map(&:text)
40
+ describe "with features" do
41
+ before do
42
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
43
+ Module.new do
44
+ extend Flipflop::Configurable
45
+ feature :world_domination, description: "Try and take over the world!"
46
+ feature :shiny_things, default: true
47
+ end
48
+
49
+ Capybara.current_session.driver.browser.clear_cookies
50
+ Flipflop::Feature.delete_all
51
+
52
+ visit "/flipflop"
53
+ end
54
+
55
+ it "should show feature header" do
56
+ assert_equal "My Test App Features", first("h1").text
57
+ end
58
+
59
+ it "should show feature names" do
60
+ assert_equal ["World domination", "Shiny things"],
61
+ all("tr[data-feature] td.name").map(&:text)
62
+ end
63
+
64
+ it "should show feature descriptions" do
65
+ assert_equal ["Try and take over the world!", "Shiny things."],
66
+ all("tr[data-feature] td.description").map(&:text)
67
+ end
68
+
69
+ it "should not show feature group" do
70
+ assert_equal [], all("tr h2").map(&:text)
71
+ end
53
72
  end
54
73
 
55
- it "should show feature descriptions" do
56
- assert_equal ["Try and take over the world!", "Shiny things."],
57
- all("tr[data-feature] td.description").map(&:text)
74
+ describe "with grouped features" do
75
+ before do
76
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
77
+ Module.new do
78
+ extend Flipflop::Configurable
79
+ group "world" do
80
+ feature :world_domination, description: "Try and take over the world!"
81
+ end
82
+ feature :shiny_things, default: true
83
+ end
84
+
85
+ Capybara.current_session.driver.browser.clear_cookies
86
+ Flipflop::Feature.delete_all
87
+
88
+ visit "/flipflop"
89
+ end
90
+
91
+ it "should show feature header" do
92
+ assert_equal "My Test App Features", first("h1").text
93
+ end
94
+
95
+ it "should show feature names" do
96
+ assert_equal ["World domination", "Shiny things"],
97
+ all("tr[data-feature] td.name").map(&:text)
98
+ end
99
+
100
+ it "should show feature descriptions" do
101
+ assert_equal ["Try and take over the world!", "Shiny things."],
102
+ all("tr[data-feature] td.description").map(&:text)
103
+ end
104
+
105
+ it "should show feature groups" do
106
+ assert_equal ["World", "Other features"], all("tr h2").map(&:text)
107
+ end
58
108
  end
59
109
 
60
110
  describe "with cookie strategy" do
111
+ before do
112
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
113
+ Module.new do
114
+ extend Flipflop::Configurable
115
+ feature :world_domination, description: "Try and take over the world!"
116
+ feature :shiny_things, default: true
117
+ end
118
+
119
+ Capybara.current_session.driver.browser.clear_cookies
120
+ Flipflop::Feature.delete_all
121
+
122
+ visit "/flipflop"
123
+ end
124
+
61
125
  it "should enable feature" do
62
126
  within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
63
127
  click_on "on"
@@ -65,7 +129,7 @@ describe Flipflop do
65
129
 
66
130
  within("tr[data-feature=world-domination]") do
67
131
  assert_equal "on", first("td.status").text
68
- assert_equal "on", first("td[data-strategy=cookie] input.active[type=submit]").value
132
+ assert_equal "on", first("td[data-strategy=cookie] button.active").text
69
133
  end
70
134
  end
71
135
 
@@ -76,7 +140,7 @@ describe Flipflop do
76
140
 
77
141
  within("tr[data-feature=world-domination]") do
78
142
  assert_equal "off", first("td.status").text
79
- assert_equal "off", first("td[data-strategy=cookie] input.active[type=submit]").value
143
+ assert_equal "off", first("td[data-strategy=cookie] button.active").text
80
144
  end
81
145
  end
82
146
 
@@ -91,7 +155,7 @@ describe Flipflop do
91
155
 
92
156
  within("tr[data-feature=world-domination]") do
93
157
  assert_equal "off", first("td.status").text
94
- refute has_selector?("td[data-strategy=cookie] input.active[type=submit]")
158
+ refute has_selector?("td[data-strategy=cookie] button.active")
95
159
  end
96
160
  end
97
161
 
@@ -109,12 +173,26 @@ describe Flipflop do
109
173
 
110
174
  within("tr[data-feature=world-domination]") do
111
175
  assert_equal "on", first("td.status").text
112
- assert_equal "on", first("td[data-strategy=cookie] input.active[type=submit]").value
176
+ assert_equal "on", first("td[data-strategy=cookie] button.active").text
113
177
  end
114
178
  end
115
179
  end
116
180
 
117
181
  describe "with active record strategy" do
182
+ before do
183
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
184
+ Module.new do
185
+ extend Flipflop::Configurable
186
+ feature :world_domination, description: "Try and take over the world!"
187
+ feature :shiny_things, default: true
188
+ end
189
+
190
+ Capybara.current_session.driver.browser.clear_cookies
191
+ Flipflop::Feature.delete_all
192
+
193
+ visit "/flipflop"
194
+ end
195
+
118
196
  it "should enable feature" do
119
197
  within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
120
198
  click_on "on"
@@ -122,7 +200,7 @@ describe Flipflop do
122
200
 
123
201
  within("tr[data-feature=world-domination]") do
124
202
  assert_equal "on", first("td.status").text
125
- assert_equal "on", first("td[data-strategy=active-record] input.active[type=submit]").value
203
+ assert_equal "on", first("td[data-strategy=active-record] button.active").text
126
204
  end
127
205
  end
128
206
 
@@ -133,7 +211,7 @@ describe Flipflop do
133
211
 
134
212
  within("tr[data-feature=world-domination]") do
135
213
  assert_equal "off", first("td.status").text
136
- assert_equal "off", first("td[data-strategy=active-record] input.active[type=submit]").value
214
+ assert_equal "off", first("td[data-strategy=active-record] button.active").text
137
215
  end
138
216
  end
139
217
 
@@ -148,7 +226,7 @@ describe Flipflop do
148
226
 
149
227
  within("tr[data-feature=world-domination]") do
150
228
  assert_equal "off", first("td.status").text
151
- refute has_selector?("td[data-strategy=active-record] input.active[type=submit]")
229
+ refute has_selector?("td[data-strategy=active-record] button.active")
152
230
  end
153
231
  end
154
232
 
@@ -166,25 +244,290 @@ describe Flipflop do
166
244
 
167
245
  within("tr[data-feature=world-domination]") do
168
246
  assert_equal "on", first("td.status").text
169
- assert_equal "on", first("td[data-strategy=active-record] input.active[type=submit]").value
247
+ assert_equal "on", first("td[data-strategy=active-record] button.active").text
170
248
  end
171
249
  end
172
250
  end
251
+
252
+ describe "with hidden strategy" do
253
+ before do
254
+ Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
255
+ Module.new do
256
+ extend Flipflop::Configurable
257
+ strategy :query_string, hidden: true
258
+ end
259
+
260
+ visit "/flipflop"
261
+ end
262
+
263
+ it "should not show hidden strategy" do
264
+ assert_equal [], all("thead th").map(&:text)[3..-1]
265
+ end
266
+ end
173
267
  end
174
268
 
175
- describe "with hidden strategy" do
269
+ describe "with translations" do
176
270
  before do
177
- Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
178
- Module.new do
179
- extend Flipflop::Configurable
180
- strategy :query_string, hidden: true
271
+ @app = TestApp.new([
272
+ TestLocaleGenerator,
273
+ ])
274
+
275
+ I18n.locale = :nl
276
+ end
277
+
278
+ after do
279
+ @app.unload!
280
+ end
281
+
282
+ subject do
283
+ @app
284
+ end
285
+
286
+ describe "without features" do
287
+ before do
288
+ visit "/flipflop"
289
+ end
290
+
291
+ it "should show feature header" do
292
+ assert_equal "My Test App Functionaliteiten", first("h1").text
293
+ end
294
+
295
+ it "should show feature table with header" do
296
+ assert_equal ["Koekje", "Actief archief", "Standaard"],
297
+ all("thead th").map(&:text)[3..-1]
298
+ end
299
+
300
+ it "should show no features" do
301
+ assert all("tbody tr").empty?
302
+ end
303
+ end
304
+
305
+ describe "with features" do
306
+ before do
307
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
308
+ Module.new do
309
+ extend Flipflop::Configurable
310
+ feature :world_domination, description: "Try and take over the world!"
311
+ feature :shiny_things, default: true
312
+ end
313
+
314
+ Capybara.current_session.driver.browser.clear_cookies
315
+ Flipflop::Feature.delete_all
316
+
317
+ visit "/flipflop"
318
+ end
319
+
320
+ it "should show feature header" do
321
+ assert_equal "My Test App Functionaliteiten", first("h1").text
322
+ end
323
+
324
+ it "should show feature names" do
325
+ assert_equal ["Wereldoverheersing", "Glimmende dingetjes"],
326
+ all("tr[data-feature] td.name").map(&:text)
327
+ end
328
+
329
+ it "should show feature descriptions" do
330
+ assert_equal ["Neem de wereld over!", "Glimmende dingetjes."],
331
+ all("tr[data-feature] td.description").map(&:text)
332
+ end
333
+
334
+ it "should not show feature group" do
335
+ assert_equal [], all("tr h2").map(&:text)
336
+ end
337
+ end
338
+
339
+ describe "with grouped features" do
340
+ before do
341
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
342
+ Module.new do
343
+ extend Flipflop::Configurable
344
+ group "world" do
345
+ feature :world_domination, description: "Try and take over the world!"
346
+ end
347
+ feature :shiny_things, default: true
348
+ end
349
+
350
+ Capybara.current_session.driver.browser.clear_cookies
351
+ Flipflop::Feature.delete_all
352
+
353
+ visit "/flipflop"
354
+ end
355
+
356
+ it "should show feature header" do
357
+ assert_equal "My Test App Functionaliteiten", first("h1").text
358
+ end
359
+
360
+ it "should show feature names" do
361
+ assert_equal ["Wereldoverheersing", "Glimmende dingetjes"],
362
+ all("tr[data-feature] td.name").map(&:text)
363
+ end
364
+
365
+ it "should show feature descriptions" do
366
+ assert_equal ["Neem de wereld over!", "Glimmende dingetjes."],
367
+ all("tr[data-feature] td.description").map(&:text)
368
+ end
369
+
370
+ it "should show feature groups" do
371
+ assert_equal ["Wereld", "Overige functionaliteiten"], all("tr h2").map(&:text)
181
372
  end
373
+ end
374
+
375
+ describe "with cookie strategy" do
376
+ before do
377
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
378
+ Module.new do
379
+ extend Flipflop::Configurable
380
+ feature :world_domination, description: "Try and take over the world!"
381
+ feature :shiny_things, default: true
382
+ end
383
+
384
+ Capybara.current_session.driver.browser.clear_cookies
385
+ Flipflop::Feature.delete_all
386
+
387
+ visit "/flipflop"
388
+ end
389
+
390
+ it "should enable feature" do
391
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
392
+ click_on "aan"
393
+ end
394
+
395
+ within("tr[data-feature=world-domination]") do
396
+ assert_equal "aan", first("td.status").text
397
+ assert_equal "aan", first("td[data-strategy=cookie] button.active").text
398
+ end
399
+ end
400
+
401
+ it "should disable feature" do
402
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
403
+ click_on "uit"
404
+ end
182
405
 
183
- visit "/flipflop"
406
+ within("tr[data-feature=world-domination]") do
407
+ assert_equal "uit", first("td.status").text
408
+ assert_equal "uit", first("td[data-strategy=cookie] button.active").text
409
+ end
410
+ end
411
+
412
+ it "should enable and clear feature" do
413
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
414
+ click_on "aan"
415
+ end
416
+
417
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
418
+ click_on "wissen"
419
+ end
420
+
421
+ within("tr[data-feature=world-domination]") do
422
+ assert_equal "uit", first("td.status").text
423
+ refute has_selector?("td[data-strategy=cookie] button.active")
424
+ end
425
+ end
426
+
427
+ it "should enable feature after in spite of redefinition and reordering" do
428
+ Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
429
+ Module.new do
430
+ extend Flipflop::Configurable
431
+ strategy :active_record, description: "Store in database."
432
+ strategy :cookie, description: "Store in cookie."
433
+ end
434
+
435
+ within("tr[data-feature=world-domination] td[data-strategy=cookie]") do
436
+ click_on "aan"
437
+ end
438
+
439
+ within("tr[data-feature=world-domination]") do
440
+ assert_equal "aan", first("td.status").text
441
+ assert_equal "aan", first("td[data-strategy=cookie] button.active").text
442
+ end
443
+ end
184
444
  end
185
445
 
186
- it "should not show hidden strategy" do
187
- assert_equal [], all("thead th").map(&:text)[3..-1]
446
+ describe "with active record strategy" do
447
+ before do
448
+ Flipflop::FeatureSet.current.instance_variable_set(:@features, {})
449
+ Module.new do
450
+ extend Flipflop::Configurable
451
+ feature :world_domination, description: "Try and take over the world!"
452
+ feature :shiny_things, default: true
453
+ end
454
+
455
+ Capybara.current_session.driver.browser.clear_cookies
456
+ Flipflop::Feature.delete_all
457
+
458
+ visit "/flipflop"
459
+ end
460
+
461
+ it "should enable feature" do
462
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
463
+ click_on "aan"
464
+ end
465
+
466
+ within("tr[data-feature=world-domination]") do
467
+ assert_equal "aan", first("td.status").text
468
+ assert_equal "aan", first("td[data-strategy=active-record] button.active").text
469
+ end
470
+ end
471
+
472
+ it "should disable feature" do
473
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
474
+ click_on "uit"
475
+ end
476
+
477
+ within("tr[data-feature=world-domination]") do
478
+ assert_equal "uit", first("td.status").text
479
+ assert_equal "uit", first("td[data-strategy=active-record] button.active").text
480
+ end
481
+ end
482
+
483
+ it "should enable and clear feature" do
484
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
485
+ click_on "aan"
486
+ end
487
+
488
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
489
+ click_on "wissen"
490
+ end
491
+
492
+ within("tr[data-feature=world-domination]") do
493
+ assert_equal "uit", first("td.status").text
494
+ refute has_selector?("td[data-strategy=active-record] button.active")
495
+ end
496
+ end
497
+
498
+ it "should enable feature after in spite of redefinition and reordering" do
499
+ Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
500
+ Module.new do
501
+ extend Flipflop::Configurable
502
+ strategy :active_record, description: "Store in database."
503
+ strategy :cookie, description: "Store in cookie."
504
+ end
505
+
506
+ within("tr[data-feature=world-domination] td[data-strategy=active-record]") do
507
+ click_on "aan"
508
+ end
509
+
510
+ within("tr[data-feature=world-domination]") do
511
+ assert_equal "aan", first("td.status").text
512
+ assert_equal "aan", first("td[data-strategy=active-record] button.active").text
513
+ end
514
+ end
515
+ end
516
+
517
+ describe "with hidden strategy" do
518
+ before do
519
+ Flipflop::FeatureSet.current.instance_variable_set(:@strategies, {})
520
+ Module.new do
521
+ extend Flipflop::Configurable
522
+ strategy :query_string, hidden: true
523
+ end
524
+
525
+ visit "/flipflop"
526
+ end
527
+
528
+ it "should not show hidden strategy" do
529
+ assert_equal [], all("thead th").map(&:text)[3..-1]
530
+ end
188
531
  end
189
532
  end
190
533
  end
@@ -0,0 +1,30 @@
1
+ nl:
2
+ flipflop:
3
+ title: "%{application} Functionaliteiten"
4
+ feature: "Functionaliteit"
5
+ description: "Beschrijving"
6
+
7
+ feature_states:
8
+ enabled: "aan"
9
+ disabled: "uit"
10
+
11
+ clear: "wissen"
12
+
13
+ groups:
14
+ default: "Overige functionaliteiten"
15
+ world: "Wereld"
16
+
17
+ features:
18
+ world_domination: "Wereldoverheersing"
19
+ world_domination_description: "Neem de wereld over!"
20
+ shiny_things: "Glimmende dingetjes"
21
+
22
+ strategies:
23
+ cookie: "Koekje"
24
+ active_record: "Actief archief"
25
+ default: "Standaard"
26
+ lambda: "Lambda"
27
+ query_string: "Vraagkoord"
28
+ redis: "Herverdeel"
29
+ session: "Sessie"
30
+ test: "Test"
@@ -0,0 +1,7 @@
1
+ Flipflop.configure do
2
+ strategy :cookie
3
+ strategy :active_record
4
+ strategy :default
5
+
6
+ feature :application_feature
7
+ end
@@ -0,0 +1,7 @@
1
+ class TestEngine < Rails::Engine
2
+ config.root = "lib/test_engine"
3
+
4
+ initializer "features" do
5
+ Flipflop::FeatureLoader.current.append(self)
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ Flipflop.configure do
2
+ feature :engine_feature
3
+ end