rspec-rfc-helper 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|