cucumber_lint 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d357226ed5d21c9e3b55736ba03019f023dce69e
4
+ data.tar.gz: 4b0998c48b5d3e4df9ac658b979ac543dc032811
5
+ SHA512:
6
+ metadata.gz: 851e8c5aaa639c14fb2185cec529f03f29bce5b9ccaa8c6177da58257b93f85bf77d69ab54dfc4ed1c59645b4944961c844d736c3cad8008fa2742cf79febc65
7
+ data.tar.gz: c769b2952189beb4f574a01bfec1479f6716de886280ee239696b4d4328313dec4adc013d314d25606b9a5f2c737f2768ad8492d7303e730b01d1243725ce328
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+
4
+
5
+ Metrics/MethodLength:
6
+ Max: 11
7
+
8
+
9
+ Style/Documentation:
10
+ Enabled: false
11
+
12
+
13
+ Style/EmptyLines:
14
+ Enabled: false
15
+
16
+
17
+ Style/EmptyLinesAroundClassBody:
18
+ EnforcedStyle: empty_lines
19
+
20
+
21
+ Style/MethodDefParentheses:
22
+ EnforcedStyle: require_no_parentheses
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ ruby '2.2.0'
4
+
5
+ gemspec
6
+
7
+ gem 'cucumber'
8
+ gem 'open4'
9
+ gem 'rake'
10
+ gem 'rspec'
11
+ gem 'rubocop'
data/Gemfile.lock ADDED
@@ -0,0 +1,63 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cucumber_lint (0.0.3)
5
+ colorize (~> 0.7.5)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ast (2.0.0)
11
+ astrolabe (1.3.0)
12
+ parser (>= 2.2.0.pre.3, < 3.0)
13
+ builder (3.2.2)
14
+ colorize (0.7.5)
15
+ cucumber (1.3.18)
16
+ builder (>= 2.1.2)
17
+ diff-lcs (>= 1.1.3)
18
+ gherkin (~> 2.12)
19
+ multi_json (>= 1.7.5, < 2.0)
20
+ multi_test (>= 0.1.1)
21
+ diff-lcs (1.2.5)
22
+ gherkin (2.12.2)
23
+ multi_json (~> 1.3)
24
+ multi_json (1.10.1)
25
+ multi_test (0.1.1)
26
+ open4 (1.3.4)
27
+ parser (2.2.0.1)
28
+ ast (>= 1.1, < 3.0)
29
+ slop (~> 3.4, >= 3.4.5)
30
+ powerpack (0.0.9)
31
+ rainbow (2.0.0)
32
+ rake (10.4.2)
33
+ rspec (3.1.0)
34
+ rspec-core (~> 3.1.0)
35
+ rspec-expectations (~> 3.1.0)
36
+ rspec-mocks (~> 3.1.0)
37
+ rspec-core (3.1.7)
38
+ rspec-support (~> 3.1.0)
39
+ rspec-expectations (3.1.2)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.1.0)
42
+ rspec-mocks (3.1.3)
43
+ rspec-support (~> 3.1.0)
44
+ rspec-support (3.1.2)
45
+ rubocop (0.28.0)
46
+ astrolabe (~> 1.3)
47
+ parser (>= 2.2.0.pre.7, < 3.0)
48
+ powerpack (~> 0.0.6)
49
+ rainbow (>= 1.99.1, < 3.0)
50
+ ruby-progressbar (~> 1.4)
51
+ ruby-progressbar (1.7.1)
52
+ slop (3.6.0)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ cucumber
59
+ cucumber_lint!
60
+ open4
61
+ rake
62
+ rspec
63
+ rubocop
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Charlie Rudolph
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.
22
+
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # cucumber_lint
2
+
3
+ A command line linter and formatter for cucumber features
4
+
5
+ ### Installation
6
+
7
+ ```
8
+ gem install cucumber_lint
9
+ ```
10
+
11
+ ### Usage
12
+
13
+ ```
14
+ cucumber_lint # Lints (exits with status 1 on failure, 0 on success)
15
+ cucumber_lint --fix # Fixes all lint errors
16
+ ```
17
+
18
+ ### Tables
19
+ * requires at a space leading and trailing cell content
20
+ * requires pipes to be aligned
21
+
22
+ Bad
23
+ ````
24
+ |header_column1|header_column2|
25
+ |row1_column1|row1_column2|
26
+ |row2_column1|row2_column2|
27
+ |row3_column1|row3_column2|
28
+ ```
29
+ Bad
30
+ ```
31
+ | header_column1 | header_column2 |
32
+ | row1_column1 | row1_column2 |
33
+ | row2_column1 | row2_column2 |
34
+ | row3_column1 | row3_column2 |
35
+ ```
36
+ Good
37
+ ```
38
+ | header_column1 | header_column2 |
39
+ | row1_column1 | row1_column2 |
40
+ | row2_column1 | row2_column2 |
41
+ | row3_column1 | row3_column2 |
42
+ ```
43
+
44
+ ### Steps
45
+ * Use `And` instead of repeating `Given`, `When`, or `Then`
46
+
47
+ Bad
48
+ ```
49
+ Given A
50
+ Given B
51
+ When C
52
+ Then D
53
+ Then E
54
+ ```
55
+ Good
56
+ ```
57
+ Given A
58
+ And B
59
+ When C
60
+ Then D
61
+ And E
62
+ ```
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ desc 'Run all linters and specs'
2
+ task default: %w(lint spec features)
3
+
4
+
5
+ desc 'Run linters'
6
+ task :lint do
7
+ sh 'bundle exec rubocop'
8
+ end
9
+
10
+ desc 'Run specs'
11
+ task :spec do
12
+ sh 'bundle exec rspec'
13
+ end
14
+
15
+ desc 'Run features'
16
+ task :features do
17
+ sh 'bundle exec cucumber'
18
+ end
19
+
20
+
21
+ desc 'Build the gem'
22
+ task build: :default do
23
+ sh 'gem build cucumber_lint.gemspec'
24
+ end
data/bin/cucumber_lint ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'cucumber_lint'
7
+ CucumberLint::Cli.execute(ARGV)
data/cucumber.yml ADDED
@@ -0,0 +1,3 @@
1
+ <% common = "-r features/step_definitions --strict" %>
2
+
3
+ default: <%= common %>
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cucumber_lint/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cucumber_lint'
8
+ spec.version = CucumberLint::VERSION
9
+ spec.authors = ['charlierudolph']
10
+ spec.email = ['charles.w.rudolph@gmail.com']
11
+ spec.summary = 'A command line interface for linting and formatting cucumber features'
12
+ spec.homepage = 'https://github.com/charlierudolph/cucumber_lint'
13
+ spec.license = 'MIT'
14
+
15
+ spec.add_runtime_dependency 'colorize', '~> 0.7.5'
16
+
17
+ spec.files = `git ls-files`.split("\n")
18
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+ end
@@ -0,0 +1,51 @@
1
+ Feature: cli format
2
+
3
+ Scenario: no files
4
+ Given I have no files
5
+ When I run `cucumber_lint --fix`
6
+ Then I see the output
7
+ """
8
+
9
+
10
+ 0 files inspected (0 written)
11
+ """
12
+ And it exits with status 0
13
+
14
+
15
+ Scenario: formatted file
16
+ Given I have a formatted file at "features/formatted.feature"
17
+ When I run `cucumber_lint --fix`
18
+ Then I see the output
19
+ """
20
+ .
21
+
22
+ 1 file inspected (0 written)
23
+ """
24
+ And it exits with status 0
25
+
26
+
27
+ Scenario: unformatted file
28
+ Given I have an unformatted file at "features/unformatted.feature"
29
+ When I run `cucumber_lint --fix`
30
+ Then I see the output
31
+ """
32
+ W
33
+
34
+ 1 file inspected (1 written)
35
+ """
36
+ And it exits with status 0
37
+ And the file "features/unformatted.feature" in now formatted
38
+
39
+
40
+ Scenario: formatted file and unformatted file
41
+ Given I have an formatted file at "features/formatted.feature"
42
+ And I have an unformatted file at "features/unformatted.feature"
43
+ When I run `cucumber_lint --fix`
44
+ Then I see the output
45
+ """
46
+ .W
47
+
48
+ 2 files inspected (1 written)
49
+ """
50
+ And it exits with status 0
51
+ And the file "features/unformatted.feature" in now formatted
@@ -0,0 +1,55 @@
1
+ Feature: cli lint
2
+
3
+ Scenario: no files
4
+ Given I have no files
5
+ When I run `cucumber_lint`
6
+ Then I see the output
7
+ """
8
+
9
+
10
+ 0 files inspected (0 passed)
11
+ """
12
+ And it exits with status 0
13
+
14
+
15
+ Scenario: formatted file
16
+ Given I have a formatted file at "features/formatted.feature"
17
+ When I run `cucumber_lint`
18
+ Then I see the output
19
+ """
20
+ .
21
+
22
+ 1 file inspected (1 passed)
23
+ """
24
+ And it exits with status 0
25
+
26
+
27
+ Scenario: unformatted file
28
+ Given I have an unformatted file at "features/unformatted.feature"
29
+ When I run `cucumber_lint`
30
+ Then I see the output
31
+ """
32
+ F
33
+
34
+ Files with errors:
35
+ ./features/unformatted.feature
36
+
37
+ 1 file inspected (0 passed, 1 failed)
38
+ """
39
+ And it exits with status 1
40
+
41
+
42
+ Scenario: formatted file and unformatted file
43
+ Given I have an formatted file at "features/formatted.feature"
44
+ And I have an unformatted file at "features/unformatted.feature"
45
+ When I run `cucumber_lint`
46
+ Then I see the output
47
+ """
48
+ .F
49
+
50
+ Files with errors:
51
+ ./features/unformatted.feature
52
+
53
+ 2 files inspected (1 passed, 1 failed)
54
+ """
55
+ And it exits with status 1
@@ -0,0 +1,106 @@
1
+ Feature: feature formatter
2
+
3
+ Scenario: feature with single unformatted table
4
+ Given feature content
5
+ """
6
+ Feature: Test Feature
7
+
8
+ Scenario: Test Scenario
9
+ Given a table
10
+ |header_column1|header_column2|
11
+ |row1_column1|row1_column2|
12
+ |row2_column1|row2_column2|
13
+ |row3_column1|row3_column2|
14
+ Given other stuff
15
+ When I do things
16
+ Then my tests pass
17
+ Then I am happy
18
+ """
19
+ Then the formatted feature content is
20
+ """
21
+ Feature: Test Feature
22
+
23
+ Scenario: Test Scenario
24
+ Given a table
25
+ | header_column1 | header_column2 |
26
+ | row1_column1 | row1_column2 |
27
+ | row2_column1 | row2_column2 |
28
+ | row3_column1 | row3_column2 |
29
+ And other stuff
30
+ When I do things
31
+ Then my tests pass
32
+ And I am happy
33
+ """
34
+
35
+
36
+ Scenario: feature with mutliple unformatted tables
37
+ Given feature content
38
+ """
39
+ Feature: Test Feature
40
+
41
+ Scenario: Test Scenario
42
+ Given a table
43
+ |header_column1|header_column2|
44
+ |row1_column1|row1_column2|
45
+ |row2_column1|row2_column2|
46
+ |row3_column1|row3_column2|
47
+ Then my tests pass
48
+
49
+ Scenario Outline: Test Scenario Outline
50
+ Given <a> and <b>
51
+ Then I expect <c>
52
+
53
+ Examples:
54
+ |a | b | c |
55
+ |a1 | b1| c1 |
56
+ |a2 | b2 | c2 |
57
+ |a3 | b3 | c3 |
58
+ """
59
+ Then the formatted feature content is
60
+ """
61
+ Feature: Test Feature
62
+
63
+ Scenario: Test Scenario
64
+ Given a table
65
+ | header_column1 | header_column2 |
66
+ | row1_column1 | row1_column2 |
67
+ | row2_column1 | row2_column2 |
68
+ | row3_column1 | row3_column2 |
69
+ Then my tests pass
70
+
71
+ Scenario Outline: Test Scenario Outline
72
+ Given <a> and <b>
73
+ Then I expect <c>
74
+
75
+ Examples:
76
+ | a | b | c |
77
+ | a1 | b1 | c1 |
78
+ | a2 | b2 | c2 |
79
+ | a3 | b3 | c3 |
80
+ """
81
+
82
+
83
+ Scenario: feature with formatted tables
84
+ Given feature content
85
+ """
86
+ Feature: Test Feature
87
+
88
+ Scenario: Test Scenario
89
+ Given a table
90
+ | header_column1 | header_column2 |
91
+ | row1_column1 | row1_column2 |
92
+ | row2_column1 | row2_column2 |
93
+ | row3_column1 | row3_column2 |
94
+ Then my tests pass
95
+
96
+ Scenario Outline: Test Scenario Outline
97
+ Given <a> and <b>
98
+ Then I expect <c>
99
+
100
+ Examples:
101
+ | a | b | c |
102
+ | a1 | b1 | c1 |
103
+ | a2 | b2 | c2 |
104
+ | a3 | b3 | c3 |
105
+ """
106
+ Then the feature is formatted
@@ -0,0 +1,30 @@
1
+ Feature: table formatter
2
+
3
+ Scenario: unformatted steps
4
+ Given steps
5
+ """
6
+ Given A
7
+ Given B
8
+ When C
9
+ Then D
10
+ Then E
11
+ """
12
+ Then the formatted steps are
13
+ """
14
+ Given A
15
+ And B
16
+ When C
17
+ Then D
18
+ And E
19
+ """
20
+
21
+ Scenario: formatted steps
22
+ Given steps
23
+ """
24
+ Given A
25
+ And B
26
+ When C
27
+ Then D
28
+ And E
29
+ """
30
+ Then the steps are formatted
@@ -0,0 +1,45 @@
1
+ Feature: table formatter
2
+
3
+ Scenario: minimal table
4
+ Given table lines
5
+ """
6
+ |header_column1|header_column2|
7
+ |row1_column1|row1_column2|
8
+ |row2_column1|row2_column2|
9
+ |row3_column1|row3_column2|
10
+ """
11
+ Then the formatted table lines are
12
+ """
13
+ | header_column1 | header_column2 |
14
+ | row1_column1 | row1_column2 |
15
+ | row2_column1 | row2_column2 |
16
+ | row3_column1 | row3_column2 |
17
+ """
18
+
19
+
20
+ Scenario: misaligned spacing in table
21
+ Given table lines
22
+ """
23
+ | header_column1 | header_column2 |
24
+ | row1_column1 | row1_column2 |
25
+ | row2_column1 | row2_column2 |
26
+ | row3_column1 | row3_column2 |
27
+ """
28
+ Then the formatted table lines are
29
+ """
30
+ | header_column1 | header_column2 |
31
+ | row1_column1 | row1_column2 |
32
+ | row2_column1 | row2_column2 |
33
+ | row3_column1 | row3_column2 |
34
+ """
35
+
36
+
37
+ Scenario: formatted table
38
+ Given table lines
39
+ """
40
+ | header_column1 | header_column2 |
41
+ | row1_column1 | row1_column2 |
42
+ | row2_column1 | row2_column2 |
43
+ | row3_column1 | row3_column2 |
44
+ """
45
+ Then the table is formatted
@@ -0,0 +1,31 @@
1
+ Given(/^I have no files$/) do
2
+ # Empty step for readability
3
+ end
4
+
5
+
6
+ Given(/^I have an? (.+?) file at "features\/(.+?)"$/) do |filetype, filename|
7
+ content = IO.read("#{FIXTURES_PATH}/#{filetype}.feature.example")
8
+ IO.write "#{TMP_DIR}/features/#{filename}", content
9
+ end
10
+
11
+
12
+ When(/^I run `(.+?)`$/) do |command|
13
+ @last_run = run command
14
+ end
15
+
16
+
17
+ Then(/^I see the output$/) do |output|
18
+ expect(@last_run.out.uncolorize).to eql "#{output}\n"
19
+ end
20
+
21
+
22
+ Then(/^it exits with status (\d+)$/) do |status|
23
+ expect(@last_run.exit_status).to eql status.to_i
24
+ end
25
+
26
+
27
+ Then(/^the file "features\/(.+?)" in now formatted$/) do |filename|
28
+ expected = IO.read "#{FIXTURES_PATH}/formatted.feature.example"
29
+ actual = IO.read "#{TMP_DIR}/features/#{filename}"
30
+ expect(actual).to eql expected
31
+ end
@@ -0,0 +1,20 @@
1
+ require 'cucumber_lint'
2
+ require 'open4'
3
+ require 'rspec'
4
+
5
+
6
+ BIN_PATH = "#{File.dirname(__FILE__)}/../../bin"
7
+ FIXTURES_PATH = "#{File.dirname(__FILE__)}/fixtures"
8
+ TMP_DIR = '/tmp'
9
+
10
+
11
+ Before do
12
+ Dir.chdir TMP_DIR
13
+ FileUtils.rm_rf 'features'
14
+ Dir.mkdir 'features'
15
+ end
16
+
17
+
18
+ at_exit do
19
+ FileUtils.rm_rf TMP_DIR
20
+ end
@@ -0,0 +1,15 @@
1
+ Given(/^feature content$/) do |content|
2
+ @content = content
3
+ end
4
+
5
+
6
+ Then(/^the formatted feature content is$/) do |content|
7
+ expected = content
8
+ actual = CucumberLint::FeatureFormatter.new(@content).formatted_content
9
+ expect(actual).to eql expected
10
+ end
11
+
12
+
13
+ Then(/^the feature is formatted$/) do
14
+ expect(CucumberLint::FeatureFormatter.new(@content)).to be_formatted
15
+ end
@@ -0,0 +1,9 @@
1
+ Feature: Test Feature
2
+
3
+ Scenario: Test Scenario
4
+ Given a table
5
+ | header_column1 | header_column2 |
6
+ | row1_column1 | row1_column2 |
7
+ | row2_column1 | row2_column2 |
8
+ | row3_column1 | row3_column2 |
9
+ Then my tests pass
@@ -0,0 +1,9 @@
1
+ Feature: Test Feature
2
+
3
+ Scenario: Test Scenario
4
+ Given a table
5
+ |header_column1|header_column2|
6
+ |row1_column1|row1_column2|
7
+ |row2_column1|row2_column2|
8
+ |row3_column1|row3_column2|
9
+ Then my tests pass
@@ -0,0 +1,15 @@
1
+ Given(/^steps$/) do |steps|
2
+ @steps = steps.split("\n", -1)
3
+ end
4
+
5
+
6
+ Then(/^the formatted steps are$/) do |steps|
7
+ expected = steps.split("\n", -1)
8
+ actual = CucumberLint::StepsFormatter.new(@steps).formatted_content
9
+ expect(actual).to eql expected
10
+ end
11
+
12
+
13
+ Then(/^the steps are formatted$/) do
14
+ expect(CucumberLint::StepsFormatter.new(@steps)).to be_formatted
15
+ end
@@ -0,0 +1,12 @@
1
+ def run command
2
+ result = OpenStruct.new
3
+ command = "PATH=#{BIN_PATH}:$PATH; #{command} 2>&1"
4
+
5
+ status = Open4.popen4(command) do |_pid, stdin, stdout, _stderr|
6
+ stdin.close
7
+ result.out = stdout.read
8
+ end
9
+
10
+ result.exit_status = status.exitstatus
11
+ result
12
+ end
@@ -0,0 +1,15 @@
1
+ Given(/^table lines$/) do |lines|
2
+ @lines = lines.split("\n", -1)
3
+ end
4
+
5
+
6
+ Then(/^the formatted table lines are$/) do |lines|
7
+ expected = lines.split("\n", -1)
8
+ actual = CucumberLint::TableFormatter.new(@lines).formatted_content
9
+ expect(actual).to eql expected
10
+ end
11
+
12
+
13
+ Then(/^the table is formatted$/) do
14
+ expect(CucumberLint::TableFormatter.new(@lines)).to be_formatted
15
+ end
@@ -0,0 +1,22 @@
1
+ # Monkey-patching Array
2
+ class Array
3
+
4
+ def group
5
+ groups = []
6
+ last_group = { passing: true, values: [] }
7
+
8
+ each do |element|
9
+ passing = yield element
10
+
11
+ if passing != last_group[:passing]
12
+ groups << last_group unless last_group[:values].empty?
13
+ last_group = { passing: passing, values: [] }
14
+ end
15
+
16
+ last_group[:values] << element
17
+ end
18
+
19
+ groups + [last_group]
20
+ end
21
+
22
+ end
@@ -0,0 +1,7 @@
1
+ require 'cucumber_lint/cli'
2
+ require 'cucumber_lint/feature_formatter'
3
+ require 'cucumber_lint/table_formatter'
4
+ require 'cucumber_lint/steps_formatter'
5
+
6
+ module CucumberLint
7
+ end
@@ -0,0 +1,85 @@
1
+ require 'colorize'
2
+ require 'ostruct'
3
+
4
+ module CucumberLint
5
+ # A class that takes in the content that make up a cucumber feature
6
+ # and can determine if it is formatted and output the formatted content
7
+ class Cli
8
+
9
+ def self.execute args
10
+ new(args).execute!
11
+ end
12
+
13
+
14
+ def initialize args, out: STDOUT
15
+ @out = out
16
+ @lint = args[0] != '--fix'
17
+ @results = OpenStruct.new(formatted: 0, unformatted: 0, unformatted_files: [])
18
+ end
19
+
20
+
21
+ def execute!
22
+ Dir.glob('./features/**/*.feature').each do |filename|
23
+ formatter = FeatureFormatter.new IO.read(filename)
24
+
25
+ if formatter.formatted?
26
+ file_formatted
27
+ else
28
+ file_unformatted filename, formatter.formatted_content
29
+ end
30
+ end
31
+
32
+ output_results
33
+ exit 1 unless @results.unformatted_files.empty?
34
+ end
35
+
36
+ def output_results
37
+ output_failures if @lint && !@results.unformatted_files.empty?
38
+ total = @results.formatted + @results.unformatted
39
+ @out.print "\n\n#{total} file#{'s' if total != 1} inspected ("
40
+ if @lint
41
+ output_inspect_results
42
+ else
43
+ output_write_results
44
+ end
45
+ @out.puts ')'
46
+ end
47
+
48
+
49
+ def output_inspect_results
50
+ @out.print "#{@results.formatted} passed".green
51
+ @out.print ', ' + "#{@results.unformatted} failed".red if @results.unformatted > 0
52
+ end
53
+
54
+
55
+ def output_write_results
56
+ @out.print "#{@results.unformatted} written".yellow
57
+ end
58
+
59
+
60
+ def output_failures
61
+ @out.puts "\n\nFiles with errors:".red
62
+ @out.print @results.unformatted_files.join("\n").red
63
+ end
64
+
65
+
66
+ def file_formatted
67
+ @results.formatted += 1
68
+ @out.print '.'.green
69
+ end
70
+
71
+
72
+ def file_unformatted filename, content
73
+ @results.unformatted += 1
74
+
75
+ if @lint
76
+ @results.unformatted_files << filename
77
+ @out.print 'F'.red
78
+ else
79
+ IO.write filename, content
80
+ @out.print 'W'.yellow
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ require 'core_ext/array'
2
+
3
+ module CucumberLint
4
+ # A class that takes in the content that make up a cucumber feature
5
+ # and can determine if it is formatted and output the formatted content
6
+ class FeatureFormatter
7
+
8
+ attr_reader :formatted_content
9
+
10
+ def initialize content
11
+ @content = content
12
+ @formatted_content = format
13
+ end
14
+
15
+
16
+ def formatted?
17
+ @content == @formatted_content
18
+ end
19
+
20
+
21
+ private
22
+
23
+
24
+ def format
25
+ lines = format_steps @content.split("\n", -1)
26
+ groupings = lines.group { |line| line =~ /^\s*\|/ }
27
+
28
+ groupings.map do |group|
29
+ if group[:passing]
30
+ format_table group[:values]
31
+ else
32
+ group[:values]
33
+ end
34
+ end.flatten.join("\n")
35
+ end
36
+
37
+
38
+ def format_table lines
39
+ TableFormatter.new(lines).formatted_content
40
+ end
41
+
42
+
43
+ def format_steps steps
44
+ StepsFormatter.new(steps).formatted_content
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,50 @@
1
+ module CucumberLint
2
+ # A class that takes in the lines of a cucumber feature
3
+ # and can determine if the steps are formatted and output the formatted steps
4
+ class StepsFormatter
5
+
6
+ attr_reader :formatted_content
7
+
8
+ def initialize lines
9
+ @lines = lines
10
+ @formatted_content = format
11
+ end
12
+
13
+
14
+ def formatted?
15
+ @lines == @formatted_content
16
+ end
17
+
18
+ private
19
+
20
+ RESET_KEYWORDS = %w(Background Scenario)
21
+ STEP_TYPES = %w(Given When Then)
22
+
23
+ def format
24
+ @lines.map { |line| format_line line }
25
+ end
26
+
27
+
28
+ def format_line line
29
+ @previous_step_type = nil if should_reset_previous_step_type? line
30
+
31
+ step_type = line.split(' ', -1)[0]
32
+
33
+ if STEP_TYPES.include?(step_type)
34
+ if @previous_step_type == step_type
35
+ line.sub! step_type, 'And'
36
+ else
37
+ @previous_step_type = step_type
38
+ end
39
+ end
40
+
41
+ line
42
+ end
43
+
44
+
45
+ def should_reset_previous_step_type? line
46
+ RESET_KEYWORDS.any? { |keyword| line.start_with? keyword }
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ module CucumberLint
2
+ # A class that takes in the lines that make up a cucumber table
3
+ # and can determine if it is formatted and output the formatted table
4
+ class TableFormatter
5
+
6
+ attr_reader :formatted_content
7
+
8
+ def initialize lines
9
+ @lines = lines
10
+ @column_widths = column_widths
11
+ @formatted_content = format
12
+ end
13
+
14
+
15
+ def formatted?
16
+ @lines == @formatted_content
17
+ end
18
+
19
+
20
+ private
21
+
22
+
23
+ def column_widths
24
+ widths_by_line = @lines.map { |line| line_column_widths line }
25
+ grouped_widths = widths_by_line[0].zip(*widths_by_line[1..-1])
26
+ grouped_widths.each_with_index.map do |width, i|
27
+ i == 0 ? width.min : width.max
28
+ end
29
+ end
30
+
31
+
32
+ def format
33
+ @lines.each_with_index.map do |line|
34
+ line.split('|', -1).each_with_index.map do |piece, index|
35
+ format_piece piece, index
36
+ end.join('|')
37
+ end
38
+ end
39
+
40
+
41
+ def format_piece piece, index
42
+ if index == 0
43
+ ' ' * column_widths[index]
44
+ elsif index == column_widths.length - 1
45
+ piece
46
+ else
47
+ " #{piece.strip} ".ljust column_widths[index]
48
+ end
49
+ end
50
+
51
+
52
+ def line_column_widths line
53
+ pieces = line.split('|', -1)
54
+ pieces.each_with_index.map do |piece, index|
55
+ if index == 0 || index == pieces.length - 1
56
+ piece.length
57
+ else
58
+ piece.strip!
59
+ piece.length + 2 # one space padding on each side
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module CucumberLint
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'core_ext/array'
2
+
3
+ describe Array do
4
+ describe '#group' do
5
+ it 'returns elements grouped by whether of not they pass the given block' do
6
+ array = [1, 1, 2, 3, 5, 8, 13, 21, 34]
7
+ expected = array.group(&:odd?)
8
+ actual = [
9
+ { passing: true, values: [1, 1] },
10
+ { passing: false, values: [2] },
11
+ { passing: true, values: [3, 5] },
12
+ { passing: false, values: [8] },
13
+ { passing: true, values: [13, 21] },
14
+ { passing: false, values: [34] }
15
+ ]
16
+ expect(expected).to eql actual
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cucumber_lint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - charlierudolph
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.5
27
+ description:
28
+ email:
29
+ - charles.w.rudolph@gmail.com
30
+ executables:
31
+ - cucumber_lint
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".rubocop.yml"
37
+ - Gemfile
38
+ - Gemfile.lock
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - bin/cucumber_lint
43
+ - cucumber.yml
44
+ - cucumber_lint.gemspec
45
+ - features/cucumber_lint/cli_fix.feature
46
+ - features/cucumber_lint/cli_lint.feature
47
+ - features/cucumber_lint/feature_formatter.feature
48
+ - features/cucumber_lint/steps_formatter.feature
49
+ - features/cucumber_lint/table_formatter.feature
50
+ - features/step_definitions/cli_steps.rb
51
+ - features/step_definitions/env.rb
52
+ - features/step_definitions/feature_formatter_steps.rb
53
+ - features/step_definitions/fixtures/formatted.feature.example
54
+ - features/step_definitions/fixtures/unformatted.feature.example
55
+ - features/step_definitions/steps_formatter_steps.rb
56
+ - features/step_definitions/support/run_helpers.rb
57
+ - features/step_definitions/table_formatter_steps.rb
58
+ - lib/core_ext/array.rb
59
+ - lib/cucumber_lint.rb
60
+ - lib/cucumber_lint/cli.rb
61
+ - lib/cucumber_lint/feature_formatter.rb
62
+ - lib/cucumber_lint/steps_formatter.rb
63
+ - lib/cucumber_lint/table_formatter.rb
64
+ - lib/cucumber_lint/version.rb
65
+ - spec/core_ext/array_spec.rb
66
+ homepage: https://github.com/charlierudolph/cucumber_lint
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.4.5
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: A command line interface for linting and formatting cucumber features
90
+ test_files: []