cuke_linter 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d4a41e4f1d37dde72387b44003228f891855230312e1da57ffe227fa7f1f853
4
- data.tar.gz: ec0842ceccb4025aad332923ae0df4d921250f58f5f2a97f82818b0d7d0717c4
3
+ metadata.gz: 7b5619ded0f8f0a4db213f7c2505b8868865ea4f5a40991515bf8324c474d5b4
4
+ data.tar.gz: 93c3214e99988dcfd03d6ce2eb15d291b8a1bbbc04a9af0f5c6f280a6fdccd6d
5
5
  SHA512:
6
- metadata.gz: c8c38a81b0d6fe47db0f46eaefbc61a8c00305f90273d752fc81e7eb364278f2dc36cc7bf09b487bb183543a27b3380fe219eb7049d934e54fb2f117bc1a31a9
7
- data.tar.gz: 8f8a924840f5ce979fe6037e85dca647eb70f91b1f7b2a1a3ee9e31716af04475a9b54095769d703cb796b41c6089366e1dd6d0a61652fd4cc430330fb774df7
6
+ metadata.gz: 325b5aadbcf822a5e00ffd1ca7db7c26c4e7c6d63673c509247c49364cd09a365b6c5bcb9e42d4a01ebfe12f8bd85378bc639bb41356bca5766c96334a934046
7
+ data.tar.gz: 6ba5e64086bc5f79716408d6383b281ef468e63dc405423f216c05d6b3a342c0fedf3d2fee9bc4ba8b896a6db8004a40852fb0ed245c8b89328ba4df52bf7402
data/CHANGELOG.md CHANGED
@@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
 
9
9
  Nothing yet...
10
10
 
11
+ ## [0.6.0] - 2019-06-25
12
+
13
+ ### Added
14
+ - File paths can now be provided for linting without the user first having to turn them into models themselves.
15
+ - Option flags have been added to the command line interface. This will allow more flexibility when running from the command line and thereby reduce the need for writing a Ruby script for even basic usages of the gem.
16
+
17
+ ### Changed
18
+ - Linting is now done using a collection of model trees instead of taking only a single model tree.
19
+
20
+ ### Fixed
21
+ - Fixed a bug in the 'pretty' formatter that was causing problems to be ordered by line number instead of being ordered by file (and then by line number within those files).
22
+ - Added a missing `require` statement that is needed if no other code in the runtime environment has loaded the needed library already.
23
+ - Fixed a bug where enabling/disabling a linter would trigger additional configuration of the linter, even for linters that could not be configured.
24
+
11
25
  ## [0.5.0] - 2019-05-25
12
26
 
13
27
  ### Added
@@ -51,7 +65,8 @@ Nothing yet...
51
65
  - Custom linters, formatters, and command line usability
52
66
 
53
67
 
54
- [Unreleased]: https://github.com/enkessler/cuke_linter/compare/v0.5.0...HEAD
68
+ [Unreleased]: https://github.com/enkessler/cuke_linter/compare/v0.6.0...HEAD
69
+ [0.6.0]: https://github.com/enkessler/cuke_linter/compare/v0.5.0...v0.6.0
55
70
  [0.5.0]: https://github.com/enkessler/cuke_linter/compare/v0.4.0...v0.5.0
56
71
  [0.4.0]: https://github.com/enkessler/cuke_linter/compare/v0.3.1...v0.4.0
57
72
  [0.3.1]: https://github.com/enkessler/cuke_linter/compare/v0.3.0...v0.3.1
data/README.md CHANGED
@@ -9,7 +9,7 @@ User stuff:
9
9
 
10
10
  Developer stuff:
