rspec-loop 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.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 kcboschert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # rspec-loop
2
+
3
+ [![Build Status](https://github.com/kcboschert/rspec-loop/actions/workflows/main.yml/badge.svg)](https://github.com/kcboschert/rspec-loop/actions/workflows/main.yml)
4
+
5
+ rspec-loop adds a `:loop` option to run rspec examples multiple times.
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ ```bash
12
+ bundle add rspec-loop --group test
13
+ ```
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ ```bash
18
+ gem install rspec-loop
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ Configure the options in your project's `spec_helper.rb`:
24
+
25
+ ```ruby
26
+ require "rspec/loop"
27
+
28
+ RSpec.configure do |config|
29
+ config.default_loop_count = 5
30
+ end
31
+ ```
32
+
33
+ ### Options
34
+
35
+ | Name | Description | Type | Default |
36
+ | --- | --- | --- | --- |
37
+ | `default_loop_count` | How many iterations to run each test by default. | Integer | `3` |
38
+
39
+ ## Usage
40
+
41
+ To configure an individual test to run multiple times:
42
+
43
+ ```ruby
44
+ it "runs three times", loop: 3 do
45
+ # test code here
46
+ end
47
+ ```
48
+
49
+ To configure a group of examples to run multiple times:
50
+
51
+ ```ruby
52
+ describe "a group of tests", loop: 3 do
53
+ it "runs three times" do
54
+ # test code here
55
+ end
56
+
57
+ it "also runs three times" do
58
+ # test code here
59
+ end
60
+ end
61
+ ```
62
+
63
+ ### Formatter
64
+
65
+ To use the included formatter, use RSpec's [--format option](https://rspec.info/features/3-13/rspec-core/command-line/format-option/):
66
+
67
+ ```bash
68
+ rspec spec --format RSpec::Loop::Formatter
69
+ ```
70
+
71
+ Or via a [rake task](https://rspec.info/features/3-13/rspec-core/command-line/rake-task/):
72
+
73
+ ```ruby
74
+ RSpec::Core::RakeTask.new(:loop) do |t|
75
+ t.rspec_opts = "--format RSpec::Loop::Formatter"
76
+ end
77
+ ```
78
+
79
+ ## Development
80
+
81
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linter. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
82
+
83
+ ## Contributing
84
+
85
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/kcboschert/rspec-loop>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/kcboschert/rspec-loop/blob/main/CODE_OF_CONDUCT.md).
86
+
87
+ ## License
88
+
89
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
90
+
91
+ ## Code of Conduct
92
+
93
+ Everyone interacting in the rspec-loop project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/kcboschert/rspec-loop/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
13
+
14
+ namespace :spec do
15
+ desc "Run specs in a loop"
16
+ RSpec::Core::RakeTask.new(:loop) do |t|
17
+ t.rspec_opts = "--format RSpec::Loop::Formatter"
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "~> 3.10.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ rspec-loop (0.1.0)
5
+ rspec-core (~> 3.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.5.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.3)
15
+ byebug (12.0.0)
16
+ coderay (1.1.3)
17
+ diff-lcs (1.6.2)
18
+ json (2.12.2)
19
+ language_server-protocol (3.17.0.5)
20
+ lint_roller (1.1.0)
21
+ method_source (1.1.0)
22
+ parallel (1.27.0)
23
+ parser (3.3.8.0)
24
+ ast (~> 2.4.1)
25
+ racc
26
+ prism (1.4.0)
27
+ pry (0.15.2)
28
+ coderay (~> 1.1)
29
+ method_source (~> 1.0)
30
+ pry-byebug (3.11.0)
31
+ byebug (~> 12.0)
32
+ pry (>= 0.13, < 0.16)
33
+ racc (1.8.1)
34
+ rainbow (3.1.1)
35
+ rake (13.3.0)
36
+ regexp_parser (2.10.0)
37
+ rspec (3.10.0)
38
+ rspec-core (~> 3.10.0)
39
+ rspec-expectations (~> 3.10.0)
40
+ rspec-mocks (~> 3.10.0)
41
+ rspec-core (3.10.2)
42
+ rspec-support (~> 3.10.0)
43
+ rspec-expectations (3.10.2)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.10.0)
46
+ rspec-mocks (3.10.3)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.10.0)
49
+ rspec-support (3.10.3)
50
+ rubocop (1.78.0)
51
+ json (~> 2.3)
52
+ language_server-protocol (~> 3.17.0.2)
53
+ lint_roller (~> 1.1.0)
54
+ parallel (~> 1.10)
55
+ parser (>= 3.3.0.2)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ regexp_parser (>= 2.9.3, < 3.0)
58
+ rubocop-ast (>= 1.45.1, < 2.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 2.4.0, < 4.0)
61
+ rubocop-ast (1.45.1)
62
+ parser (>= 3.3.7.2)
63
+ prism (~> 1.4)
64
+ ruby-progressbar (1.13.0)
65
+ thor (1.3.2)
66
+ unicode-display_width (3.1.4)
67
+ unicode-emoji (~> 4.0, >= 4.0.4)
68
+ unicode-emoji (4.0.4)
69
+
70
+ PLATFORMS
71
+ ruby
72
+ x86_64-linux
73
+
74
+ DEPENDENCIES
75
+ appraisal
76
+ pry-byebug
77
+ rake (~> 13.0)
78
+ rspec (~> 3.10.0)
79
+ rspec-loop!
80
+ rubocop (~> 1.21)
81
+
82
+ BUNDLED WITH
83
+ 2.6.7
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "~> 3.11.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ rspec-loop (0.1.0)
5
+ rspec-core (~> 3.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.5.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.3)
15
+ byebug (12.0.0)
16
+ coderay (1.1.3)
17
+ diff-lcs (1.6.2)
18
+ json (2.12.2)
19
+ language_server-protocol (3.17.0.5)
20
+ lint_roller (1.1.0)
21
+ method_source (1.1.0)
22
+ parallel (1.27.0)
23
+ parser (3.3.8.0)
24
+ ast (~> 2.4.1)
25
+ racc
26
+ prism (1.4.0)
27
+ pry (0.15.2)
28
+ coderay (~> 1.1)
29
+ method_source (~> 1.0)
30
+ pry-byebug (3.11.0)
31
+ byebug (~> 12.0)
32
+ pry (>= 0.13, < 0.16)
33
+ racc (1.8.1)
34
+ rainbow (3.1.1)
35
+ rake (13.3.0)
36
+ regexp_parser (2.10.0)
37
+ rspec (3.11.0)
38
+ rspec-core (~> 3.11.0)
39
+ rspec-expectations (~> 3.11.0)
40
+ rspec-mocks (~> 3.11.0)
41
+ rspec-core (3.11.0)
42
+ rspec-support (~> 3.11.0)
43
+ rspec-expectations (3.11.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.11.0)
46
+ rspec-mocks (3.11.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.11.0)
49
+ rspec-support (3.11.1)
50
+ rubocop (1.78.0)
51
+ json (~> 2.3)
52
+ language_server-protocol (~> 3.17.0.2)
53
+ lint_roller (~> 1.1.0)
54
+ parallel (~> 1.10)
55
+ parser (>= 3.3.0.2)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ regexp_parser (>= 2.9.3, < 3.0)
58
+ rubocop-ast (>= 1.45.1, < 2.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 2.4.0, < 4.0)
61
+ rubocop-ast (1.45.1)
62
+ parser (>= 3.3.7.2)
63
+ prism (~> 1.4)
64
+ ruby-progressbar (1.13.0)
65
+ thor (1.3.2)
66
+ unicode-display_width (3.1.4)
67
+ unicode-emoji (~> 4.0, >= 4.0.4)
68
+ unicode-emoji (4.0.4)
69
+
70
+ PLATFORMS
71
+ ruby
72
+ x86_64-linux
73
+
74
+ DEPENDENCIES
75
+ appraisal
76
+ pry-byebug
77
+ rake (~> 13.0)
78
+ rspec (~> 3.11.0)
79
+ rspec-loop!
80
+ rubocop (~> 1.21)
81
+
82
+ BUNDLED WITH
83
+ 2.6.7
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "~> 3.12.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ rspec-loop (0.1.0)
5
+ rspec-core (~> 3.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.5.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.3)
15
+ byebug (12.0.0)
16
+ coderay (1.1.3)
17
+ diff-lcs (1.6.2)
18
+ json (2.12.2)
19
+ language_server-protocol (3.17.0.5)
20
+ lint_roller (1.1.0)
21
+ method_source (1.1.0)
22
+ parallel (1.27.0)
23
+ parser (3.3.8.0)
24
+ ast (~> 2.4.1)
25
+ racc
26
+ prism (1.4.0)
27
+ pry (0.15.2)
28
+ coderay (~> 1.1)
29
+ method_source (~> 1.0)
30
+ pry-byebug (3.11.0)
31
+ byebug (~> 12.0)
32
+ pry (>= 0.13, < 0.16)
33
+ racc (1.8.1)
34
+ rainbow (3.1.1)
35
+ rake (13.3.0)
36
+ regexp_parser (2.10.0)
37
+ rspec (3.12.0)
38
+ rspec-core (~> 3.12.0)
39
+ rspec-expectations (~> 3.12.0)
40
+ rspec-mocks (~> 3.12.0)
41
+ rspec-core (3.12.3)
42
+ rspec-support (~> 3.12.0)
43
+ rspec-expectations (3.12.4)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.12.0)
46
+ rspec-mocks (3.12.7)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.12.0)
49
+ rspec-support (3.12.2)
50
+ rubocop (1.78.0)
51
+ json (~> 2.3)
52
+ language_server-protocol (~> 3.17.0.2)
53
+ lint_roller (~> 1.1.0)
54
+ parallel (~> 1.10)
55
+ parser (>= 3.3.0.2)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ regexp_parser (>= 2.9.3, < 3.0)
58
+ rubocop-ast (>= 1.45.1, < 2.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 2.4.0, < 4.0)
61
+ rubocop-ast (1.45.1)
62
+ parser (>= 3.3.7.2)
63
+ prism (~> 1.4)
64
+ ruby-progressbar (1.13.0)
65
+ thor (1.3.2)
66
+ unicode-display_width (3.1.4)
67
+ unicode-emoji (~> 4.0, >= 4.0.4)
68
+ unicode-emoji (4.0.4)
69
+
70
+ PLATFORMS
71
+ ruby
72
+ x86_64-linux
73
+
74
+ DEPENDENCIES
75
+ appraisal
76
+ pry-byebug
77
+ rake (~> 13.0)
78
+ rspec (~> 3.12.0)
79
+ rspec-loop!
80
+ rubocop (~> 1.21)
81
+
82
+ BUNDLED WITH
83
+ 2.6.7
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "~> 3.13.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ rspec-loop (0.1.0)
5
+ rspec-core (~> 3.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.5.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.3)
15
+ byebug (12.0.0)
16
+ coderay (1.1.3)
17
+ diff-lcs (1.6.2)
18
+ json (2.12.2)
19
+ language_server-protocol (3.17.0.5)
20
+ lint_roller (1.1.0)
21
+ method_source (1.1.0)
22
+ parallel (1.27.0)
23
+ parser (3.3.8.0)
24
+ ast (~> 2.4.1)
25
+ racc
26
+ prism (1.4.0)
27
+ pry (0.15.2)
28
+ coderay (~> 1.1)
29
+ method_source (~> 1.0)
30
+ pry-byebug (3.11.0)
31
+ byebug (~> 12.0)
32
+ pry (>= 0.13, < 0.16)
33
+ racc (1.8.1)
34
+ rainbow (3.1.1)
35
+ rake (13.3.0)
36
+ regexp_parser (2.10.0)
37
+ rspec (3.13.1)
38
+ rspec-core (~> 3.13.0)
39
+ rspec-expectations (~> 3.13.0)
40
+ rspec-mocks (~> 3.13.0)
41
+ rspec-core (3.13.5)
42
+ rspec-support (~> 3.13.0)
43
+ rspec-expectations (3.13.5)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.13.0)
46
+ rspec-mocks (3.13.5)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.13.0)
49
+ rspec-support (3.13.4)
50
+ rubocop (1.78.0)
51
+ json (~> 2.3)
52
+ language_server-protocol (~> 3.17.0.2)
53
+ lint_roller (~> 1.1.0)
54
+ parallel (~> 1.10)
55
+ parser (>= 3.3.0.2)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ regexp_parser (>= 2.9.3, < 3.0)
58
+ rubocop-ast (>= 1.45.1, < 2.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 2.4.0, < 4.0)
61
+ rubocop-ast (1.45.1)
62
+ parser (>= 3.3.7.2)
63
+ prism (~> 1.4)
64
+ ruby-progressbar (1.13.0)
65
+ thor (1.3.2)
66
+ unicode-display_width (3.1.4)
67
+ unicode-emoji (~> 4.0, >= 4.0.4)
68
+ unicode-emoji (4.0.4)
69
+
70
+ PLATFORMS
71
+ ruby
72
+ x86_64-linux
73
+
74
+ DEPENDENCIES
75
+ appraisal
76
+ pry-byebug
77
+ rake (~> 13.0)
78
+ rspec (~> 3.13.0)
79
+ rspec-loop!
80
+ rubocop (~> 1.21)
81
+
82
+ BUNDLED WITH
83
+ 2.6.7
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpecExampleProcsyLoop
4
+ def loop_count
5
+ example.loop_count
6
+ end
7
+
8
+ def exception=(exception)
9
+ example.exception = exception
10
+ end
11
+ end
12
+
13
+ module RSpecExampleLoop
14
+ def exception=(exception)
15
+ @exception = exception
16
+ end
17
+
18
+ def loop_count
19
+ metadata[:loop] || RSpec.configuration.default_loop_count
20
+ end
21
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Support.require_rspec_core "formatters/base_text_formatter"
4
+ RSpec::Support.require_rspec_core "formatters/console_codes"
5
+
6
+ module RSpec
7
+ class Loop
8
+ class Formatter < RSpec::Core::Formatters::BaseTextFormatter
9
+ NOTIFICATIONS = %i[
10
+ example_group_started
11
+ example_started
12
+ example_iteration_finished
13
+ example_finished
14
+ example_group_finished
15
+ dump_pending
16
+ dump_failures
17
+ dump_summary
18
+ ].freeze
19
+
20
+ RESULT_CHAR_MAP = {
21
+ passed: ".",
22
+ pending: "*",
23
+ failed: "F",
24
+ }.freeze
25
+
26
+ RESULT_COLOR_CODE_MAP = {
27
+ passed: :success,
28
+ pending: :pending,
29
+ failed: :failure,
30
+ }.freeze
31
+
32
+ RSpec::Core::Formatters.register self, *NOTIFICATIONS
33
+
34
+ def initialize(output)
35
+ super
36
+ @group_level = 0
37
+
38
+ @example_running = false
39
+ @messages = []
40
+ @totals = Hash.new do |h, key|
41
+ h[key] = { failed: 0, passed: 0, pending: 0 }
42
+ end
43
+ end
44
+
45
+ def example_group_started(notification)
46
+ output.puts if @group_level.zero?
47
+ output.puts "#{current_indentation}#{notification.group.description.strip}"
48
+
49
+ @group_level += 1
50
+ end
51
+
52
+ def example_group_finished(_notification)
53
+ @group_level -= 1 if @group_level.positive?
54
+ end
55
+
56
+ def example_started(_notification)
57
+ @example_running = true
58
+ output.print "#{current_indentation}["
59
+ end
60
+
61
+ def example_iteration_finished(notification)
62
+ status = notification.example.metadata[:loop_last_result].status
63
+ return if notification.example.skipped?
64
+
65
+ @totals[notification.example.id][status] += 1
66
+
67
+ print_char_for_status(status)
68
+ end
69
+
70
+ def example_finished(notification)
71
+ @example_running = false
72
+
73
+ if notification.example.skipped?
74
+ notification.example.loop_count.times do
75
+ print_char_for_status(:pending)
76
+ end
77
+ end
78
+
79
+ code = RESULT_COLOR_CODE_MAP[notification.example.execution_result.status]
80
+ output.puts "] #{RSpec::Core::Formatters::ConsoleCodes.wrap(notification.example.description.strip, code)}"
81
+
82
+ flush_messages
83
+ end
84
+
85
+ def message(notification)
86
+ if @example_running
87
+ @messages << notification.message
88
+ else
89
+ output.puts "#{current_indentation}#{notification.message}"
90
+ end
91
+ end
92
+
93
+ def dump_pending(notification)
94
+ return if RSpec.configuration.pending_failure_output == :skip
95
+ return if notification.pending_notifications.empty?
96
+
97
+ formatted = "\nPending: (Failures listed here are expected and do not affect your suite's status)\n".dup
98
+ pending_examples = notification.pending_notifications.uniq(&:example)
99
+ pending_examples.each_with_index do |pending, index|
100
+ formatted << pending.fully_formatted(index.next)
101
+ end
102
+ puts formatted
103
+ end
104
+
105
+ def dump_failures(notification)
106
+ return if notification.failure_notifications.empty?
107
+
108
+ output.puts notification.fully_formatted_failed_examples
109
+ end
110
+
111
+ def dump_summary(summary)
112
+ output.puts summary.fully_formatted
113
+ end
114
+
115
+ private
116
+
117
+ def print_char_for_status(status)
118
+ result_char = RESULT_CHAR_MAP[status]
119
+ color_code = RESULT_COLOR_CODE_MAP[status]
120
+ output.print RSpec::Core::Formatters::ConsoleCodes.wrap(result_char, color_code)
121
+ end
122
+
123
+ def flush_messages
124
+ @messages.each do |message|
125
+ output.puts "#{current_indentation(1)}#{message}"
126
+ end
127
+
128
+ @messages.clear
129
+ end
130
+
131
+ def current_indentation(offset = 0)
132
+ " " * (@group_level + offset)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ class Loop
5
+ VERSION = "0.1.0"
6
+ end
7
+ end