rspec-rfc-helper 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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +34 -0
- data/LICENSE +21 -0
- data/README.md +175 -0
- data/Rakefile +8 -0
- data/lib/rspec/rfc_helper/markdown_renderer.rb +178 -0
- data/lib/rspec/rfc_helper/spec.rb +87 -0
- data/lib/rspec/rfc_helper/specs.rb +138 -0
- data/lib/rspec/rfc_helper/version.rb +7 -0
- data/lib/rspec/rfc_helper.rb +34 -0
- data/report.md +204 -0
- data/sig/rspec/rfc_helper/markdown_renderer.rbs +27 -0
- data/sig/rspec/rfc_helper/spec.rbs +19 -0
- data/sig/rspec/rfc_helper/specs.rbs +25 -0
- data/sig/rspec/rfc_helper.rbs +12 -0
- data/specification_example.md +74 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 77bf0b42fa2165a6521a2929936cb1a1b58ab359cb19bbe19681ff7e2767d75f
|
4
|
+
data.tar.gz: 42e537f5b31324440b1cf98ac49a23ae1aea884cfe734e660943441ee88acafa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ca6a4cd4d10c244b4d2ab310d17a4e51f831c3c2e3bb97abd984996913f19d76621de9e93bde197dfc005616a73be1684351d4c1867f6cf958502d70f4317400
|
7
|
+
data.tar.gz: de7b0bc373efa3875b961354733c8ce5a9fc2405447d7d98d61c336fc7380bbdf8215eb8c7228c1b7a4076b2d77f80caa7c2811b6c4486695767deb4fdf48ca2
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
require:
|
3
|
+
- rubocop-performance
|
4
|
+
- rubocop-rake
|
5
|
+
- rubocop-rspec
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
# Do not lint generated files
|
9
|
+
Exclude:
|
10
|
+
- coverage/**/*
|
11
|
+
- vendor/**/*
|
12
|
+
TargetRubyVersion: 3.1.2
|
13
|
+
NewCops: enable
|
14
|
+
|
15
|
+
Layout/HashAlignment:
|
16
|
+
EnforcedColonStyle: table
|
17
|
+
EnforcedHashRocketStyle: table
|
18
|
+
|
19
|
+
Layout/LineLength:
|
20
|
+
Max: 130
|
21
|
+
|
22
|
+
Style/HashSyntax:
|
23
|
+
EnforcedShorthandSyntax: never
|
24
|
+
|
25
|
+
Style/SymbolArray:
|
26
|
+
EnforcedStyle: brackets
|
27
|
+
|
28
|
+
Style/TrailingCommaInArrayLiteral:
|
29
|
+
EnforcedStyleForMultiline: comma
|
30
|
+
|
31
|
+
Style/TrailingCommaInHashLiteral:
|
32
|
+
EnforcedStyleForMultiline: comma
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.2
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
<!--
|
9
|
+
Quick remainder of the possible sections:
|
10
|
+
-----------------------------------------
|
11
|
+
### Added
|
12
|
+
for new features.
|
13
|
+
### Changed
|
14
|
+
for changes in existing functionality.
|
15
|
+
### Deprecated
|
16
|
+
for soon-to-be removed features.
|
17
|
+
### Removed
|
18
|
+
for now removed features.
|
19
|
+
### Fixed
|
20
|
+
for any bug fixes.
|
21
|
+
### Security
|
22
|
+
in case of vulnerabilities.
|
23
|
+
### Maintenance
|
24
|
+
in case of rework, dependencies change
|
25
|
+
|
26
|
+
Please, keep them in this order when updating.
|
27
|
+
-->
|
28
|
+
|
29
|
+
## [Unreleased]
|
30
|
+
|
31
|
+
## [0.1.0] - 2023-11-07 - Initial release
|
32
|
+
|
33
|
+
Initial release
|
34
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 Experiments Labs / Experimentations
|
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,175 @@
|
|
1
|
+
# RSpec::RfcHelper
|
2
|
+
|
3
|
+
RSpec RFC Helper is a RSpec module to help tracking implementation of big specifications, when having only comments in
|
4
|
+
code or tests becomes too tedious to maintain and follow.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add the gem to your Gemfile:
|
9
|
+
|
10
|
+
```rb
|
11
|
+
gem 'rspec-rfc-helper'
|
12
|
+
```
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
1. Write the specs: read the RFC/specifications that needs to be implemented and extract everything that has to be implemented. This is tedious.
|
17
|
+
2. Use the gem in Rspec
|
18
|
+
3. Sprinkle your tests to reference the specs
|
19
|
+
4. Run the tests
|
20
|
+
5. Open the report
|
21
|
+
|
22
|
+
### Writing specs
|
23
|
+
|
24
|
+
#### ...in a YAML file
|
25
|
+
Specs in a YAML file can be loaded easily when integrated in RSpec; the format is simple:
|
26
|
+
|
27
|
+
```yaml
|
28
|
+
# Optional, the specification/RFC name
|
29
|
+
name:
|
30
|
+
# Optional, the URL to the RFC/specification
|
31
|
+
url:
|
32
|
+
# Here we are
|
33
|
+
specs:
|
34
|
+
# You should at least have one section in the file
|
35
|
+
#
|
36
|
+
# Required. Section/chapter... It's converted to a string when imported
|
37
|
+
- section: 1
|
38
|
+
# Required. Unique section identifier. IDs of the specs in this section will be prefixed by it. Converted to symbol
|
39
|
+
# so only the character class used for symbols should be used.
|
40
|
+
id:
|
41
|
+
# Optional: URL to the section
|
42
|
+
url:
|
43
|
+
specs:
|
44
|
+
# Optional. A spec without an ID will appear with an "unknown" status in the report. It allows you to fill the
|
45
|
+
# specs list without thinking right now how you will identify it.
|
46
|
+
# Spec IDS must be unique across all the specs, but are automatically prefixed with the section's ID, which allows
|
47
|
+
# definition of two specs with the same ID over different sections.
|
48
|
+
- id:
|
49
|
+
# Required. Text must be unique across specs. Imperative verbs have to be bracketed, so you can use the same paragraph
|
50
|
+
# in multiple specs, targeting something different without losing context.
|
51
|
+
text: It [[MUST]] work
|
52
|
+
```
|
53
|
+
|
54
|
+
For more example, there is a fixture in `spec/fixtures/rfc.yaml`, and the example spec file in `spec/rfc.yaml`.
|
55
|
+
|
56
|
+
#### ...programmatically
|
57
|
+
|
58
|
+
Declaring specs programmatically can be interesting if you manage somehow to process the original specification
|
59
|
+
automatically.
|
60
|
+
|
61
|
+
```rb
|
62
|
+
# Create an instance of the Specs class: it holds the specs. "name" and "url" are optional
|
63
|
+
specs = RSpec::RfcHelper::Specs.new name: 'The easy thing RFC', url: 'https://somewhere'
|
64
|
+
|
65
|
+
# Create a section
|
66
|
+
spec.add_section number: '1.1', title: 'Implementation', id: :implementation
|
67
|
+
|
68
|
+
# Add a spec:
|
69
|
+
# ID is optional. A spec without an ID will appear with an "unknown" status in the report. It allows you to fill the
|
70
|
+
# specs list without thinking right now how you will identify it.
|
71
|
+
# Spec IDS must be unique across all the specs, but are automatically prefixed with the section's ID, which allows
|
72
|
+
# definition of two specs with the same ID over different sections.
|
73
|
+
#
|
74
|
+
# Here, spec ID will be :implementation__do_something
|
75
|
+
spec.add section: '1.1', text: 'It [[MUST]] do something', id: :do_something
|
76
|
+
|
77
|
+
# For long texts, use heredocs:
|
78
|
+
spec.add section: '1.1', text: <<~TXT, id: :do_something
|
79
|
+
Some long text, but the context is important to understand
|
80
|
+
what you [[MUST]] implement, how you SHOULD do it
|
81
|
+
and what you MAY do if you feel like it
|
82
|
+
TXT
|
83
|
+
```
|
84
|
+
|
85
|
+
### Usage in RSpec
|
86
|
+
|
87
|
+
#### ...with the spec file: use the module
|
88
|
+
|
89
|
+
```rb
|
90
|
+
# spec_helper.rb
|
91
|
+
|
92
|
+
require 'rspec-rfc-helper'
|
93
|
+
|
94
|
+
RSpec.configure do |config|
|
95
|
+
config.before(:suite) do
|
96
|
+
RSpec::RfcHelper.start_from_file 'path/to/your/spec.yaml'
|
97
|
+
end
|
98
|
+
|
99
|
+
config.after do |example|
|
100
|
+
RSpec::RfcHelper.add_example example
|
101
|
+
end
|
102
|
+
|
103
|
+
config.after( :suite) do
|
104
|
+
RSpec::RfcHelper.save_markdown_report 'path/to/report.md'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
#### ...programmatically: use the classes
|
110
|
+
|
111
|
+
```rb
|
112
|
+
# spec_helper.rb
|
113
|
+
|
114
|
+
require 'rspec-rfc-helper'
|
115
|
+
|
116
|
+
RSpec.configure do |config|
|
117
|
+
# Putting all the specs in the spec_helper.rb is not a good idea, but as you go programmatically, i'll let you find
|
118
|
+
# a way to organize yourself better.
|
119
|
+
# The main point here is to use the same instance for the definitions and the usages in the RSpec hooks
|
120
|
+
rfc_helper = RSpec::RfcHelper::Spec.new name: 'Plumbus management', url: 'https://somewhere'
|
121
|
+
rfc_helper.add_section #...
|
122
|
+
rfc_helper.add #...
|
123
|
+
# Alternatively, you can still load a file
|
124
|
+
rfc_helper = RSpec::RfcHelper::Spec.new_from_file 'path_to_file'
|
125
|
+
|
126
|
+
config.after do |example|
|
127
|
+
rfc_helper.add_example example
|
128
|
+
end
|
129
|
+
|
130
|
+
config.after( :suite) do
|
131
|
+
rfc_helper.save_markdown_report 'path/to/report.md'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
### Reference the specs in your suite
|
137
|
+
|
138
|
+
RFC Helper uses the RSpec tagging system to track and assign examples to _specs_.
|
139
|
+
|
140
|
+
```rb
|
141
|
+
RSpec.describe 'Something' do
|
142
|
+
# references spec "spec_id" in section "section1"
|
143
|
+
it 'works', rfc: :section1__spec_id do
|
144
|
+
# ...
|
145
|
+
end
|
146
|
+
|
147
|
+
# references spec "spec_id" in section "section1", and "other_spec_id" in "other_section"
|
148
|
+
it 'works', rfc: [:section1__spec_id, :other_section__other_spec_id] do
|
149
|
+
# ...
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
## Development
|
155
|
+
|
156
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests.
|
157
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
158
|
+
|
159
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
|
160
|
+
version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
|
161
|
+
push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
162
|
+
|
163
|
+
### Tools
|
164
|
+
|
165
|
+
Well, we use RSpec for testing.
|
166
|
+
|
167
|
+
Also, we use Rubocop for code style.
|
168
|
+
|
169
|
+
## Contributing
|
170
|
+
|
171
|
+
All contributions, ideas and discussions are welcome. Feel free to open issues and feature requests on the
|
172
|
+
[bug tracker](https://gitlab.com/experimentslabs/rspec-rfc-helper/-/issues).
|
173
|
+
|
174
|
+
You also can join the ExperimentsLabs [Matrix chatroom](https://matrix.to/#/!qpmxpfSIevwoRUgrxm:matrix.org?via=matrix.org)
|
175
|
+
to discuss of the project.
|
data/Rakefile
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module RfcHelper
|
5
|
+
##
|
6
|
+
# Markdown renderer for specs
|
7
|
+
class MarkdownRenderer
|
8
|
+
STATUS_COLORS = {
|
9
|
+
# Status from Spec class
|
10
|
+
failing: 'red',
|
11
|
+
pass: 'green',
|
12
|
+
has_pending: 'orange',
|
13
|
+
unknown: 'gray',
|
14
|
+
|
15
|
+
# Statuses from RSpec::Core::Example
|
16
|
+
'failed' => 'red',
|
17
|
+
'pending' => 'orange',
|
18
|
+
'passed' => 'green',
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
##
|
22
|
+
# @param specs [RSpec::RfcHelper::Specs]
|
23
|
+
def initialize(specs)
|
24
|
+
@specs = specs
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Generates a report in Markdown format.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
def render
|
32
|
+
main_table_lines = []
|
33
|
+
detailed_spec_report_blocks = []
|
34
|
+
|
35
|
+
@specs.each do |spec|
|
36
|
+
main_table_lines << main_table_row(spec)
|
37
|
+
detailed_spec_report_blocks << detailed_spec_report_block(spec)
|
38
|
+
end
|
39
|
+
|
40
|
+
<<~MD.chomp
|
41
|
+
#{header}
|
42
|
+
|
43
|
+
#{main_table(main_table_lines)}
|
44
|
+
|
45
|
+
#{detailed_spec_report_blocks.join("\n")}
|
46
|
+
MD
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Generates the report header
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
def header
|
54
|
+
title = 'Compliance status'
|
55
|
+
title = "#{title}: #{@specs.name}" if @specs.name
|
56
|
+
url = @specs.specs_url ? "[#{@specs.specs_url}](#{@specs.specs_url})" : '~'
|
57
|
+
|
58
|
+
<<~MD.chomp
|
59
|
+
# #{title}
|
60
|
+
|
61
|
+
Report generated on #{Time.now}
|
62
|
+
|
63
|
+
Source specification: #{url}
|
64
|
+
MD
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Generates a colored HTML string to represent the spec status
|
69
|
+
#
|
70
|
+
# @param value [String, Symbol] Status
|
71
|
+
# @param string [String?] Custom text replacement
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
def status(value, string = nil)
|
75
|
+
"<span style='color: #{STATUS_COLORS[value]}'>#{string || value}</span>"
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Makes some replacement in a text
|
80
|
+
#
|
81
|
+
# @param string [String]
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
def paragraph(string)
|
85
|
+
string.gsub("\n", ' <br> ')
|
86
|
+
.gsub(/\[\[([^\]]*)\]\]/, '<mark>\1</mark>')
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Generates a spec report block (header and examples table)
|
91
|
+
#
|
92
|
+
# @param spec [RSpec::RfcHelper::Spec]
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
def detailed_spec_report_block(spec)
|
96
|
+
<<~MD.chomp
|
97
|
+
#{detailed_spec_report_header(spec)}
|
98
|
+
|
99
|
+
#{detailed_spec_report_examples(spec)}
|
100
|
+
MD
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Generates header for a spec report
|
105
|
+
#
|
106
|
+
# @param spec [RSpec::RfcHelper::Spec]
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
def detailed_spec_report_header(spec) # rubocop:disable Metrics/AbcSize
|
110
|
+
section = "#{spec.section} - #{@specs.sections[spec.section][:title]}"
|
111
|
+
spec_link = if @specs.sections[spec.section][:url]
|
112
|
+
"[#{section}](#{@specs.sections[spec.section][:url]})"
|
113
|
+
else
|
114
|
+
section
|
115
|
+
end
|
116
|
+
<<~MD.chomp
|
117
|
+
## #{status(spec.status, '○')} `#{spec.verb.upcase}` #{spec.id}
|
118
|
+
|
119
|
+
**Specification:** #{spec_link}
|
120
|
+
|
121
|
+
> #{paragraph(spec.text)}
|
122
|
+
MD
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Generates the example table of a spec report
|
127
|
+
#
|
128
|
+
# @param spec [RSpec::RfcHelper::Spec]
|
129
|
+
#
|
130
|
+
# @return [String]
|
131
|
+
def detailed_spec_report_examples(spec) # rubocop:disable Metrics/MethodLength
|
132
|
+
lines = []
|
133
|
+
spec.examples.each do |example|
|
134
|
+
meta = example.metadata
|
135
|
+
lines << "| #{status(meta[:last_run_status])} | `#{meta[:type]}`: `#{meta[:location]}` |"
|
136
|
+
end
|
137
|
+
|
138
|
+
return "**No related examples**\n" if lines.empty?
|
139
|
+
|
140
|
+
<<~MD.chomp
|
141
|
+
**Examples:**
|
142
|
+
|
143
|
+
| Status | Reference |
|
144
|
+
|:------:|-----------|
|
145
|
+
#{lines.join("\n")}
|
146
|
+
|
147
|
+
MD
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Generates a row for the main table
|
152
|
+
#
|
153
|
+
# @param spec [RSpec::RfcHelper::Spec]
|
154
|
+
#
|
155
|
+
# @return [String]
|
156
|
+
def main_table_row(spec)
|
157
|
+
id = spec.id ? "`#{spec.id}`" : ''
|
158
|
+
section = @specs.sections[spec.section]
|
159
|
+
section_link = section[:url] ? "[#{spec.section}](#{section[:url]})" : spec.section
|
160
|
+
"| #{status(spec.status)} | #{id} | #{section_link} | #{spec.verb} | #{paragraph(spec.text)} |"
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Generates the main table
|
165
|
+
#
|
166
|
+
# @param lines [Array<String>] Table rows
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
def main_table(lines)
|
170
|
+
<<~MD.chomp
|
171
|
+
| status | id | section | verb | text |
|
172
|
+
|-------:|---:|---------|------|------|
|
173
|
+
#{lines.join("\n")}
|
174
|
+
MD
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module RfcHelper
|
5
|
+
##
|
6
|
+
# Represents one spec
|
7
|
+
class Spec
|
8
|
+
attr_reader :id, :section, :verb, :text, :examples
|
9
|
+
|
10
|
+
##
|
11
|
+
# Initializes a new Spec
|
12
|
+
#
|
13
|
+
# @param section [String] Section number
|
14
|
+
# @param text [String] Paragraph from the RFC.
|
15
|
+
# @param id [Symbol, NilClass] Unique identifier
|
16
|
+
#
|
17
|
+
# +text+ should have one _verb_ surrounded by brackets, representing what this Spec expects:
|
18
|
+
#
|
19
|
+
# bad: It [[MUST]] work with a missing user but [[MAY]] log a warning
|
20
|
+
#
|
21
|
+
# Use two Specs instead:
|
22
|
+
# It [[MUST]] work with a missing user but MAY log a warning
|
23
|
+
# It MUST work with a missing user but [[MAY]] log a warning
|
24
|
+
#
|
25
|
+
# When nothing is highlighted, spec will appear as a "note".
|
26
|
+
def initialize(section:, text:, id: nil)
|
27
|
+
raise "Missing text ({section: #{section}, text: #{text}, id: #{id})" unless text.is_a?(String) && !text.empty?
|
28
|
+
raise "Missing section ({section: #{section}, text: #{text}, id: #{id})" unless section.is_a?(String) && !section.empty?
|
29
|
+
|
30
|
+
@id = id
|
31
|
+
@section = section.to_s
|
32
|
+
@text = text
|
33
|
+
@examples = []
|
34
|
+
set_verb
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Determines status from RSpec examples
|
39
|
+
#
|
40
|
+
# @return [:unknown|:pass|:has_pending|:failing] Spec state from all examples
|
41
|
+
#
|
42
|
+
# Statuses:
|
43
|
+
# unknown: No test currently covers this spec
|
44
|
+
# failing: At least one example failed
|
45
|
+
# has_pending: Some examples are pending, other passed
|
46
|
+
# pass: All examples passed
|
47
|
+
def status
|
48
|
+
value = :unknown
|
49
|
+
@examples.each do |example|
|
50
|
+
return :failing if example.metadata[:last_run_status] == 'failed'
|
51
|
+
return :has_pending if example.metadata[:last_run_status] == 'pending'
|
52
|
+
|
53
|
+
value = :pass
|
54
|
+
end
|
55
|
+
|
56
|
+
value
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Adds an RSpec example
|
61
|
+
#
|
62
|
+
# @param example [RSpec::Core::Example] RSpec example
|
63
|
+
def add_example(example)
|
64
|
+
@examples << example
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
##
|
70
|
+
# Determines the verb of interest in the given text; falls back to +:note+
|
71
|
+
#
|
72
|
+
# The key words MAY, MUST, MUST NOT, SHOULD, and SHOULD NOT are to be interpreted as described in RFC2119:
|
73
|
+
# @see https://www.w3.org/TR/activitypub/#bib-RFC2119
|
74
|
+
def set_verb
|
75
|
+
occurrences = @text.scan(/\[\[((?:MUST|SHOULD)(?: NOT)?|MAY)\]\]/)
|
76
|
+
case occurrences.size
|
77
|
+
when 0
|
78
|
+
@verb = :note
|
79
|
+
when 1
|
80
|
+
@verb = occurrences.first.first.downcase.tr(' ', '_').to_sym
|
81
|
+
else
|
82
|
+
raise 'Too much verbs in paragraph' if occurrences.size > 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require_relative 'spec'
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module RfcHelper
|
9
|
+
##
|
10
|
+
# Spec collection
|
11
|
+
class Specs
|
12
|
+
attr_reader :name, :specs_url, :sections
|
13
|
+
|
14
|
+
##
|
15
|
+
# Instantiates a Spec collection from a YAML file.
|
16
|
+
#
|
17
|
+
# Note: String keys are expected.
|
18
|
+
#
|
19
|
+
# @param file [String] Path to the specs file
|
20
|
+
#
|
21
|
+
# @return [RSpec::RfcHelper::Specs]
|
22
|
+
def self.new_from_file(file) # rubocop:disable Metrics/AbcSize
|
23
|
+
specs = YAML.load_file(file)
|
24
|
+
|
25
|
+
instance = new name: specs['name'], specs_url: specs['url']
|
26
|
+
|
27
|
+
specs['specs'].each do |section|
|
28
|
+
section_number = section['section'].to_s
|
29
|
+
|
30
|
+
instance.add_section id: section['id'].to_sym, number: section_number, title: section['title'], url: section['url']
|
31
|
+
|
32
|
+
section['specs'].each do |spec|
|
33
|
+
instance.add section: section_number, text: spec['text'], id: spec['id']&.to_sym
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
instance
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Instantiate a new list of specs
|
42
|
+
#
|
43
|
+
# @param name [String?] Specification name
|
44
|
+
# @param specs_url [String?] URL to the full specification
|
45
|
+
def initialize(name: nil, specs_url: nil)
|
46
|
+
@name = name
|
47
|
+
@specs_url = specs_url
|
48
|
+
@sections = {}
|
49
|
+
@specs = []
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Adds a spec section
|
54
|
+
#
|
55
|
+
# @param number [String] Section number
|
56
|
+
# @param title [String] Section title
|
57
|
+
# @param id [Symbol] Unique identifier
|
58
|
+
# @param url [String?] URL to this section
|
59
|
+
def add_section(number:, title:, id:, url: nil)
|
60
|
+
if @sections.key?(number) || @sections.find { |_number, section| section[:id] == id }
|
61
|
+
raise "Section number #{number} already exists"
|
62
|
+
end
|
63
|
+
|
64
|
+
@sections[number] = { title: title, id: id, url: url }
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Finds an example by id
|
69
|
+
#
|
70
|
+
# @param id [Symbol] Spec identifier
|
71
|
+
#
|
72
|
+
# @return [RSpec::RfcHelper::Spec]
|
73
|
+
def find(id)
|
74
|
+
@specs.find { |spec| spec.id == id } || raise("Spec not found with id #{id}")
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Adds a spec to the list
|
79
|
+
#
|
80
|
+
# It will prefix the spec ID with relevant section ID
|
81
|
+
#
|
82
|
+
# @param section [String] Section number
|
83
|
+
# @param text [String] Content
|
84
|
+
# @param id [Symbol?] Unique section identifier
|
85
|
+
def add(section:, text:, id: nil)
|
86
|
+
if id.is_a? Symbol
|
87
|
+
id = "#{section_id(section)}__#{id}".to_sym
|
88
|
+
raise "Duplicated id #{id}" if ids.include? id
|
89
|
+
end
|
90
|
+
|
91
|
+
@specs << Spec.new(section: section, text: text, id: id)
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Adds an RSpec example to all the relevant specs
|
96
|
+
#
|
97
|
+
# @param example [RSpec::Core::Example]
|
98
|
+
def add_example(example)
|
99
|
+
ids = example.metadata[:rfc]
|
100
|
+
return if ids.nil? || ids.empty?
|
101
|
+
|
102
|
+
ids = [ids] if ids.is_a? Symbol
|
103
|
+
ids.each do |id|
|
104
|
+
find(id).add_example example
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def ids
|
109
|
+
@specs.map(&:id)
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Delegation
|
114
|
+
def each(&)
|
115
|
+
@specs.each(&)
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Saves the report in Markdown
|
120
|
+
#
|
121
|
+
# @param file [String] Output file path
|
122
|
+
def save_markdown_report(file)
|
123
|
+
File.write file, RSpec::RfcHelper::MarkdownRenderer.new(self).render
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
##
|
129
|
+
# @param number [String] Section number
|
130
|
+
#
|
131
|
+
# @return [Symbol] Section ID from its number
|
132
|
+
def section_id(number)
|
133
|
+
section = @sections[number] || raise("Section not found with number #{number}")
|
134
|
+
section[:id]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'rfc_helper/version'
|
4
|
+
require_relative 'rfc_helper/specs'
|
5
|
+
|
6
|
+
module RSpec
|
7
|
+
##
|
8
|
+
# RSpec module to track specifications with more context
|
9
|
+
module RfcHelper
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Delegation when using the module as a starting point
|
14
|
+
def self.add_example(example)
|
15
|
+
raise 'Did you start?' unless @specs
|
16
|
+
|
17
|
+
@specs.add_example(example)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Delegation when using the module as a starting point
|
22
|
+
def self.start_from_file(file)
|
23
|
+
raise 'Already started' if @specs
|
24
|
+
|
25
|
+
@specs = Specs.new_from_file file
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.save_markdown_report(file)
|
29
|
+
raise 'Did you start?' unless @specs
|
30
|
+
|
31
|
+
@specs.save_markdown_report file
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/report.md
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
# Compliance status: RSpec RFC Helper
|
2
|
+
|
3
|
+
Report generated on 2023-11-07 22:11:05 +0100
|
4
|
+
|
5
|
+
Source specification: [https://gitlab.com/experimentslabs/rspec-rfc-helper/-/blob/main/specification_example.md](https://gitlab.com/experimentslabs/rspec-rfc-helper/-/blob/main/specification_example.md)
|
6
|
+
|
7
|
+
| status | id | section | verb | text |
|
8
|
+
|-------:|---:|---------|------|------|
|
9
|
+
| <span style='color: green'>pass</span> | `features__have_version` | 2.1 | must | The RFC Helper <mark>MUST</mark> have a version number |
|
10
|
+
| <span style='color: green'>pass</span> | `features__can_define_spec` | 2.1 | must | The RFC Helper <mark>MUST</mark> have methods to define specs that needs to be enforced and tracked. |
|
11
|
+
| <span style='color: gray'>unknown</span> | `features__wrap_verbs` | 2.1 | must | To tackle this issue, the text used in a spec defined for the RFC Helper <mark>MUST</mark> wrap the right verb in the relevant <br> paragraph into double square brackets, like, for example `<mark>VERB</mark>`. The same paragraph MAY be used in multiple specs, <br> with everytime a different verb wrapper. <br> |
|
12
|
+
| <span style='color: gray'>unknown</span> | `features__use_same_paragraph_twice` | 2.1 | may | To tackle this issue, the text used in a spec defined for the RFC Helper MUST wrap the right verb in the relevant <br> paragraph into double square brackets, like, for example `<mark>VERB</mark>`. The same paragraph <mark>MAY</mark> be used in multiple specs, <br> with everytime a different verb wrapper. <br> |
|
13
|
+
| <span style='color: green'>pass</span> | `features__identify_specs_with_id` | 2.1 | must | Defined specs also need to be identifiable, so a unique ID <mark>MUST</mark> be assigned to them. |
|
14
|
+
| <span style='color: gray'>unknown</span> | `features__no_verb_means_note` | 2.1 | note | When a portion of text is interesting but has no imperative verb in it, it still can be added, and the RFC Helper will <br> give it a status of "note". <br> |
|
15
|
+
| <span style='color: green'>pass</span> | `reporting__rfc_tag` | 2.2 | must | Once the RFC helper is configured in a RSpec suite, it <mark>MUST</mark> handle examples tagged with the `rfc` tag. |
|
16
|
+
| <span style='color: green'>pass</span> | `reporting__rfc_with_single_id_or_list` | 2.2 | must | The RFC helper <mark>MUST</mark> handle a single ID, as well as an array of IDs. |
|
17
|
+
| <span style='color: green'>pass</span> | `reporting__export_method` | 2.2 | must | The RFC Helper <mark>MUST</mark> have at least one method to export the generated report in a file. |
|
18
|
+
| <span style='color: gray'>unknown</span> | `reporting__export_date` | 2.2 | must | The report <mark>MUST</mark> contain the generation date. |
|
19
|
+
| <span style='color: gray'>unknown</span> | `usage__two_ways` | 3 | should | To have some flexibility, the helper <mark>SHOULD</mark> be useable in two ways: using the module `RSpec::RfcHelper`, or the classes. <br> |
|
20
|
+
| <span style='color: gray'>unknown</span> | `usage_module__start_method` | 3.1 | must | The `RSpec::RfcHelper` <mark>MUST</mark> have a method to "start" the helper |
|
21
|
+
| <span style='color: gray'>unknown</span> | `usage_module__handle_examples` | 3.1 | must | It <mark>MUST</mark> have a method to add examples to specs once they are evaluated |
|
22
|
+
| <span style='color: gray'>unknown</span> | `usage_module__export_method` | 3.1 | must | it <mark>MUST</mark> have a method to save the report |
|
23
|
+
| <span style='color: gray'>unknown</span> | | 3.2 | note | The RFC Helper SHOULD be used directly by instantiating a class. |
|
24
|
+
| <span style='color: gray'>unknown</span> | | 3.2 | note | The instance MUST provide methods to add sections |
|
25
|
+
| <span style='color: gray'>unknown</span> | | 3.2 | note | The instance MUST provide methods to define the specs |
|
26
|
+
| <span style='color: gray'>unknown</span> | | 3.2 | note | The instance MUST provide methods to generate the report |
|
27
|
+
| <span style='color: gray'>unknown</span> | | 3.2 | note | The instance MAY provide methods to load sections and specs from a file |
|
28
|
+
|
29
|
+
## <span style='color: green'>○</span> `MUST` features__have_version
|
30
|
+
|
31
|
+
**Specification:** 2.1 - Features
|
32
|
+
|
33
|
+
> The RFC Helper <mark>MUST</mark> have a version number
|
34
|
+
|
35
|
+
**Examples:**
|
36
|
+
|
37
|
+
| Status | Reference |
|
38
|
+
|:------:|-----------|
|
39
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper_spec.rb:4` |
|
40
|
+
|
41
|
+
## <span style='color: green'>○</span> `MUST` features__can_define_spec
|
42
|
+
|
43
|
+
**Specification:** 2.1 - Features
|
44
|
+
|
45
|
+
> The RFC Helper <mark>MUST</mark> have methods to define specs that needs to be enforced and tracked.
|
46
|
+
|
47
|
+
**Examples:**
|
48
|
+
|
49
|
+
| Status | Reference |
|
50
|
+
|:------:|-----------|
|
51
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/spec_spec.rb:10` |
|
52
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/specs_spec.rb:64` |
|
53
|
+
|
54
|
+
## <span style='color: gray'>○</span> `MUST` features__wrap_verbs
|
55
|
+
|
56
|
+
**Specification:** 2.1 - Features
|
57
|
+
|
58
|
+
> To tackle this issue, the text used in a spec defined for the RFC Helper <mark>MUST</mark> wrap the right verb in the relevant <br> paragraph into double square brackets, like, for example `<mark>VERB</mark>`. The same paragraph MAY be used in multiple specs, <br> with everytime a different verb wrapper. <br>
|
59
|
+
|
60
|
+
**No related examples**
|
61
|
+
|
62
|
+
## <span style='color: gray'>○</span> `MAY` features__use_same_paragraph_twice
|
63
|
+
|
64
|
+
**Specification:** 2.1 - Features
|
65
|
+
|
66
|
+
> To tackle this issue, the text used in a spec defined for the RFC Helper MUST wrap the right verb in the relevant <br> paragraph into double square brackets, like, for example `<mark>VERB</mark>`. The same paragraph <mark>MAY</mark> be used in multiple specs, <br> with everytime a different verb wrapper. <br>
|
67
|
+
|
68
|
+
**No related examples**
|
69
|
+
|
70
|
+
## <span style='color: green'>○</span> `MUST` features__identify_specs_with_id
|
71
|
+
|
72
|
+
**Specification:** 2.1 - Features
|
73
|
+
|
74
|
+
> Defined specs also need to be identifiable, so a unique ID <mark>MUST</mark> be assigned to them.
|
75
|
+
|
76
|
+
**Examples:**
|
77
|
+
|
78
|
+
| Status | Reference |
|
79
|
+
|:------:|-----------|
|
80
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/specs_spec.rb:75` |
|
81
|
+
|
82
|
+
## <span style='color: gray'>○</span> `NOTE` features__no_verb_means_note
|
83
|
+
|
84
|
+
**Specification:** 2.1 - Features
|
85
|
+
|
86
|
+
> When a portion of text is interesting but has no imperative verb in it, it still can be added, and the RFC Helper will <br> give it a status of "note". <br>
|
87
|
+
|
88
|
+
**No related examples**
|
89
|
+
|
90
|
+
## <span style='color: green'>○</span> `MUST` reporting__rfc_tag
|
91
|
+
|
92
|
+
**Specification:** 2.2 - Reporting
|
93
|
+
|
94
|
+
> Once the RFC helper is configured in a RSpec suite, it <mark>MUST</mark> handle examples tagged with the `rfc` tag.
|
95
|
+
|
96
|
+
**Examples:**
|
97
|
+
|
98
|
+
| Status | Reference |
|
99
|
+
|:------:|-----------|
|
100
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/specs_spec.rb:91` |
|
101
|
+
|
102
|
+
## <span style='color: green'>○</span> `MUST` reporting__rfc_with_single_id_or_list
|
103
|
+
|
104
|
+
**Specification:** 2.2 - Reporting
|
105
|
+
|
106
|
+
> The RFC helper <mark>MUST</mark> handle a single ID, as well as an array of IDs.
|
107
|
+
|
108
|
+
**Examples:**
|
109
|
+
|
110
|
+
| Status | Reference |
|
111
|
+
|:------:|-----------|
|
112
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/specs_spec.rb:91` |
|
113
|
+
|
114
|
+
## <span style='color: green'>○</span> `MUST` reporting__export_method
|
115
|
+
|
116
|
+
**Specification:** 2.2 - Reporting
|
117
|
+
|
118
|
+
> The RFC Helper <mark>MUST</mark> have at least one method to export the generated report in a file.
|
119
|
+
|
120
|
+
**Examples:**
|
121
|
+
|
122
|
+
| Status | Reference |
|
123
|
+
|:------:|-----------|
|
124
|
+
| <span style='color: green'>passed</span> | ``: `./spec/rspec/rfc_helper/specs_spec.rb:103` |
|
125
|
+
|
126
|
+
## <span style='color: gray'>○</span> `MUST` reporting__export_date
|
127
|
+
|
128
|
+
**Specification:** 2.2 - Reporting
|
129
|
+
|
130
|
+
> The report <mark>MUST</mark> contain the generation date.
|
131
|
+
|
132
|
+
**No related examples**
|
133
|
+
|
134
|
+
## <span style='color: gray'>○</span> `SHOULD` usage__two_ways
|
135
|
+
|
136
|
+
**Specification:** 3 - Usage
|
137
|
+
|
138
|
+
> To have some flexibility, the helper <mark>SHOULD</mark> be useable in two ways: using the module `RSpec::RfcHelper`, or the classes. <br>
|
139
|
+
|
140
|
+
**No related examples**
|
141
|
+
|
142
|
+
## <span style='color: gray'>○</span> `MUST` usage_module__start_method
|
143
|
+
|
144
|
+
**Specification:** 3.1 - Using the module
|
145
|
+
|
146
|
+
> The `RSpec::RfcHelper` <mark>MUST</mark> have a method to "start" the helper
|
147
|
+
|
148
|
+
**No related examples**
|
149
|
+
|
150
|
+
## <span style='color: gray'>○</span> `MUST` usage_module__handle_examples
|
151
|
+
|
152
|
+
**Specification:** 3.1 - Using the module
|
153
|
+
|
154
|
+
> It <mark>MUST</mark> have a method to add examples to specs once they are evaluated
|
155
|
+
|
156
|
+
**No related examples**
|
157
|
+
|
158
|
+
## <span style='color: gray'>○</span> `MUST` usage_module__export_method
|
159
|
+
|
160
|
+
**Specification:** 3.1 - Using the module
|
161
|
+
|
162
|
+
> it <mark>MUST</mark> have a method to save the report
|
163
|
+
|
164
|
+
**No related examples**
|
165
|
+
|
166
|
+
## <span style='color: gray'>○</span> `NOTE`
|
167
|
+
|
168
|
+
**Specification:** 3.2 - Using the classes
|
169
|
+
|
170
|
+
> The RFC Helper SHOULD be used directly by instantiating a class.
|
171
|
+
|
172
|
+
**No related examples**
|
173
|
+
|
174
|
+
## <span style='color: gray'>○</span> `NOTE`
|
175
|
+
|
176
|
+
**Specification:** 3.2 - Using the classes
|
177
|
+
|
178
|
+
> The instance MUST provide methods to add sections
|
179
|
+
|
180
|
+
**No related examples**
|
181
|
+
|
182
|
+
## <span style='color: gray'>○</span> `NOTE`
|
183
|
+
|
184
|
+
**Specification:** 3.2 - Using the classes
|
185
|
+
|
186
|
+
> The instance MUST provide methods to define the specs
|
187
|
+
|
188
|
+
**No related examples**
|
189
|
+
|
190
|
+
## <span style='color: gray'>○</span> `NOTE`
|
191
|
+
|
192
|
+
**Specification:** 3.2 - Using the classes
|
193
|
+
|
194
|
+
> The instance MUST provide methods to generate the report
|
195
|
+
|
196
|
+
**No related examples**
|
197
|
+
|
198
|
+
## <span style='color: gray'>○</span> `NOTE`
|
199
|
+
|
200
|
+
**Specification:** 3.2 - Using the classes
|
201
|
+
|
202
|
+
> The instance MAY provide methods to load sections and specs from a file
|
203
|
+
|
204
|
+
**No related examples**
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RSpec
|
2
|
+
module RfcHelper
|
3
|
+
class MarkdownRenderer
|
4
|
+
STATUS_COLORS: Hash[String|Symbol, String]
|
5
|
+
|
6
|
+
@specs: Specs
|
7
|
+
|
8
|
+
def detailed_spec_report_block: -> String
|
9
|
+
|
10
|
+
def detailed_spec_report_examples: -> String
|
11
|
+
|
12
|
+
def detailed_spec_report_header: -> String
|
13
|
+
|
14
|
+
def header: -> String
|
15
|
+
|
16
|
+
def main_table: -> String
|
17
|
+
|
18
|
+
def main_table_row: -> String
|
19
|
+
|
20
|
+
def paragraph: -> String
|
21
|
+
|
22
|
+
def render: -> String
|
23
|
+
|
24
|
+
def status: -> String
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RSpec
|
2
|
+
module RfcHelper
|
3
|
+
class Spec
|
4
|
+
attr_reader id: Symbol?
|
5
|
+
attr_reader section: String
|
6
|
+
attr_reader text: String
|
7
|
+
attr_reader verb: Symbol
|
8
|
+
attr_reader examples: Array[RSpec::Core::Example]
|
9
|
+
|
10
|
+
def add_example: -> void
|
11
|
+
|
12
|
+
def status: -> Symbol
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def set_verb: -> void
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RSpec
|
2
|
+
module RfcHelper
|
3
|
+
class Specs
|
4
|
+
@name: String?
|
5
|
+
@sections: Hash[String, {id: Symbol, title: String, url: String? }]
|
6
|
+
@specs: Array[Spec]
|
7
|
+
@specs_url: String?
|
8
|
+
|
9
|
+
def self.new_from_file: -> Specs
|
10
|
+
|
11
|
+
attr_reader name: String?
|
12
|
+
attr_reader sections: Hash[String, {id: Symbol, title: String, url: String? }]
|
13
|
+
attr_reader specs_url: String?
|
14
|
+
|
15
|
+
def add: -> void
|
16
|
+
def add_example: -> void
|
17
|
+
def add_section: -> void
|
18
|
+
def each: -> void
|
19
|
+
def find: -> Spec
|
20
|
+
def ids: -> Array[Symbol]
|
21
|
+
def save_markdown_report: -> void
|
22
|
+
def section_id: -> Symbol
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module RSpec
|
2
|
+
module RfcHelper
|
3
|
+
VERSION: String
|
4
|
+
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
5
|
+
|
6
|
+
@specs: ::RSpec::RfcHelper::Specs
|
7
|
+
|
8
|
+
def self.add_example: -> void
|
9
|
+
def self.load_specs: -> void
|
10
|
+
def self.start_from_file: -> void
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# RSpec RFC Helper Specification
|
2
|
+
|
3
|
+
Well... kind of, it's for the example.
|
4
|
+
|
5
|
+
This is the specification on how `rspec-rfc-helper` must behave and should be used in a test suite.
|
6
|
+
|
7
|
+
They somehow lack of substance, as I'm not a spec writer...
|
8
|
+
|
9
|
+
### 1 - Overview
|
10
|
+
|
11
|
+
RSpec RFC Helper (_RFC Helper_) is a RSpec module to help implement complicated specs: it is often hard to keep track of
|
12
|
+
the external specifications and their implementation; leaving comments in the code is not ideal to understand the state
|
13
|
+
of the overall implementation.
|
14
|
+
|
15
|
+
### 2 - Features
|
16
|
+
|
17
|
+
#### 2.1 Spec definition
|
18
|
+
|
19
|
+
The RFC Helper MUST have a version number
|
20
|
+
|
21
|
+
The RFC Helper MUST have methods to define specs that needs to be enforced and tracked.
|
22
|
+
|
23
|
+
Sometimes, in RFC and other formal documents, the same paragraph can have multiple imperative verbs as `MUST`, `SHOULD`,
|
24
|
+
..., making it complicated to break down and summarize.
|
25
|
+
To tackle this issue, the text used in a spec defined for the RFC Helper MUST wrap the right verb in the relevant
|
26
|
+
paragraph into double square brackets, like, for example `[[VERB]]`. The same paragraph MAY be used in multiple specs,
|
27
|
+
with everytime a different verb wrapper.
|
28
|
+
|
29
|
+
Defined specs also need to be identifiable, so a unique ID MUST be assigned to them.
|
30
|
+
|
31
|
+
When a portion of text is interesting but has no imperative verb in it, it still can be added, and the RFC Helper will
|
32
|
+
give it a status of "note".
|
33
|
+
|
34
|
+
Specifications are often separated in multiple sections. These sections SHOULD be used to group the specs together.
|
35
|
+
|
36
|
+
#### 2.2 Reporting
|
37
|
+
|
38
|
+
Once the RFC helper is configured in a RSpec suite, it MUST handle examples tagged with the `rfc` tag.
|
39
|
+
|
40
|
+
As a value, developers needs to specify one or many spec IDs targeted by the example. The RFC helper MUST handle a
|
41
|
+
single ID, as well as an array of IDs.
|
42
|
+
|
43
|
+
The RFC Helper MUST have at least one method to export the generated report in a file.
|
44
|
+
|
45
|
+
The report MUST contain the generation date.
|
46
|
+
|
47
|
+
### 3 - Usage
|
48
|
+
|
49
|
+
To have some flexibility, the helper SHOULD be useable in two ways : using the module `RSpec::RfcHelper`, or the classes.
|
50
|
+
|
51
|
+
#### 3.1 Using the module
|
52
|
+
|
53
|
+
The `RSpec::RfcHelper` MUST have a method to "start" the helper, reading a file with sections and specs. It MUST have a
|
54
|
+
method to add examples to specs once they are evaluated, and it MUST have a method to save the report.
|
55
|
+
|
56
|
+
Using the module is similar to using a singleton. It has its goods and bad sides.
|
57
|
+
|
58
|
+
#### 3.2 Using the classes
|
59
|
+
|
60
|
+
The RFC Helper SHOULD be used directly by instantiating a class.
|
61
|
+
|
62
|
+
The instance MUST provide methods to add sections
|
63
|
+
|
64
|
+
The instance MUST provide methods to define the specs
|
65
|
+
|
66
|
+
The instance MUST provide methods to generate the report
|
67
|
+
|
68
|
+
The instance MAY provide methods to load sections and specs from a file
|
69
|
+
|
70
|
+
Using the classes is a bit more complex but allows more flexibility.
|
71
|
+
|
72
|
+
### 3 - Final words
|
73
|
+
|
74
|
+
Yeah! that's some specification!
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-rfc-helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Manuel Tancoigne
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-11-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description: Helps keeping track of specifications with more context than just RSpec
|
28
|
+
examples
|
29
|
+
email:
|
30
|
+
- manu@experimentslabs.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".rspec"
|
36
|
+
- ".rubocop.yml"
|
37
|
+
- ".ruby-version"
|
38
|
+
- CHANGELOG.md
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- lib/rspec/rfc_helper.rb
|
43
|
+
- lib/rspec/rfc_helper/markdown_renderer.rb
|
44
|
+
- lib/rspec/rfc_helper/spec.rb
|
45
|
+
- lib/rspec/rfc_helper/specs.rb
|
46
|
+
- lib/rspec/rfc_helper/version.rb
|
47
|
+
- report.md
|
48
|
+
- sig/rspec/rfc_helper.rbs
|
49
|
+
- sig/rspec/rfc_helper/markdown_renderer.rbs
|
50
|
+
- sig/rspec/rfc_helper/spec.rbs
|
51
|
+
- sig/rspec/rfc_helper/specs.rbs
|
52
|
+
- specification_example.md
|
53
|
+
homepage: https://gitlab.com/experimentslabs/rspec-rfc-helper
|
54
|
+
licenses: []
|
55
|
+
metadata:
|
56
|
+
rubygems_mfa_required: 'true'
|
57
|
+
homepage_uri: https://gitlab.com/experimentslabs/rspec-rfc-helper
|
58
|
+
source_code_uri: https://gitlab.com/experimentslabs/rspec-rfc-helper
|
59
|
+
changelog_uri: https://gitlab.com/experimentslabs/rspec-rfc-helper/-/blob/main/CHANGELOG.md
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.1.2
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.4.15
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: RSpec plugin to track RFC compliance
|
79
|
+
test_files: []
|