feature_behavior 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: 6aa8f0a886160aaebf382daa8498747a286537e4e351ed9e054f9973da4cec7f
4
+ data.tar.gz: 5d19898954cf686a006210e23db32b8329bbee47e96d960934d19c3bc04f306f
5
+ SHA512:
6
+ metadata.gz: 749503ae935902174aef61195893e03991751680218e8379ef9973b6d744f3127d7331e0c0f50b91c94bbf2c8b942e5a23e53f2305c6902d3e3022a45a1d4beb
7
+ data.tar.gz: a1aa67d0adb2aeafb3b80a231165c4ed7533445030e95f94ed54a11a15f002aaff1604cee97ca2a0c782668604bef8121dd024fb731df7f6b4f9467cd61834e4
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-07-08
4
+
5
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Richard Jordan
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # FeatureBehavior
2
+
3
+ Add behaveior steps to RSpec feature examples.
4
+
5
+ ## Installation
6
+
7
+ Please do not do it earlier due to security reasons. Alternatively,
8
+ replace this section with instructions to install your gem from git
9
+ if you don't plan to release to RubyGems.org.
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add feature_behavior
14
+
15
+ If bundler is not being used to manage dependencies, install the gem
16
+ by executing:
17
+
18
+ $ gem install feature_behavior
19
+
20
+ ## Usage
21
+
22
+ TODO: Instructions to follow
23
+
24
+ ## Development
25
+
26
+ After checking out the repo, run `bin/setup` to install dependencies.
27
+ Then, run `rake spec` to run the tests. You can also run `bin/console`
28
+ for an interactive prompt that will allow you to experiment.
29
+
30
+ To install this gem onto your local machine,
31
+
32
+ run `bundle exec rake install`.
33
+
34
+ To release a new version, update the version number in `version.rb`,
35
+ and then run `bundle exec rake release`, which will create a git tag
36
+ for the version, push git commits and the created tag, and push the
37
+ `.gem` file to [rubygems.org](https://rubygems.org).
38
+
39
+ ## Contributing
40
+
41
+ Bug reports and pull requests are welcome on GitHub at
42
+ https://github.com/RichardJordan/feature_behavior.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
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
+ task default: :spec
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/feature_behavior/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "feature_behavior"
7
+ spec.version = FeatureBehavior::VERSION
8
+ spec.authors = ["Richard Jordan"]
9
+ spec.email = ["richarddjordan@gmail.com"]
10
+ spec.licenses = ["MIT"]
11
+
12
+ spec.summary = "Add behaviors to RSpec feature spec runs"
13
+ spec.description = "Break up example blocks into behavior steps."
14
+ spec.homepage = "https://github.com/RichardJordan/feature_behavior"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/RichardJordan/feature_behavior"
21
+ spec.metadata["changelog_uri"] = "https://github.com/RichardJordan/feature_behavior/blob/main/CHANGELOG"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) ||
28
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
29
+ end
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+ end
@@ -0,0 +1,11 @@
1
+ module FeatureBehavior
2
+ module DSL
3
+ def behavior(description, example = nil, &block)
4
+ Runner.run description, example, true, &block
5
+ end
6
+
7
+ def xbehavior(description, example = nil, &block)
8
+ Runner.run description, example, false, &block
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,191 @@
1
+ module FeatureBehavior
2
+ class Formatter < RSpec::Core::Formatters::DocumentationFormatter
3
+ RSpec::Core::Formatters.register self,
4
+ :example_failed,
5
+ :example_passed,
6
+ :example_started,
7
+ :behavior_terminated,
8
+ :behavior_passed,
9
+ :behavior_skipped,
10
+ :behavior_started,
11
+ :dump_pending,
12
+ :dump_summary
13
+
14
+
15
+ attr_reader :current_behavior,
16
+ :current_behavior_step,
17
+ :current_description,
18
+ :multi_step_example,
19
+ :skipped_behaviors,
20
+ :total_behaviors
21
+
22
+
23
+ def initialize(output)
24
+ super
25
+ @current_behavior = ""
26
+ @current_behavior_step = 0
27
+ @current_description = ""
28
+ @multi_step_example = false
29
+ @skipped_behaviors = []
30
+ @total_behaviors = 0
31
+ end
32
+
33
+ def behavior_passed(_notification)
34
+ msg = "#{step}#{current_behavior}"
35
+
36
+ if current_behavior_step == 1
37
+ output.puts "#{step_indent}#{"-" * msg.length}"
38
+ end
39
+
40
+ @last_msg = msg
41
+ output.puts colorized("#{step_indent}#{msg}", :success)
42
+ end
43
+
44
+ def behavior_skipped(notification)
45
+ @skipped_behaviors << notification
46
+ msg = "#{step}SKIPPED: #{current_behavior}"
47
+
48
+ if current_behavior_step == 1
49
+ output.puts "#{step_indent}#{"-" * msg.length}"
50
+ end
51
+
52
+ @last_msg = msg
53
+ output.puts colorized("#{step_indent}#{msg}", :blue)
54
+ end
55
+
56
+ def behavior_started(notification)
57
+ @total_behaviors += 1
58
+ @current_behavior = notification
59
+ @current_behavior_step += 1
60
+
61
+ if current_behavior_step == 1
62
+ @multi_step_example = true
63
+ msg = "#{current_description}, multiple steps:"
64
+ output.puts "#{current_indentation}#{msg}"
65
+ end
66
+ end
67
+
68
+ def behavior_terminated(_notification)
69
+ end
70
+
71
+ def dump_pending(notification)
72
+ super
73
+
74
+ if @skipped_behaviors.length > 0
75
+ msg = "\nBehaviors Pending: " \
76
+ "(Behaviors listed here have been intentionally skipped pending attention)"
77
+ @output << msg
78
+
79
+ results = []
80
+
81
+ @skipped_behaviors.each_with_index do |behavior, index|
82
+ results << skipped_behavior_dump_string(behavior, index)
83
+ end
84
+
85
+ @output << colorized("\n#{results.join("\n")}\n", :blue)
86
+ end
87
+ end
88
+
89
+ def dump_summary(notification)
90
+ behavior_summary if @total_behaviors > 0
91
+ super
92
+ end
93
+
94
+ def example_failed(_notification)
95
+ if multi_step_example
96
+ msg = "#{step}#{current_behavior} (Failed)"
97
+ output.puts colorized("#{step_indent}#{msg}", :failure)
98
+ output.puts "#{step_indent}#{"-" * msg.length}"
99
+ reset_behavior
100
+ end
101
+ super
102
+ end
103
+
104
+ def example_passed(_notification)
105
+ if multi_step_example
106
+ output.puts "#{step_indent}#{"-" * @last_msg.length}"
107
+ reset_behavior
108
+ end
109
+ super
110
+ end
111
+
112
+ def example_started(notification)
113
+ @current_behavior = ""
114
+ @current_behavior_step = 0
115
+ @current_description = notification.example.description
116
+ super
117
+ end
118
+
119
+
120
+ private
121
+
122
+ def behavior_summary
123
+ @output << "\n\nBehaviors:\n"
124
+
125
+ @output << colorized(
126
+ "\nTotal Behaviors Tested: #{total_behaviors}, Skipped: #{skipped_behaviors.length}\n",
127
+ behavior_summary_color,
128
+ )
129
+
130
+ @output << "\nExamples:\n"
131
+ end
132
+
133
+ def behavior_summary_color
134
+ skipped_behaviors.length > 0 ? :blue : :green
135
+ end
136
+
137
+ def colorized(text_string, color_scheme)
138
+ console_color_wrapper.wrap(text_string, color_scheme)
139
+ end
140
+
141
+ def console_color_wrapper
142
+ RSpec::Core::Formatters::ConsoleCodes
143
+ end
144
+
145
+ def reset_behavior
146
+ @current_behavior = nil
147
+ @multi_step_example = false
148
+ end
149
+
150
+ def skip_description_indent(idx)
151
+ " " * (skip_indent_length + 1 - Math.log10(idx + 1).to_i)
152
+ end
153
+
154
+ def skip_indent
155
+ " " * skip_indent_length
156
+ end
157
+
158
+ def skip_indent_length
159
+ Math.log10(skipped_behaviors.length).to_i
160
+ end
161
+
162
+ def skipped_behavior_dump_string(bh, idx)
163
+ colorized(
164
+ "\n #{idx + 1})#{skip_description_indent(idx)}#{bh[:example_group_description]}" \
165
+ "\n #{skip_indent}- Scenario: #{bh[:behavior_description_args].first}" \
166
+ "\n #{skip_indent}- Behavior: #{bh[:current_behavior]}",
167
+ :blue
168
+ ) + colorized(
169
+ "\n #{skip_indent}# Temporarily skipped with xbehavior" \
170
+ "\n #{skip_indent}# #{bh[:location]}",
171
+ :cyan
172
+ )
173
+ end
174
+
175
+ def step
176
+ "Step#{step_number_indent}#{current_behavior_step}. "
177
+ end
178
+
179
+ def step_indent
180
+ "#{current_indentation}#{' ' * step_indent_extra_space_count}"
181
+ end
182
+
183
+ def step_indent_extra_space_count
184
+ 2
185
+ end
186
+
187
+ def step_number_indent
188
+ current_behavior_step > 9 ? " " : " "
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,83 @@
1
+ module FeatureBehavior
2
+ class Runner
3
+
4
+ def self.run(description, example = nil, no_skip = true, &block)
5
+ new(description, example, no_skip, &block).run
6
+ end
7
+
8
+ attr_reader :description, :example, :example_block, :skipped
9
+
10
+ def initialize(description, example = nil, no_skip = true, &block)
11
+ @description = description
12
+ @example = example
13
+ @example_block = block
14
+ @skipped = !no_skip
15
+ end
16
+
17
+ def run
18
+ before_behavior
19
+ notify :behavior_started
20
+
21
+ if skipped
22
+ reporter.notify :behavior_skipped, skipped_behavior_args
23
+ else
24
+ run_example!
25
+ notify :behavior_passed
26
+ end
27
+
28
+ notify :behavior_terminated
29
+ after_behavior
30
+ end
31
+
32
+
33
+ private
34
+
35
+ def after_behavior
36
+ metadata[:description_args].pop
37
+ refresh_description
38
+ end
39
+
40
+ def before_behavior
41
+ metadata[:description_args].push(description)
42
+ refresh_description
43
+ end
44
+
45
+ def current_example
46
+ @current_example ||= example || RSpec.current_example
47
+ end
48
+
49
+ def metadata
50
+ current_example.metadata
51
+ end
52
+
53
+ def notify(event)
54
+ reporter.notify event, description
55
+ end
56
+
57
+ def refresh_description
58
+ metadata[:description] = metadata[:description_args].join(', ')
59
+
60
+ metadata[:full_description] = [
61
+ metadata[:example_group][:full_description],
62
+ ].concat(metadata[:description_args]).join(', ')
63
+ end
64
+
65
+ def reporter
66
+ current_example.reporter
67
+ end
68
+
69
+ def run_example!
70
+ example_block.call
71
+ end
72
+
73
+ def skipped_behavior_args
74
+ {
75
+ current_behavior: description,
76
+ example_group_description: metadata[:example_group][:full_description],
77
+ behavior_description_args: metadata[:description_args],
78
+ location: metadata[:location],
79
+ }
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FeatureBehavior
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "feature_behavior/version"
4
+
5
+ require 'feature_behavior/formatter'
6
+ require 'feature_behavior/runner'
7
+
8
+ module FeatureBehavior
9
+ class Error < StandardError; end
10
+
11
+ end
@@ -0,0 +1,4 @@
1
+ module FeatureBehavior
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feature_behavior
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Jordan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-07-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Break up example blocks into behavior steps.
14
+ email:
15
+ - richarddjordan@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rspec"
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - Rakefile
25
+ - feature_behavior.gemspec
26
+ - lib/feature_behavior.rb
27
+ - lib/feature_behavior/dsl.rb
28
+ - lib/feature_behavior/formatter.rb
29
+ - lib/feature_behavior/runner.rb
30
+ - lib/feature_behavior/version.rb
31
+ - sig/feature_behavior.rbs
32
+ homepage: https://github.com/RichardJordan/feature_behavior
33
+ licenses:
34
+ - MIT
35
+ metadata:
36
+ allowed_push_host: https://rubygems.org
37
+ homepage_uri: https://github.com/RichardJordan/feature_behavior
38
+ source_code_uri: https://github.com/RichardJordan/feature_behavior
39
+ changelog_uri: https://github.com/RichardJordan/feature_behavior/blob/main/CHANGELOG
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 3.0.0
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.3.26
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Add behaviors to RSpec feature spec runs
59
+ test_files: []