flipflop 2.2.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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