cuke_linter 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/.gitignore +16 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +148 -0
- data/Rakefile +23 -0
- data/appveyor.yml +24 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cuke_linter.gemspec +34 -0
- data/environments/common_env.rb +11 -0
- data/environments/cucumber_env.rb +4 -0
- data/environments/rspec_env.rb +41 -0
- data/exe/cuke_linter +6 -0
- data/lib/cuke_linter/formatters/pretty_formatter.rb +48 -0
- data/lib/cuke_linter/linters/feature_without_scenarios_linter.rb +22 -0
- data/lib/cuke_linter/version.rb +3 -0
- data/lib/cuke_linter.rb +71 -0
- data/testing/cucumber/features/command_line.feature +12 -0
- data/testing/cucumber/features/formatters/pretty_formatter.feature +24 -0
- data/testing/cucumber/features/linters/default_linters.feature +7 -0
- data/testing/cucumber/features/linters/feature_without_scenarios.feature +12 -0
- data/testing/cucumber/step_definitions/action_steps.rb +13 -0
- data/testing/cucumber/step_definitions/setup_steps.rb +30 -0
- data/testing/cucumber/step_definitions/verification_steps.rb +18 -0
- data/testing/file_helper.rb +28 -0
- data/testing/formatter_factory.rb +15 -0
- data/testing/linter_factory.rb +22 -0
- data/testing/model_factory.rb +39 -0
- data/testing/rspec/spec/integration/cuke_linter_integration_spec.rb +112 -0
- data/testing/rspec/spec/integration/formatters/formatter_integration_specs.rb +5 -0
- data/testing/rspec/spec/integration/formatters/pretty_formatter_integration_spec.rb +8 -0
- data/testing/rspec/spec/integration/linters/feature_without_scenarios_linter_integration_spec.rb +8 -0
- data/testing/rspec/spec/integration/linters/linter_integration_specs.rb +7 -0
- data/testing/rspec/spec/unit/cuke_linter_unit_spec.rb +83 -0
- data/testing/rspec/spec/unit/formatters/formatter_unit_specs.rb +11 -0
- data/testing/rspec/spec/unit/formatters/pretty_formatter_unit_spec.rb +75 -0
- data/testing/rspec/spec/unit/linters/feature_without_scenarios_linter_unit_spec.rb +72 -0
- data/testing/rspec/spec/unit/linters/linter_unit_specs.rb +38 -0
- metadata +183 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aa45813f720250fdd2aaceab4b69148a6029074418331a2467c65fa544733067
|
4
|
+
data.tar.gz: 89abfd15ecc91f630bc8ab247346d26f8a20ec1f14ce11af197db65725e1fbb6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2e986813b8bdb0e86b358058ceac6c62939fce89bf1049599e18e51d5d88d5cbc244c1d172333ed93091e71353a9a859e60caad47aaa08b81e1ca716a9ee72e8
|
7
|
+
data.tar.gz: 3e354fcea0ac508b7bbeb25eeea8ff40bf17e21c2897213dedca3ab76d2c0e9f0c53e2fa030daf0de097b7e3c88a9337647336b17c0e2c0facee0852d8c0c354
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
Nothing yet...
|
10
|
+
|
11
|
+
|
12
|
+
## [0.1.0] - 2019-02-10
|
13
|
+
|
14
|
+
### Added
|
15
|
+
- Custom linters, formatters, and command line usability
|
16
|
+
|
17
|
+
|
18
|
+
[Unreleased]: https://github.com/enkessler/cuke_linter/compare/v0.1.0...HEAD
|
19
|
+
[0.1.0]: https://github.com/enkessler/cuke_linter/compare/2bbd3f29f4eb45b6e9ea7d47c5bb47182bf4fde7...HEAD
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018-2019 Eric Kessler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/cuke_linter.svg)](https://rubygems.org/gems/cuke_linter)
|
2
|
+
[![Build Status](https://travis-ci.org/enkessler/cuke_linter.svg?branch=master)](https://travis-ci.org/enkessler/cuke_linter)
|
3
|
+
[![Build Status](https://ci.appveyor.com/api/projects/status/g5o70u747x073evy?svg=true)](https://ci.appveyor.com/project/enkessler/cuke-linter)
|
4
|
+
[![Project License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/mit-license.php)
|
5
|
+
|
6
|
+
|
7
|
+
# CukeLinter
|
8
|
+
|
9
|
+
So you have started to use Cucumber to describe your system in the abstract, natural language style of Gherkin. But wait! All of your feature files are themselves code and that means that they may need the same protection from anti-patterns as the lower level source code of your system. Enter `cuke_linter`.
|
10
|
+
|
11
|
+
This gem provides linting functionality for `.feature` files by building upon the modeling capabilities of the `cuke_modeler` gem. By passing models through a set of linters, reports can be generated that will inform you of potential bugs, style violations, or anything else that you can define as a problem via custom linters!
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'cuke_linter'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install cuke_linter
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
The easiest way to use the gem is to use all of the defaults by invoking it from the command line directly.
|
32
|
+
|
33
|
+
```
|
34
|
+
$ cuke_linter
|
35
|
+
```
|
36
|
+
|
37
|
+
The linter can also be used inside of a Ruby script, like so:
|
38
|
+
|
39
|
+
```
|
40
|
+
require 'cuke_linter'
|
41
|
+
|
42
|
+
CukeLinter.lint
|
43
|
+
```
|
44
|
+
|
45
|
+
The linting will happen against a tree of `CukeModeler` models that is generated based on the current directory. You can generate your own model tree and use that instead, if desired.
|
46
|
+
|
47
|
+
Custom linters can be any object that responds to `#lint` and returns a collection of detected issues in the format of
|
48
|
+
|
49
|
+
```
|
50
|
+
[
|
51
|
+
{ problem: 'some linting issue',
|
52
|
+
location: 'path/to/file:line_number' },
|
53
|
+
{ problem: 'some linting issue',
|
54
|
+
location: 'path/to/file:line_number' },
|
55
|
+
# etc.
|
56
|
+
]
|
57
|
+
```
|
58
|
+
|
59
|
+
Note that a linter will receive, in turn, *every model* in the model tree in order for it to have the chance to detect problems with it. Checking the model's class before attempting to lint it is recommended.
|
60
|
+
|
61
|
+
Custom formatters can be any object that responds to `#format` and takes input data in the following format:
|
62
|
+
|
63
|
+
```
|
64
|
+
[
|
65
|
+
{ linter: 'some linter name',
|
66
|
+
problem: 'some linting issue',
|
67
|
+
location: 'path/to/file:line_number' },
|
68
|
+
{ linter: 'some linter name',
|
69
|
+
problem: 'some linting issue',
|
70
|
+
location: 'path/to/file:line_number' },
|
71
|
+
# etc.
|
72
|
+
]
|
73
|
+
```
|
74
|
+
|
75
|
+
All formatted data will be output to STDOUT unless a file location is provided as an alternative.
|
76
|
+
|
77
|
+
Below is an example of using non-default linting options.
|
78
|
+
|
79
|
+
```
|
80
|
+
require 'cuke_linter'
|
81
|
+
|
82
|
+
class MyCustomLinter
|
83
|
+
|
84
|
+
def name
|
85
|
+
'MyCustomLinter'
|
86
|
+
end
|
87
|
+
|
88
|
+
def lint(model)
|
89
|
+
return [] unless model.is_a?(CukeModeler::Scenario)
|
90
|
+
|
91
|
+
if model.name.empty?
|
92
|
+
[{ problem: 'Scenario has no name', location: "#{model.get_ancestor(:feature_file).path}:#{model.source_line}" }]
|
93
|
+
else
|
94
|
+
[]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class MyCustomFormatter
|
101
|
+
|
102
|
+
def format(linting_data)
|
103
|
+
formatted_data = ''
|
104
|
+
|
105
|
+
linting_data.each do |lint_item|
|
106
|
+
formatted_data << "#{lint_item[:linter]}\n"
|
107
|
+
formatted_data << " #{lint_item[:problem]}\n"
|
108
|
+
formatted_data << " #{lint_item[:location]}\n"
|
109
|
+
end
|
110
|
+
|
111
|
+
formatted_data
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
linter = MyCustomLinter.new
|
117
|
+
formatter = MyCustomFormatter.new
|
118
|
+
output_path = "#{__dir__}/my_report.txt"
|
119
|
+
model_tree_root = CukeModeler::Directory.new(Dir.pwd)
|
120
|
+
|
121
|
+
# Providing the formatter twice so that it also is printed to the console
|
122
|
+
CukeLinter.lint(linters: [linter], formatters: [[formatter], [formatter, output_path]], model_tree: model_tree_root)
|
123
|
+
```
|
124
|
+
|
125
|
+
|
126
|
+
For more information, see the documentation [here](https://app.cucumber.pro/projects/cuke_linter/documents/branch/master/).
|
127
|
+
|
128
|
+
|
129
|
+
## Development
|
130
|
+
|
131
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake cuke_linter:test_everything` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
132
|
+
|
133
|
+
## Contributing
|
134
|
+
|
135
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/enkessler/cuke_linter.
|
136
|
+
|
137
|
+
1. Fork it
|
138
|
+
2. Create your feature branch (off of the development branch)
|
139
|
+
`git checkout -b my-new-feature`
|
140
|
+
3. Commit your changes
|
141
|
+
`git commit -am 'Add some feature'`
|
142
|
+
4. Push to the branch
|
143
|
+
`git push origin my-new-feature`
|
144
|
+
5. Create new Pull Request
|
145
|
+
|
146
|
+
## License
|
147
|
+
|
148
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'racatt'
|
3
|
+
|
4
|
+
|
5
|
+
namespace 'racatt' do
|
6
|
+
Racatt.create_tasks
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace 'cuke_linter' do
|
10
|
+
|
11
|
+
desc 'Run all of the tests'
|
12
|
+
task :test_everything do
|
13
|
+
rspec_args = '--pattern testing/rspec/spec/**/*_spec.rb'
|
14
|
+
cucumber_args = "testing/cucumber/features -r environments/cucumber_env.rb -f progress -t 'not @wip'"
|
15
|
+
|
16
|
+
Rake::Task['racatt:test_everything'].invoke(rspec_args, cucumber_args)
|
17
|
+
end
|
18
|
+
|
19
|
+
task :ci_build => 'cuke_linter:test_everything'
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
task :default => 'cuke_linter:test_everything'
|
data/appveyor.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
version: '1.0.{build}'
|
2
|
+
|
3
|
+
environment:
|
4
|
+
matrix:
|
5
|
+
- RUBY_VERSION: 23
|
6
|
+
- RUBY_VERSION: 23-x64
|
7
|
+
- RUBY_VERSION: 24
|
8
|
+
- RUBY_VERSION: 24-x64
|
9
|
+
- RUBY_VERSION: 25
|
10
|
+
- RUBY_VERSION: 25-x64
|
11
|
+
|
12
|
+
install:
|
13
|
+
- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
|
14
|
+
- bundle install
|
15
|
+
|
16
|
+
build: off
|
17
|
+
|
18
|
+
before_test:
|
19
|
+
- ruby -v
|
20
|
+
- gem -v
|
21
|
+
- bundle -v
|
22
|
+
|
23
|
+
test_script:
|
24
|
+
- bundle exec rake cuke_linter:ci_build
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cuke_linter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/cuke_linter.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cuke_linter/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cuke_linter"
|
8
|
+
spec.version = CukeLinter::VERSION
|
9
|
+
spec.authors = ["Eric Kessler"]
|
10
|
+
spec.email = ["morrow748@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Lints feature files used by Cucumber and other similar frameworks.}
|
13
|
+
spec.homepage = 'https://github.com/enkessler/cuke_linter'
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_runtime_dependency 'cuke_modeler', '~> 1.5'
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "< 3.0"
|
29
|
+
spec.add_development_dependency "cucumber", "~> 3.0"
|
30
|
+
spec.add_development_dependency "racatt", "~> 1.0"
|
31
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
32
|
+
spec.add_development_dependency "require_all", "~> 2.0"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'require_all'
|
2
|
+
|
3
|
+
require 'cuke_linter'
|
4
|
+
|
5
|
+
|
6
|
+
require_relative '../testing/model_factory'
|
7
|
+
require_relative '../testing/linter_factory'
|
8
|
+
require_relative '../testing/formatter_factory'
|
9
|
+
require_relative '../testing/file_helper'
|
10
|
+
|
11
|
+
PROJECT_ROOT = "#{__dir__}/.."
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'common_env'
|
2
|
+
require 'rspec'
|
3
|
+
require 'rubygems/mock_gem_ui'
|
4
|
+
|
5
|
+
require_relative '../testing/rspec/spec/unit/formatters/formatter_unit_specs'
|
6
|
+
require_relative '../testing/rspec/spec/unit/linters/linter_unit_specs'
|
7
|
+
require_relative '../testing/rspec/spec/integration/formatters/formatter_integration_specs'
|
8
|
+
require_relative '../testing/rspec/spec/integration/linters/linter_integration_specs'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
# Enable flags like --only-failures and --next-failure
|
12
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
13
|
+
|
14
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
15
|
+
config.disable_monkey_patching!
|
16
|
+
|
17
|
+
config.expect_with :rspec do |c|
|
18
|
+
c.syntax = :expect
|
19
|
+
end
|
20
|
+
|
21
|
+
config.before(:suite) do
|
22
|
+
# Using a global variable instead of an instance variable because instance variables are not compatible with :suite hooks
|
23
|
+
$default_linters = Marshal.load(Marshal.dump(CukeLinter.registered_linters))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Restore the original linters after any test that modifies them so that other tests can rely on them being only the default ones
|
27
|
+
config.after(:example, :linter_registration) do
|
28
|
+
CukeLinter.clear_registered_linters
|
29
|
+
|
30
|
+
$default_linters.each_pair do |name, linter|
|
31
|
+
CukeLinter.register_linter(name: name, linter: linter)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
config.after(:suite) do
|
36
|
+
CukeLinter::FileHelper.created_directories.each do |dir_path|
|
37
|
+
FileUtils.remove_entry(dir_path, true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/exe/cuke_linter
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module CukeLinter
|
2
|
+
class PrettyFormatter
|
3
|
+
|
4
|
+
def format(data)
|
5
|
+
categorized_problems = Hash.new { |linters, linter_name| linters[linter_name] = Hash.new { |problems, problem| problems[problem] = [] } }
|
6
|
+
|
7
|
+
data.each do |lint_item|
|
8
|
+
categorized_problems[lint_item[:linter]][lint_item[:problem]] << lint_item[:location]
|
9
|
+
end
|
10
|
+
|
11
|
+
formatted_data = ''
|
12
|
+
|
13
|
+
categorized_problems.each_pair do |linter, problems|
|
14
|
+
formatted_data << linter + "\n"
|
15
|
+
|
16
|
+
problems.each_pair do |problem, locations|
|
17
|
+
formatted_data << " #{problem}" + "\n"
|
18
|
+
|
19
|
+
sorted_locations = locations.sort do |a, b|
|
20
|
+
location_1 = a.match(/:(\d+)/)[1].to_i
|
21
|
+
location_2 = b.match(/:(\d+)/)[1].to_i
|
22
|
+
|
23
|
+
case
|
24
|
+
when location_1 < location_2
|
25
|
+
-1
|
26
|
+
when location_1 > location_2
|
27
|
+
1
|
28
|
+
else
|
29
|
+
0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
sorted_locations.each do |location|
|
34
|
+
formatted_data << " #{location}\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
total_problems = data.count
|
40
|
+
formatted_data << "\n" unless total_problems.zero?
|
41
|
+
|
42
|
+
formatted_data << "#{total_problems} issues found"
|
43
|
+
|
44
|
+
formatted_data
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CukeLinter
|
2
|
+
class FeatureWithoutScenariosLinter
|
3
|
+
# # class FeatureWithoutScenariosLinter < BaseLinter
|
4
|
+
#
|
5
|
+
# # targets :features
|
6
|
+
|
7
|
+
def name
|
8
|
+
'FeatureWithoutScenariosLinter'
|
9
|
+
end
|
10
|
+
|
11
|
+
def lint(model)
|
12
|
+
return [] unless model.is_a?(CukeModeler::Feature)
|
13
|
+
|
14
|
+
if model.tests.empty?
|
15
|
+
[{ problem: 'Feature has no scenarios', location: "#{model.parent_model.path}:#{model.source_line}" }]
|
16
|
+
else
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/cuke_linter.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'cuke_modeler'
|
2
|
+
|
3
|
+
require "cuke_linter/version"
|
4
|
+
require 'cuke_linter/formatters/pretty_formatter'
|
5
|
+
require 'cuke_linter/linters/feature_without_scenarios_linter'
|
6
|
+
|
7
|
+
|
8
|
+
module CukeLinter
|
9
|
+
|
10
|
+
@registered_linters = { 'FeatureWithoutScenariosLinter' => FeatureWithoutScenariosLinter.new }
|
11
|
+
|
12
|
+
def self.register_linter(linter:, name:)
|
13
|
+
@registered_linters[name] = linter
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.unregister_linter(name)
|
17
|
+
@registered_linters.delete(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.registered_linters
|
21
|
+
@registered_linters
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clear_registered_linters
|
25
|
+
@registered_linters.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.lint(model_tree: CukeModeler::Directory.new(Dir.pwd), linters: @registered_linters.values, formatters: [[CukeLinter::PrettyFormatter.new]])
|
29
|
+
# puts "model tree: #{model_tree}"
|
30
|
+
# puts "linters: #{linters}"
|
31
|
+
# puts "formatters: #{formatters}"
|
32
|
+
|
33
|
+
linting_data = []
|
34
|
+
|
35
|
+
model_tree.each_model do |model|
|
36
|
+
linters.each do |linter|
|
37
|
+
# TODO: have linters lint only certain types of models
|
38
|
+
# linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
|
39
|
+
|
40
|
+
linted_data = linter.lint(model)
|
41
|
+
linted_data.each { |data_point| data_point[:linter] = linter.name }
|
42
|
+
linting_data.concat(linted_data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
formatters.each do |formatter_output_pair|
|
47
|
+
formatter = formatter_output_pair[0]
|
48
|
+
location = formatter_output_pair[1]
|
49
|
+
|
50
|
+
formatted_data = formatter.format(linting_data)
|
51
|
+
|
52
|
+
if location
|
53
|
+
File.write(location, formatted_data)
|
54
|
+
else
|
55
|
+
puts formatted_data
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO: keep this or always format data?
|
60
|
+
linting_data
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# # def self.relevant_model?(linter, model)
|
65
|
+
# # model_classes = linter.class.target_model_types.map { |type| CukeModeler.const_get(type.to_s.capitalize.chop) }
|
66
|
+
# # model_classes.any? { |clazz| model.is_a?(clazz) }
|
67
|
+
# # end
|
68
|
+
# #
|
69
|
+
# # private_class_method(:relevant_model?)
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Using cuke_linter on the command line
|
2
|
+
|
3
|
+
Linting functionality can be used directly from the command line.
|
4
|
+
|
5
|
+
Scenario: Linting features
|
6
|
+
|
7
|
+
Given the cuke_linter executable is available
|
8
|
+
When the following command is executed:
|
9
|
+
"""
|
10
|
+
cuke_linter
|
11
|
+
"""
|
12
|
+
Then a linting report will be made for all features
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Pretty formatter
|
2
|
+
|
3
|
+
Scenario: Formatting linter data
|
4
|
+
Given the following linter data:
|
5
|
+
| linter name | problem | location |
|
6
|
+
| SomeLinter | Some problem | path/to/the_file:1 |
|
7
|
+
| SomeOtherLinter | Some other problem | path/to/the_file:33 |
|
8
|
+
| SomeOtherLinter | Some other problem | path/to/the_file:101 |
|
9
|
+
| SomeOtherLinter | Yet another problem | path/to/the_file:55 |
|
10
|
+
When it is formatted by the "pretty" formatter
|
11
|
+
Then the resulting output is the following:
|
12
|
+
"""
|
13
|
+
SomeLinter
|
14
|
+
Some problem
|
15
|
+
path/to/the_file:1
|
16
|
+
SomeOtherLinter
|
17
|
+
Some other problem
|
18
|
+
path/to/the_file:33
|
19
|
+
path/to/the_file:101
|
20
|
+
Yet another problem
|
21
|
+
path/to/the_file:55
|
22
|
+
|
23
|
+
4 issues found
|
24
|
+
"""
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Feature without scenarios linter
|
2
|
+
|
3
|
+
Scenario: Linting
|
4
|
+
Given a linter for features without scenarios
|
5
|
+
And the following feature:
|
6
|
+
"""
|
7
|
+
Feature:
|
8
|
+
"""
|
9
|
+
When it is linted
|
10
|
+
Then an error is reported
|
11
|
+
| problem | location |
|
12
|
+
| Feature has no scenarios | <path_to_file>:1 |
|
@@ -0,0 +1,13 @@
|
|
1
|
+
When(/^the following command is executed:$/) do |command|
|
2
|
+
command = "bundle exec ruby #{@executable_directory}/#{command}"
|
3
|
+
|
4
|
+
@output = `#{command}`
|
5
|
+
end
|
6
|
+
|
7
|
+
When(/^it is formatted by the "([^"]*)" formatter$/) do |linter_name|
|
8
|
+
@results = CukeLinter.const_get("#{linter_name.capitalize}Formatter").new.format(@linter_data)
|
9
|
+
end
|
10
|
+
|
11
|
+
When(/^it is linted$/) do
|
12
|
+
@results = @linter.lint(@model)
|
13
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Given(/^the cuke_linter executable is available$/) do
|
2
|
+
@executable_directory = "#{PROJECT_ROOT}/exe"
|
3
|
+
end
|
4
|
+
|
5
|
+
Given(/^the following linter data:$/) do |linter_data|
|
6
|
+
@linter_data = [].tap do |data|
|
7
|
+
linter_data.hashes.each do |data_point|
|
8
|
+
data << { linter: data_point['linter name'],
|
9
|
+
problem: data_point['problem'],
|
10
|
+
location: data_point['location'] }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Given(/^the following feature:$/) do |text|
|
16
|
+
@model = CukeModeler::Feature.new(text)
|
17
|
+
|
18
|
+
fake_file_model = CukeModeler::FeatureFile.new
|
19
|
+
fake_file_model.path = 'path_to_file'
|
20
|
+
|
21
|
+
@model.parent_model = fake_file_model
|
22
|
+
end
|
23
|
+
|
24
|
+
Given(/^a linter for features without scenarios$/) do
|
25
|
+
@linter = CukeLinter::FeatureWithoutScenariosLinter.new
|
26
|
+
end
|
27
|
+
|
28
|
+
Given(/^no other linters have been registered$/) do
|
29
|
+
# There is no way to 'reset' the linters, so just assume that no changes have been made
|
30
|
+
end
|