11
11
  [![Build Status](https://travis-ci.org/enkessler/cuke_linter.svg?branch=dev)](https://travis-ci.org/enkessler/cuke_linter)
12
- [![Build Status](https://ci.appveyor.com/api/projects/status/g5o70u747x073evy?svg=true)](https://ci.appveyor.com/project/enkessler/cuke-linter)
12
+ [![Build Status](https://ci.appveyor.com/api/projects/status/g5o70u747x073evy/branch/dev?svg=true)](https://ci.appveyor.com/project/enkessler/cuke-linter/branch/dev)
13
13
  [![Coverage Status](https://coveralls.io/repos/github/enkessler/cuke_linter/badge.svg?branch=dev)](https://coveralls.io/github/enkessler/cuke_linter?branch=dev)
14
14
  [![Maintainability](https://api.codeclimate.com/v1/badges/d1b86760e59a457c8e73/maintainability)](https://codeclimate.com/github/enkessler/cuke_linter/maintainability)
15
15
  [![Inline docs](http://inch-ci.org/github/enkessler/cuke_linter.svg?branch=dev)](https://inch-ci.org/github/enkessler/cuke_linter?branch=dev)
@@ -40,12 +40,18 @@ Or install it yourself as:
40
40
 
41
41
  ## Usage
42
42
 
43
+ #### From the command line
44
+
43
45
  The easiest way to use the gem is to use all of the defaults by invoking it from the command line directly.
44
46
 
45
47
  ```
46
48
  $ cuke_linter
47
49
  ```
48
50
 
51
+ Additional command line options can be provided that can adjust the default behavior. See [documentation](#documentation) for specifics.
52
+
53
+ #### From a Ruby script
54
+
49
55
  The linter can also be used inside of a Ruby script, like so:
50
56
 
51
57
  ```
@@ -54,20 +60,20 @@ require 'cuke_linter'
54
60
  CukeLinter.lint
55
61
  ```
56
62
 
57
- 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.
63
+ The linting will happen against a tree of `CukeModeler` models that is generated based on the current directory. You can generate your own model trees and use them instead, if desired, or even provide specific file paths that will be modeled and linted.
58
64
 
59
- Custom linters can be any object that responds to `#lint` and returns a detected issue (or `nil`) in the format of
65
+ `cuke_linter` comes with a set of pre-made linters and will use them by default but custom linters can be used instead. Custom linters can be any object that responds to `#lint` and returns a detected issue (or `nil`) in the format of
60
66
 
61
67
  ```
62
68
  { problem: 'some linting issue',
63
69
  location: 'path/to/file:line_number' }
64
70
  ```
65
71
 
66
- 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.
72
+ Note that a linter will receive, in turn, *every model* in a 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.
67
73
 
68
74
  **In order to simplify the process of creating custom linters a base class is provided (see [documentation](#documentation)).**
69
75
 
70
- Custom formatters can be any object that responds to `#format` and takes input data in the following format:
76
+ `cuke_linter` comes with a set of pre-made formatters and will use them by default but custom formatters can be used instead. Custom formatters can be any object that responds to `#format` and takes input data in the following format:
71
77
 
72
78
  ```
73
79
  [
@@ -122,13 +128,14 @@ class MyCustomFormatter
122
128
 
123
129
  end
124
130
 
125
- linter = MyCustomLinter.new
126
- formatter = MyCustomFormatter.new
127
- output_path = "#{__dir__}/my_report.txt"
128
- model_tree_root = CukeModeler::Directory.new(Dir.pwd)
131
+ linter = MyCustomLinter.new
132
+ formatter = MyCustomFormatter.new
133
+ output_path = "#{__dir__}/my_report.txt"
134
+ model_tree_root = CukeModeler::Directory.new(Dir.pwd)
135
+ additional_file_path = 'path/to/some.feature'
129
136
 
130
137
  # Providing the formatter twice so that it also is printed to the console
131
- CukeLinter.lint(linters: [linter], formatters: [[formatter], [formatter, output_path]], model_tree: model_tree_root)
138
+ CukeLinter.lint(linters: [linter], formatters: [[formatter], [formatter, output_path]], model_trees: [model_tree_root], file_paths: [additional_file_path])
132
139
  ```
133
140
 
134
141
  ### Configuration
data/exe/cuke_linter CHANGED
@@ -1,6 +1,108 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "cuke_linter"
4
+ require 'optparse'
4
5
 
5
- CukeLinter.lint
6
+ params = {}
7
+ params[:paths] = []
8
+ params[:formatters] = []
9
+ params[:outs] = []
10
+ params[:requires] = []
6
11
 
12
+ parser = OptionParser.new do |options|
13
+
14
+ options.set_summary_width(30)
15
+
16
+ options.on('-p', '--path PATH', String,
17
+ 'The file path that should be linted. Can be a file or directory.',
18
+ 'This option can be specified multiple times in order to lint',
19
+ 'multiple, unconnected locations.') do |path|
20
+ params[:paths] << path
21
+ end
22
+
23
+ options.on('-f', '--formatter FORMATTER', String,
24
+ 'The formatter used for generating linting output. This option',
25
+ 'can be specified multiple times in order to use more than one',
26
+ 'formatter. Formatters must be specified using their fully',
27
+ 'qualified class name (e.g CukeLinter::PrettyFormatter). Uses',
28
+ 'the default formatter if none are specified.') do |format|
29
+ params[:formatters] << format
30
+ end
31
+
32
+ options.on('-o', '--out OUT', String,
33
+ 'The file path to which linting results are output. Can be specified',
34
+ 'multiple times. Specified files are matched to formatters in the',
35
+ 'same order that the formatters are specified. Any formatter without',
36
+ 'a corresponding file path will output to STDOUT instead.') do |out|
37
+ params[:outs] << out
38
+ end
39
+
40
+ options.on('-r', '--require FILEPATH', String,
41
+ 'A file that will be required before further processing. Likely',
42
+ 'needed when using custom linters or formatters in order to ensure',
43
+ 'that the specified classes have been read into memory. This option',
44
+ 'can be specified multiple times in order to load more than one file.') do |file_path|
45
+ params[:requires] << file_path
46
+ end
47
+
48
+ options.on('-c', '--config FILEPATH', String,
49
+ 'The configuration file that will be used. Will use the default',
50
+ 'configuration file (if present) if this option is not specified.') do |file_path|
51
+
52
+ if params[:config]
53
+ puts 'Cannot specify more than one configuration file!'
54
+ exit(1)
55
+ end
56
+
57
+ params[:config] = file_path
58
+ end
59
+
60
+ options.on('-h', '--help', 'Display the help that you are reading now.') do
61
+ puts options.help
62
+ exit
63
+ end
64
+
65
+ options.on('-v', '--version', 'Display the version of the gem being used.') do
66
+ puts CukeLinter::VERSION
67
+ exit
68
+ end
69
+
70
+ end
71
+
72
+ begin
73
+ parser.parse!
74
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
75
+ puts e.message
76
+ puts parser.help
77
+
78
+ exit(1)
79
+ end
80
+
81
+
82
+ require_files = params[:requires]
83
+
84
+ require_files.each do |file|
85
+ require file
86
+ end
87
+
88
+ file_paths = params[:paths]
89
+ formatters = params[:formatters].map { |formatter| Kernel.const_get(formatter).new }
90
+ output_paths = params[:outs]
91
+
92
+ bundled_formatters = [].tap do |formatter_output_pairs|
93
+ [formatters.count, output_paths.count].max.times do |count|
94
+ formatter_output_pairs << [formatters[count] || CukeLinter::PrettyFormatter.new, output_paths[count]]
95
+ end
96
+ end
97
+
98
+ options = {}
99
+ options[:formatters] = bundled_formatters unless bundled_formatters.empty?
100
+ options[:file_paths] = file_paths
101
+
102
+ if params[:config]
103
+ CukeLinter.load_configuration(config_file_path: params[:config])
104
+ elsif File.exist?("#{Dir.pwd}/.cuke_linter")
105
+ CukeLinter.load_configuration
106
+ end
107
+
108
+ CukeLinter.lint(options)
data/lib/cuke_linter.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'yaml'
1
2
  require 'cuke_modeler'
2
3
 
3
4
  require "cuke_linter/version"
@@ -27,7 +28,7 @@ module CukeLinter
27
28
  'StepWithEndPeriodLinter' => StepWithEndPeriodLinter.new,
28
29
  'StepWithTooManyCharactersLinter' => StepWithTooManyCharactersLinter.new,
29
30
  'TestWithTooManyStepsLinter' => TestWithTooManyStepsLinter.new }
30
-
31
+
31
32
 
32
33
  # Configures linters based on the given options
33
34
  def self.load_configuration(config_file_path: nil, config: nil)
@@ -39,11 +40,11 @@ module CukeLinter
39
40
  end
40
41
 
41
42
  config = config || YAML.load_file(config_file_path)
42
-
43
+
43
44
  config.each_pair do |linter_name, options|
44
45
  unregister_linter(linter_name) if options.key?('Enabled') && !options['Enabled']
45
46
 
46
- registered_linters[linter_name].configure(options) if registered_linters[linter_name]
47
+ registered_linters[linter_name].configure(options) if registered_linters[linter_name] && registered_linters[linter_name].respond_to?(:configure)
47
48
  end
48
49
  end
49
50
 
@@ -72,24 +73,37 @@ module CukeLinter
72
73
  self.registered_linters.clear
73
74
  end
74
75
 
75
- # Lints the tree of model objects rooted at the given model using the given linting objects and formatting the results with the given formatters and their respective output locations
76
- def self.lint(model_tree: CukeModeler::Directory.new(Dir.pwd), linters: self.registered_linters.values, formatters: [[CukeLinter::PrettyFormatter.new]])
77
- # puts "model tree: #{model_tree}"
78
- # puts "linters: #{linters}"
79
- # puts "formatters: #{formatters}"
76
+ # Lints the given model trees and file paths using the given linting objects and formatting the results with the given formatters and their respective output locations
77
+ def self.lint(file_paths: [], model_trees: [], linters: self.registered_linters.values, formatters: [[CukeLinter::PrettyFormatter.new]])
78
+
79
+ model_trees = [CukeModeler::Directory.new(Dir.pwd)] if model_trees.empty? && file_paths.empty?
80
+ file_path_models = file_paths.collect do |file_path|
81
+ # TODO: raise exception unless path exists
82
+ case
83
+ when File.directory?(file_path)
84
+ CukeModeler::Directory.new(file_path)
85
+ when File.file?(file_path) && File.extname(file_path) == '.feature'
86
+ CukeModeler::FeatureFile.new(file_path)
87
+ else
88
+ # Non-feature files are not modeled
89
+ end
90
+ end.compact # Compacting in order to get rid of any `nil` values left over from non-feature files
80
91
 
81
92
  linting_data = []
93
+ model_sets = model_trees + file_path_models
82
94
 
83
- model_tree.each_model do |model|
84
- linters.each do |linter|
85
- # TODO: have linters lint only certain types of models
86
- # linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
95
+ model_sets.each do |model_tree|
96
+ model_tree.each_model do |model|
97
+ linters.each do |linter|
98
+ # TODO: have linters lint only certain types of models
99
+ # linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
87
100
 
88
- result = linter.lint(model)
101
+ result = linter.lint(model)
89
102
 
90
- if result
91
- result[:linter] = linter.name
92
- linting_data << result
103
+ if result
104
+ result[:linter] = linter.name
105
+ linting_data << result
106
+ end
93
107
  end
94
108
  end
95
109
  end
@@ -21,13 +21,17 @@ module CukeLinter
21
21
  formatted_data << " #{problem}" + "\n"
22
22
 
23
23
  sorted_locations = locations.sort do |a, b|
24
- location_1 = a.match(/:(\d+)/)[1].to_i
25
- location_2 = b.match(/:(\d+)/)[1].to_i
24
+ file_name_1 = a.match(/(.*):\d+$/)[1]
25
+ line_number_1 = a.match(/:(\d+)$/)[1].to_i
26
+ file_name_2 = b.match(/(.*):\d+$/)[1]
27
+ line_number_2 = b.match(/:(\d+)$/)[1].to_i
26
28
 
27
29
  case
28
- when location_1 < location_2
30
+ when (file_name_1 < file_name_2) ||
31
+ (file_name_1 == file_name_2) && (line_number_1 < line_number_2)
29
32
  -1
30
- when location_1 > location_2
33
+ when (file_name_1 > file_name_2) ||
34
+ (file_name_1 == file_name_2) && (line_number_1 > line_number_2)
31
35
  1
32
36
  else
33
37
  0
@@ -1,4 +1,4 @@
1
1
  module CukeLinter
2
2
  # The release version of this gem
3
- VERSION = "0.5.0"
3
+ VERSION = "0.6.0"
4
4
  end
@@ -2,11 +2,188 @@ Feature: Using cuke_linter on the command line
2
2
 
3
3
  Linting functionality can be used directly from the command line.
4
4
 
5
+
5
6
  Scenario: Linting features
6
7
 
8
+ Note: By default, linting will be done in the current directory using all linters and 'pretty' formatter.
9
+
7
10
  Given the cuke_linter executable is available
8
11
  When the following command is executed:
9
- """
10
- cuke_linter
11
- """
12
+ """
13
+ cuke_linter
14
+ """
12
15
  Then a linting report will be made for all features
16
+
17
+ Scenario: Accessing command line help
18
+ Given the cuke_linter executable is available
19
+ When the following command is executed:
20
+ """
21
+ cuke_linter -h
22
+ """
23
+ Then the following help is displayed:
24
+ """
25
+ Usage: cuke_linter [options]
26
+ -p, --path PATH The file path that should be linted. Can be a file or directory.
27
+ This option can be specified multiple times in order to lint
28
+ multiple, unconnected locations.
29
+ -f, --formatter FORMATTER The formatter used for generating linting output. This option
30
+ can be specified multiple times in order to use more than one
31
+ formatter. Formatters must be specified using their fully
32
+ qualified class name (e.g CukeLinter::PrettyFormatter). Uses
33
+ the default formatter if none are specified.
34
+ -o, --out OUT The file path to which linting results are output. Can be specified
35
+ multiple times. Specified files are matched to formatters in the
36
+ same order that the formatters are specified. Any formatter without
37
+ a corresponding file path will output to STDOUT instead.
38
+ -r, --require FILEPATH A file that will be required before further processing. Likely
39
+ needed when using custom linters or formatters in order to ensure
40
+ that the specified classes have been read into memory. This option
41
+ can be specified multiple times in order to load more than one file.
42
+ -c, --config FILEPATH The configuration file that will be used. Will use the default
43
+ configuration file (if present) if this option is not specified.
44
+ -h, --help Display the help that you are reading now.
45
+ -v, --version Display the version of the gem being used.
46
+ """
47
+
48
+ Scenario: Checking the version of CukeLinter
49
+ Given the cuke_linter executable is available
50
+ When the following command is executed:
51
+ """
52
+ cuke_linter -v
53
+ """
54
+ Then the version of the tool is displayed:
55
+ """
56
+ <major>.<minor>.<patch>
57
+ """
58
+
59
+ Scenario: Specifying directories and files to lint
60
+ Given the following feature file "some.feature":
61
+ """
62
+ Feature:
63
+ Scenario: A scenario
64
+ * a step
65
+ """
66
+ And the following feature file "a_directory/with_a.feature":
67
+ """
68
+ Feature:
69
+ Scenario: A scenario
70
+ * a step
71
+ """
72
+ When the following command is executed:
73
+ """
74
+ cuke_linter -p <path_to>/some.feature -p <path_to>/a_directory
75
+ """
76
+ Then the resulting output is the following:
77
+ """
78
+ FeatureWithoutDescriptionLinter
79
+ Feature has no description
80
+ <path_to>/a_directory/with_a.feature:1
81
+ <path_to>/some.feature:1
82
+
83
+ 2 issues found
84
+ """
85
+
86
+ Scenario: Loading additional files
87
+ Given the following file "some_important_file.rb":
88
+ """
89
+ puts 'I got loaded!'
90
+ """
91
+ When the following command is executed:
92
+ """
93
+ cuke_linter -r <path_to>/some_important_file.rb
94
+ """
95
+ Then the resulting output will include the following:
96
+ """
97
+ I got loaded!
98
+ """
99
+
100
+ Scenario: Specifying a formatter to use
101
+
102
+ Note: The file containing the formatter class will have to be explicitly loaded if not using one of the built in formatters
103
+
104
+ Given the following feature file "some.feature":
105
+ """
106
+ Feature: This feature will have linted problems
107
+ """
108
+ And the following file "my_custom_formatter.rb":
109
+ """
110
+ class MyCustomFormatter
111
+ def format(data)
112
+ puts "Formatting done by #{self.class}"
113
+ end
114
+ end
115
+ """
116
+ When the following command is executed:
117
+ """
118
+ cuke_linter -p <path_to>/some.feature -f MyCustomFormatter -r <path_to>/my_custom_formatter.rb
119
+ """
120
+ Then the resulting output is the following:
121
+ """
122
+ Formatting done by MyCustomFormatter
123
+ """
124
+
125
+ Scenario: Redirecting output
126
+ Given the cuke_linter executable is available
127
+ When the following command is executed:
128
+ """
129
+ cuke_linter -o <path_to>/my_report.txt
130
+ """
131
+ Then the linting report will be output to "<path_to>/my_report.txt"
132
+
133
+ Scenario: Redirecting output for specific formatters
134
+
135
+ Note: Formatters match to output locations in the same order that they are specified. Formatters that do not have their output location specified will output to STDOUT. Output locations that are not matched to a formatter will use the default formatter.
136
+
137
+ Given the following feature file "some.feature":
138
+ """
139
+ Feature: This feature will have linted problems
140
+ """
141
+ And the following file "my_custom_formatters.rb":
142
+ """
143
+ class MyCustomFormatter
144
+ def format(data)
145
+ "Formatting done by #{self.class}"
146
+ end
147
+ end
148
+
149
+ class MyOtherCustomFormatter
150
+ def format(data)
151
+ "Formatting done by #{self.class}"
152
+ end
153
+ end
154
+ """
155
+ When the following command is executed:
156
+ """
157
+ cuke_linter -p <path_to>/some.feature -f MyCustomFormatter -f MyOtherCustomFormatter -o <path_to>/my_report.txt -r <path_to>/my_custom_formatters.rb
158
+ """
159
+ Then the resulting output is the following:
160
+ """
161
+ Formatting done by MyOtherCustomFormatter
162
+ """
163
+ And the file "<path_to>/my_report.txt" contains:
164
+ """
165
+ Formatting done by MyCustomFormatter
166
+ """
167
+
168
+ Scenario: Specifying a configuration file
169
+
170
+ Note: If not specified, the default configuration file, if present, will be used
171
+
172
+ Given the following feature file "has_no_scenarios.feature":
173
+ """
174
+ Feature: This feature
175
+ has no scenarios
176
+ """
177
+ And the following configuration file "my_config.file":
178
+ """
179
+ FeatureWithoutScenariosLinter:
180
+ Enabled: false
181
+ """
182
+ When the following command is executed:
183
+ """
184
+ cuke_linter -p <path_to>/has_no_scenarios.feature -c <path_to>/my_config.file
185
+ """
186
+ Then the resulting output is the following:
187
+ """
188
+ 0 issues found
189
+ """