rspec_power 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 15e38ae448c0c4dfeb0251c6186f5fff49aeb9fc36b68ee2c307e5bb9956a0e3
4
+ data.tar.gz: 29e94be5c241dcf1f839e5494c41d1327b289c8a6763a1cab3cda2ef4d601e81
5
+ SHA512:
6
+ metadata.gz: 5966690eb7da430b3e2ddd99acee303efb0c46909b6dca64d4d116fe12264d4e6fb40b215128850e4392f74fb152515e2dc2c667de2c8be52695467eaafe697f
7
+ data.tar.gz: 2b1c5ce8f74d29bbb54715aa8d4f9255a685f4e624073508e2fc2209d99a65bfcae6339d306a6bcc482641dc1325507781838a5c1dce7ed3b7d37b61128e8dac
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Igor Kasyanchuk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,503 @@
1
+ # RSpec Power πŸ”₯
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rspec_power.svg)](https://badge.fury.io/rb/rspec_power)
4
+ [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![RailsJazz](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/my_other.svg?raw=true)](https://www.railsjazz.com)
6
+
7
+ A powerful collection of RSpec helpers and utilities that supercharge your Rails testing experience! πŸš€
8
+
9
+ ## ✨ Features
10
+
11
+ | Feature | Summary | Usage |
12
+ | --- | --- | --- |
13
+ | [πŸ” Enhanced Logging](#-enhanced-logging) | Capture and control Rails logs; ActiveRecord-only option | `:with_log`, `:with_logs`, `:with_log_ar`, `with_logging`, `with_ar_logging` |
14
+ | [🌍 Environment Management](#-environment-variable-management) | Override environment variables with auto-restore | `:with_env`, `with_test_env` |
15
+ | [🌐 I18n Testing](#-internationalization-i18n-testing) | Switch locales and assert translations | `:with_locale`, `with_locale` |
16
+ | [⏰ Time Freeze](#-time-freeze) | Freeze/travel time for deterministic tests | `:with_time_freeze` |
17
+ | [πŸ•˜ Time Zone](#-time-zone) | Run examples in a specific time zone | `:with_time_zone` |
18
+ | [⚑ Performance Budgeting](#-performance-budgeting) | Enforce maximum example execution time | `with_maximum_execution_time`, `:with_maximum_execution_time` |
19
+ | [πŸ“ Benchmarking](#-benchmarking) | Run examples multiple times and summarize | `with_benchmark: { runs: N }` |
20
+ | [πŸ”’ CI Guards](#-ci-guards) | Conditionally run or skip on CI | `:with_ci_only`, `:with_skip_ci` |
21
+ | [πŸ§ͺ SQL Guards](#-sql-guards) | Ensure no SQL or require at least one | `expect_no_sql`, `:with_no_sql_queries`, `expect_sql`, `:with_sql_queries` |
22
+ | [πŸ’Ύ Request Dump](#-request-dump) | Dump session, cookies, flash, headers after each example | `:with_request_dump`, `with_request_dump: { what: [:session, :cookies, :flash, :headers] }` |
23
+ | [πŸ›’ DB Dump on Failure](#-db-dump-on-failure) | Dump DB tables to CSV when an example fails | `:with_dump_db_on_fail`, `with_dump_db_on_fail: { tables: [...], except: [...] }` |
24
+
25
+ ## πŸ“¦ Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ group :test do
31
+ gem "rspec_power", require: false
32
+ end
33
+ ```
34
+
35
+ And then execute:
36
+
37
+ ```bash
38
+ bundle install
39
+ ```
40
+
41
+ ## πŸš€ Quick Start
42
+
43
+ The gem automatically configures itself when required. Just add it to your Gemfile.
44
+
45
+ ## πŸ“š Usage Examples
46
+
47
+ ### πŸ” Enhanced Logging
48
+
49
+ Capture all Rails logs during specific tests:
50
+
51
+ ```ruby
52
+ RSpec.describe User, :with_log do
53
+ it "creates a user with logging" do
54
+ # All Rails logs will be captured and displayed
55
+ user = User.create!(name: "John Doe", email: "john@example.com")
56
+ expect(user).to be_persisted
57
+ end
58
+ end
59
+ ```
60
+
61
+ or for specific tests (alias `:with_logs` is also supported):
62
+
63
+ ```ruby
64
+ RSpec.describe User do
65
+ it "creates a user with logging", :with_log do
66
+ # All Rails logs will be captured and displayed
67
+ user = User.create!(name: "John Doe", email: "john@example.com")
68
+ expect(user).to be_persisted
69
+ end
70
+ end
71
+ ```
72
+
73
+ Capture only ActiveRecord logs:
74
+
75
+ ```ruby
76
+ RSpec.describe User, :with_log_ar do
77
+ it "shows SQL queries" do
78
+ # Only ActiveRecord logs will be captured
79
+ users = User.where(active: true).includes(:profile)
80
+ expect(users).to be_any
81
+ end
82
+ end
83
+ ```
84
+
85
+ Manual logging control:
86
+
87
+ ```ruby
88
+ RSpec.describe User do
89
+ it "manually controls logging" do
90
+ with_logging do
91
+ # All Rails logs captured here
92
+ User.create!(name: "Jane")
93
+ end
94
+ # Logging back to normal here
95
+ end
96
+
97
+ it "captures only ActiveRecord logs" do
98
+ with_ar_logging do
99
+ # Only ActiveRecord logs captured here
100
+ User.count
101
+ end
102
+ end
103
+ end
104
+ ```
105
+
106
+ ### πŸ§ͺ SQL Guards
107
+
108
+ Ensure a block performs no SQL:
109
+
110
+ ```ruby
111
+ RSpec.describe CacheWarmup do
112
+ it "does not hit the DB" do
113
+ expect_no_sql do
114
+ CacheWarmup.build_in_memory!
115
+ end
116
+ end
117
+ end
118
+ ```
119
+
120
+ Require that a block performs at least one SQL statement:
121
+
122
+ ```ruby
123
+ RSpec.describe MigrationChecker do
124
+ it "touches the DB" do
125
+ expect_sql do
126
+ ActiveRecord::Base.connection.execute("SELECT 1")
127
+ end
128
+ end
129
+ end
130
+ ```
131
+
132
+ Or tag an example/group to enforce or require queries automatically:
133
+
134
+ ```ruby
135
+ RSpec.describe CacheWarmup, :with_no_sql_queries do
136
+ it "builds entirely in memory" do
137
+ CacheWarmup.build_in_memory!
138
+ end
139
+ end
140
+
141
+ RSpec.describe MigrationChecker, :with_sql_queries do
142
+ it "must hit the DB at least once" do
143
+ ActiveRecord::Base.connection.execute("SELECT 1")
144
+ end
145
+ end
146
+
147
+ Supported tags: `:with_no_sql_queries`, `:with_sql_queries`
148
+ ```
149
+
150
+ ### 🌍 Environment Variable Management
151
+
152
+ Override environment variables for specific tests:
153
+
154
+ ```ruby
155
+ RSpec.describe PaymentService, :with_env do
156
+ it "uses test API key", with_env: { 'STRIPE_API_KEY' => 'test_key_123' } do
157
+ service = PaymentService.new
158
+ expect(service.api_key).to eq('test_key_123')
159
+ end
160
+
161
+ it "handles multiple env vars", with_env: {
162
+ 'RAILS_ENV' => 'test',
163
+ 'DATABASE_URL' => 'postgresql://localhost/test_db'
164
+ } do
165
+ expect(ENV['RAILS_ENV']).to eq('test')
166
+ expect(ENV['DATABASE_URL']).to eq('postgresql://localhost/test_db')
167
+ end
168
+ end
169
+ ```
170
+
171
+ Manual environment control:
172
+
173
+ ```ruby
174
+ RSpec.describe ConfigService do
175
+ it "manually overrides environment" do
176
+ with_test_env('API_URL' => 'https://api.test.com') do
177
+ expect(ENV['API_URL']).to eq('https://api.test.com')
178
+ end
179
+ # Environment restored automatically
180
+ end
181
+ end
182
+ ```
183
+
184
+ ### 🌐 Internationalization (I18n) Testing
185
+
186
+ Test your application in different locales:
187
+
188
+ ```ruby
189
+ RSpec.describe User, :with_locale do
190
+ it "displays name in English", with_locale: :en do
191
+ user = User.new(name: "John")
192
+ expect(user.greeting).to eq("Hello, John!")
193
+ end
194
+
195
+ it "displays name in Spanish", with_locale: :es do
196
+ user = User.new(name: "Juan")
197
+ expect(user.greeting).to eq("Β‘Hola, Juan!")
198
+ end
199
+
200
+ it "displays name in French", with_locale: :fr do
201
+ user = User.new(name: "Jean")
202
+ expect(user.greeting).to eq("Bonjour, Jean!")
203
+ end
204
+ end
205
+ ```
206
+
207
+ Manual locale control:
208
+
209
+ ```ruby
210
+ RSpec.describe LocalizationHelper do
211
+ it "manually changes locale" do
212
+ with_locale(:de) do
213
+ expect(I18n.locale).to eq(:de)
214
+ expect(t('hello')).to eq('Hallo')
215
+ end
216
+ # Locale restored automatically
217
+ end
218
+ end
219
+ ```
220
+
221
+ ### ⏰ Time Freeze
222
+
223
+ Freeze time for consistent test results:
224
+
225
+ ```ruby
226
+ RSpec.describe Order, :with_time_freeze do
227
+ it "creates order with current timestamp", with_time_freeze: "2024-01-15 10:30:00" do
228
+ order = Order.create!(amount: 100)
229
+ expect(order.created_at).to eq(Time.parse("2024-01-15 10:30:00"))
230
+ end
231
+
232
+ it "handles time-sensitive logic", with_time_freeze: Time.new(2024, 12, 25, 12, 0, 0) do
233
+ expect(Time.current).to eq(Time.new(2024, 12, 25, 12, 0, 0))
234
+ # Test Christmas-specific logic
235
+ end
236
+ end
237
+ ```
238
+
239
+ ### πŸ•˜ Time Zone
240
+
241
+ ```ruby
242
+ RSpec.describe ReportGenerator do
243
+ it "builds report in US Pacific", with_time_zone: "Pacific Time (US & Canada)" do
244
+ # The block runs with Time.zone set to Pacific
245
+ expect(Time.zone.name).to eq("Pacific Time (US & Canada)")
246
+ end
247
+ end
248
+ ```
249
+
250
+ ### ⚑ Performance Budgeting
251
+
252
+ Limit example duration:
253
+
254
+ ```ruby
255
+ RSpec.describe Importer do
256
+ it "is fast enough" do
257
+ with_maximum_execution_time(50) do
258
+ Importer.run!
259
+ end
260
+ end
261
+ end
262
+ ```
263
+
264
+ Or via metadata:
265
+
266
+ ```ruby
267
+ RSpec.describe Importer, with_maximum_execution_time: 100 do
268
+ it "completes quickly" do
269
+ Importer.run!
270
+ end
271
+ end
272
+ ```
273
+
274
+ ### πŸ“ Benchmarking
275
+
276
+ Benchmark entire examples via metadata and get a suite summary:
277
+
278
+ ```ruby
279
+ RSpec.describe Parser, with_benchmark: { runs: 10 } do
280
+ it "parses quickly" do
281
+ Parser.parse!(payload)
282
+ end
283
+ end
284
+ ```
285
+
286
+ The example is executed multiple times (runs) and the average/min/max times are printed after the suite.
287
+
288
+ ### πŸ’Ύ Request Dump
289
+
290
+ Dump request-related state after each example to help debug request specs.
291
+
292
+ Supported items:
293
+
294
+ - `:session`
295
+ - `:cookies`
296
+ - `:flash`
297
+ - `:headers`
298
+
299
+ Enable for an example or group and choose what to dump:
300
+
301
+ ```ruby
302
+ RSpec.describe "Users API", type: :request do
303
+ it "dumps everything by default", :with_request_dump do
304
+ post "/set_state"
305
+ expect(response).to be_successful
306
+ end
307
+
308
+ it "dumps only session and cookies",
309
+ with_request_dump: { what: [:session, :cookies] } do
310
+ post "/set_state"
311
+ expect(response).to be_successful
312
+ end
313
+ end
314
+ ```
315
+
316
+ Example output:
317
+
318
+ ```
319
+ [rspec_power] Dump after example: Users API dumps everything by default
320
+ [rspec_power] session: {"user_id"=>42}
321
+ [rspec_power] cookies: {"hello"=>"world"}
322
+ [rspec_power] flash: {"notice"=>"done"}
323
+ [rspec_power] headers: { ... }
324
+ ```
325
+
326
+ ### πŸ”’ CI Guards
327
+
328
+ Run or skip specs depending on whether the suite is running on CI.
329
+
330
+ - Tag to run only on CI: `:with_ci_only`
331
+ - Tag to skip on CI: `:with_skip_ci`
332
+
333
+ ```ruby
334
+ RSpec.describe Deployment, :with_ci_only do
335
+ it "runs only on CI" do
336
+ expect(ENV["CI"]).to be_present
337
+ end
338
+ end
339
+
340
+ RSpec.describe HeavySpec, :with_skip_ci do
341
+ it "skips on CI" do
342
+ # expensive checks
343
+ end
344
+ end
345
+ ```
346
+
347
+ CI detection via environment variable:
348
+
349
+ The guards rely on the `CI` environment variable:
350
+
351
+ - Considered CI when `ENV["CI"]` is set to any non-empty value other than `"false"` or `"0"` (case-insensitive).
352
+ - Considered non-CI when `ENV["CI"]` is unset/empty, `"false"`, or `"0"`.
353
+
354
+ Examples:
355
+
356
+ ```bash
357
+ # Run a single file as if on CI
358
+ CI=true bundle exec rspec spec/path/to/file_spec.rb
359
+
360
+ # Also treated as CI
361
+ CI=1 bundle exec rspec
362
+
363
+ # Explicitly run as non-CI
364
+ CI=0 bundle exec rspec
365
+ ```
366
+
367
+ ### πŸ›’ DB Dump on Failure
368
+
369
+ Dump database state to CSV files when an example fails. Useful to inspect exactly what data led to the failure.
370
+
371
+ - Defaults:
372
+ - Dumps all non-empty tables, excluding `schema_migrations` and `ar_internal_metadata`
373
+ - Exports each table to a separate CSV, ordered by primary key (if present)
374
+ - Writes to `tmp/rspec_power/db_failures/<timestamp>_<spec-name>/`
375
+ - Includes `metadata.json` with spec info
376
+
377
+ Enable for an example or group:
378
+
379
+ ```ruby
380
+ RSpec.describe User, :with_dump_db_on_fail do
381
+ it "creates a user" do
382
+ # ...
383
+ end
384
+ end
385
+ ```
386
+
387
+ Customize which tables to include/exclude and output directory:
388
+
389
+ ```ruby
390
+ RSpec.describe Report, with_dump_db_on_fail: {
391
+ tables: ["users", "accounts"],
392
+ except: ["accounts"],
393
+ dir: Rails.root.join("tmp", "db_dumps").to_s
394
+ } do
395
+ it "fails and dumps only selected tables" do
396
+ # ...
397
+ end
398
+ end
399
+ ```
400
+
401
+ Options:
402
+
403
+ - `tables` / `only`: whitelist tables to dump
404
+ - `except` / `exclude`: tables to skip
405
+ - `dir`: base output directory (default: `tmp/rspec_power/db_failures`)
406
+
407
+ Compatibility: the legacy tag `:dump_db_on_fail` remains supported as an alias.
408
+
409
+ ## 🎯 Shared Contexts
410
+
411
+ The gem provides several pre-configured shared contexts:
412
+
413
+ - `rspec_power::logging:verbose` - Enables verbose logging for tests with `:with_log` metadata
414
+ - `rspec_power::logging:active_record` - Enables ActiveRecord logging for tests with `:with_log_ar` metadata
415
+ - `rspec_power::env:override` - Automatically handles environment variable overrides
416
+ - `rspec_power::i18n:dynamic` - Manages locale changes for tests with `:with_locale` metadata
417
+ - `rspec_power::time:freeze` - Handles time freezing for tests with `:with_time_freeze` metadata
418
+ - `rspec_power::time:zone` - Executes examples in a given time zone with `:with_time_zone` metadata
419
+ - `rspec_power::ci:only` - Runs examples only in CI when tagged with `:with_ci_only`
420
+ - `rspec_power::ci:skip` - Skips examples in CI when tagged with `:with_skip_ci`
421
+ - `rspec_power::request_dump:after` - Dumps selected request state after each example with `:with_request_dump` metadata
422
+
423
+ ## πŸ”§ Configuration
424
+
425
+ The gem automatically configures itself, but you can customize the behavior:
426
+
427
+ ```ruby
428
+ # In spec_helper.rb or rails_helper.rb
429
+ RSpec.configure do |config|
430
+ # Customize logging behavior
431
+ config.include RSpecPower::Rails::LoggingHelpers
432
+ config.include_context "rspec_power::logging:verbose", with_log: true
433
+ config.include_context "rspec_power::logging:verbose", with_logs: true
434
+ config.include_context "rspec_power::logging:active_record", with_log_ar: true
435
+
436
+ # Customize environment helpers
437
+ config.include RSpecPower::Rails::EnvHelpers
438
+ config.include_context "rspec_power::env:override", :with_env
439
+
440
+ # Customize I18n helpers
441
+ config.include RSpecPower::Rails::I18nHelpers
442
+ config.include_context "rspec_power::i18n:dynamic", :with_locale
443
+
444
+ # Customize time helpers
445
+ config.include RSpecPower::Rails::TimeHelpers
446
+ config.include_context "rspec_power::time:freeze", :with_time_freeze
447
+ config.include_context "rspec_power::time:zone", :with_time_zone
448
+
449
+ # CI-only guards
450
+ config.include_context "rspec_power::ci:only", :with_ci_only
451
+ config.include_context "rspec_power::ci:skip", :with_skip_ci
452
+
453
+ # Request dump helpers (session/cookies/flash/headers)
454
+ config.include RSpecPower::RequestDumpHelpers
455
+ config.include_context "rspec_power::request_dump:after", :with_request_dump
456
+ end
457
+ ```
458
+
459
+ ## πŸ§ͺ Testing
460
+
461
+ Run the test suite:
462
+
463
+ ```bash
464
+ bundle exec rspec
465
+ ```
466
+
467
+ ## Linter
468
+
469
+ ```bash
470
+ bundle exec rubocop
471
+ ```
472
+
473
+ To fix most issues, run:
474
+
475
+ ```bash
476
+ bundle exec rubocop -A
477
+ ```
478
+
479
+
480
+ ## 🀝 Contributing
481
+
482
+ 1. Fork the repository
483
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
484
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
485
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
486
+ 5. Open a Pull Request
487
+
488
+ ## πŸ“– Credits
489
+
490
+ Code for logging was extracted from [test-prof](https://github.com/test-prof/test-prof) gem.
491
+
492
+ ## πŸ“„ License
493
+
494
+ This project is licensed under the MIT License - see the [MIT-LICENSE](MIT-LICENSE) file for details.
495
+
496
+ ---
497
+
498
+ [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
499
+ />](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=rails_performance)
500
+
501
+ [!["Buy Me A Coffee"](https://github.com/igorkasyanchuk/get-smart/blob/main/docs/snapshot-bmc-button.png?raw=true)](https://buymeacoffee.com/igorkasyanchuk)
502
+
503
+ Made with ❀️ for the Rails testing community!
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+
5
+ require "bundler/gem_tasks"
6
+
7
+ require "rspec/core/rake_task"
8
+
9
+ # plugin’s own specs
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ # dummy app specs
13
+ namespace :dummy do
14
+ desc "Run dummy Rails app specs"
15
+ task :spec do
16
+ Dir.chdir("spec/dummy") { sh "bundle exec rspec" }
17
+ end
18
+ end
19
+
20
+ task default: [ :spec, "dummy:spec" ]
@@ -0,0 +1,74 @@
1
+ module RSpecPower
2
+ module BenchmarkHelpers
3
+ class << self
4
+ def results_registry
5
+ @results_registry ||= []
6
+ end
7
+
8
+ def add_result(result)
9
+ results_registry << result
10
+ end
11
+
12
+ def results
13
+ results_registry.dup
14
+ end
15
+ end
16
+
17
+ # Internal: run the given block multiple times and record a summary.
18
+ # Used by the shared context, not exposed as a public helper anymore.
19
+ def __run_benchmark__(runs: 1, label:)
20
+ raise ArgumentError, "__run_benchmark__ requires a block" unless block_given?
21
+
22
+ iterations = runs.to_i
23
+ raise ArgumentError, "runs must be >= 1" if iterations < 1
24
+
25
+ timings_ms = []
26
+ iterations.times do
27
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
+ yield
29
+ finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
30
+ timings_ms << (finish - start) * 1000.0
31
+ end
32
+
33
+ avg = timings_ms.sum / timings_ms.length
34
+ summary = {
35
+ label: label,
36
+ runs: iterations,
37
+ avg_ms: avg,
38
+ min_ms: timings_ms.min,
39
+ max_ms: timings_ms.max
40
+ }
41
+
42
+ BenchmarkHelpers.add_result(summary)
43
+ summary
44
+ end
45
+ end
46
+ end
47
+
48
+ # Shared context to configure benchmark with metadata
49
+ RSpec.shared_context "rspec_power::benchmark:run" do
50
+ around(:each) do |example|
51
+ opts = example.metadata[:with_benchmark]
52
+ if opts
53
+ runs = (opts[:runs] || 1).to_i
54
+ label = example.full_description
55
+ extend RSpecPower::BenchmarkHelpers
56
+ __run_benchmark__(runs: runs, label: label) { example.run }
57
+ else
58
+ example.run
59
+ end
60
+ end
61
+ end
62
+
63
+ # Print a consolidated report after the suite finishes
64
+ RSpec.configure do |config|
65
+ config.after(:suite) do
66
+ results = RSpecPower::BenchmarkHelpers.results
67
+ next if results.empty?
68
+
69
+ puts "\nBenchmark results (rspec_power):"
70
+ results.each do |r|
71
+ puts "- #{r[:label]}: runs=#{r[:runs]} avg=#{format('%.3f', r[:avg_ms])}ms min=#{format('%.3f', r[:min_ms])}ms max=#{format('%.3f', r[:max_ms])}ms"
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,21 @@
1
+ RSpec.shared_context "rspec_power::ci:only" do
2
+ around(:each) do |example|
3
+ ci = ENV["CI"].to_s.downcase
4
+ if ci == "" || ci == "false" || ci == "0"
5
+ skip "Skipped in non-CI environment"
6
+ else
7
+ example.run
8
+ end
9
+ end
10
+ end
11
+
12
+ RSpec.shared_context "rspec_power::ci:skip" do
13
+ around(:each) do |example|
14
+ ci = ENV["CI"].to_s.downcase
15
+ if ci != "" && ci != "false" && ci != "0"
16
+ skip "Skipped in CI environment"
17
+ else
18
+ example.run
19
+ end
20
+ end
21
+ end