scss-lint 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/scss-lint +1 -4
  3. data/config/default.yml +5 -4
  4. data/data/property-sort-orders/recess.txt +149 -0
  5. data/data/property-sort-orders/smacss.txt +138 -0
  6. data/lib/scss_lint.rb +1 -0
  7. data/lib/scss_lint/cli.rb +93 -153
  8. data/lib/scss_lint/config.rb +16 -13
  9. data/lib/scss_lint/control_comment_processor.rb +83 -0
  10. data/lib/scss_lint/engine.rb +21 -5
  11. data/lib/scss_lint/exceptions.rb +6 -0
  12. data/lib/scss_lint/linter.rb +6 -2
  13. data/lib/scss_lint/linter/bang_format.rb +20 -9
  14. data/lib/scss_lint/linter/duplicate_property.rb +35 -30
  15. data/lib/scss_lint/linter/empty_line_between_blocks.rb +1 -1
  16. data/lib/scss_lint/linter/id_selector.rb +10 -0
  17. data/lib/scss_lint/linter/indentation.rb +2 -1
  18. data/lib/scss_lint/linter/leading_zero.rb +6 -6
  19. data/lib/scss_lint/linter/name_format.rb +11 -0
  20. data/lib/scss_lint/linter/selector_format.rb +0 -4
  21. data/lib/scss_lint/linter/single_line_per_property.rb +13 -7
  22. data/lib/scss_lint/linter/single_line_per_selector.rb +19 -11
  23. data/lib/scss_lint/linter/trailing_semicolon.rb +5 -3
  24. data/lib/scss_lint/linter/trailing_zero.rb +4 -4
  25. data/lib/scss_lint/options.rb +113 -0
  26. data/lib/scss_lint/reporter/default_reporter.rb +15 -7
  27. data/lib/scss_lint/reporter/json_reporter.rb +15 -8
  28. data/lib/scss_lint/reporter/xml_reporter.rb +12 -6
  29. data/lib/scss_lint/runner.rb +4 -5
  30. data/lib/scss_lint/version.rb +1 -1
  31. data/spec/scss_lint/cli_spec.rb +9 -229
  32. data/spec/scss_lint/linter/bang_format_spec.rb +20 -0
  33. data/spec/scss_lint/linter/duplicate_property_spec.rb +13 -0
  34. data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +12 -11
  35. data/spec/scss_lint/linter/id_selector_spec.rb +62 -0
  36. data/spec/scss_lint/linter/indentation_spec.rb +11 -0
  37. data/spec/scss_lint/linter/name_format_spec.rb +147 -117
  38. data/spec/scss_lint/linter/selector_format_spec.rb +3 -66
  39. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +20 -0
  40. data/spec/scss_lint/linter_spec.rb +248 -0
  41. data/spec/scss_lint/options_spec.rb +42 -0
  42. data/spec/spec_helper.rb +1 -1
  43. metadata +177 -183
  44. data/lib/scss_lint/linter/id_with_extraneous_selector.rb +0 -20
  45. data/spec/scss_lint/linter/id_with_extraneous_selector_spec.rb +0 -139
@@ -29,10 +29,6 @@ module SCSSLint
29
29
  check(placeholder, 'placeholder') unless @ignored_types.include?('placeholder')
30
30
  end
31
31
 
32
- def visit_pseudo(pseudo)
33
- check(pseudo, 'pseudo') unless @ignored_types.include?('pseudo-selector')
34
- end
35
-
36
32
  private
37
33
 
38
34
  def check(node, type)
@@ -18,13 +18,7 @@ module SCSSLint
18
18
  'on separate line from selector')
19
19
  end
20
20
 
21
- # Compare each property against the next property to see if they are on
22
- # the same line
23
- properties[0..-2].zip(properties[1..-1]).each do |first, second|
24
- next unless first.line == second.line
25
-
26
- add_lint(second, "Property '#{second.name.join}' should be placed on own line")
27
- end
21
+ check_adjacent_properties(properties)
28
22
  end
29
23
 
30
24
  private
@@ -49,5 +43,17 @@ module SCSSLint
49
43
  def first_property_not_on_own_line?(rule, properties)
50
44
  properties.any? && properties.first.line == rule.line
51
45
  end
46
+
47
+ # Compare each property against the next property to see if they are on
48
+ # the same line.
49
+ #
50
+ # @param properties [Array<Sass::Tree::PropNode>]
51
+ def check_adjacent_properties(properties)
52
+ properties[0..-2].zip(properties[1..-1]).each do |first, second|
53
+ next unless first.line == second.line
54
+
55
+ add_lint(second, "Property '#{second.name.join}' should be placed on own line")
56
+ end
57
+ end
52
58
  end
53
59
  end
@@ -8,19 +8,27 @@ module SCSSLint
8
8
  def visit_comma_sequence(node)
9
9
  return unless node.members.count > 1
10
10
 
11
- if node.members[0].members[1] == "\n"
12
- # Comma is on its own line
13
- add_lint(node, MESSAGE)
14
- end
11
+ check_comma_on_own_line(node)
15
12
 
16
13
  node.members[1..-1].each_with_index do |sequence, index|
17
- if sequence.members[0] != "\n"
18
- # Next sequence doesn't reside on its own line
19
- add_lint(node.line + index, MESSAGE)
20
- elsif sequence.members[1] == "\n"
21
- # Comma is on its own line
22
- add_lint(node.line + index, MESSAGE)
23
- end
14
+ check_sequence_commas(node, sequence, index)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def check_comma_on_own_line(node)
21
+ return unless node.members[0].members[1] == "\n"
22
+ add_lint(node, MESSAGE)
23
+ end
24
+
25
+ def check_sequence_commas(node, sequence, index)
26
+ if sequence.members[0] != "\n"
27
+ # Next sequence doesn't reside on its own line
28
+ add_lint(node.line + index, MESSAGE)
29
+ elsif sequence.members[1] == "\n"
30
+ # Comma is on its own line
31
+ add_lint(node.line + index, MESSAGE)
24
32
  end
25
33
  end
26
34
  end
@@ -28,6 +28,8 @@ module SCSSLint
28
28
  end
29
29
 
30
30
  def visit_import(node)
31
+ # Ignore all but the last import for comma-separated @imports
32
+ return if source_from_range(node.source_range) =~ /,\s*$/
31
33
  check_semicolon(node)
32
34
  end
33
35
 
@@ -35,13 +37,13 @@ module SCSSLint
35
37
 
36
38
  def check_semicolon(node)
37
39
  if has_space_before_semicolon?(node)
38
- line = node.source_range.start_pos.line
39
- add_lint line, 'Declaration should be terminated by a semicolon'
40
- elsif !ends_with_semicolon?(node)
41
40
  line = node.source_range.start_pos.line
42
41
  add_lint line,
43
42
  'Declaration should not have a space before ' \
44
43
  'the terminating semicolon'
44
+ elsif !ends_with_semicolon?(node)
45
+ line = node.source_range.start_pos.line
46
+ add_lint line, 'Declaration should be terminated by a semicolon'
45
47
  elsif ends_with_multiple_semicolons?(node)
46
48
  line = node.source_range.start_pos.line
47
49
  add_lint line, 'Declaration should be terminated by a single semicolon'
@@ -1,5 +1,5 @@
1
1
  module SCSSLint
2
- # Checks for unnecessary leading zeros in numeric values with decimal points.
2
+ # Checks for unnecessary trailing zeros in numeric values with decimal points.
3
3
  class Linter::TrailingZero < Linter
4
4
  include LinterRegistry
5
5
 
@@ -9,7 +9,7 @@ module SCSSLint
9
9
  non_string_values = remove_quoted_strings(node.value).split
10
10
  non_string_values.each do |value|
11
11
  next unless number = value[FRACTIONAL_DIGIT_REGEX, 1]
12
- check_number(node, number)
12
+ check_for_trailing_zeros(node, number)
13
13
  end
14
14
  end
15
15
 
@@ -17,14 +17,14 @@ module SCSSLint
17
17
  return unless number =
18
18
  source_from_range(node.source_range)[FRACTIONAL_DIGIT_REGEX, 1]
19
19
 
20
- check_number(node, number)
20
+ check_for_trailing_zeros(node, number)
21
21
  end
22
22
 
23
23
  private
24
24
 
25
25
  FRACTIONAL_DIGIT_REGEX = /^-?(\d*\.\d+)/
26
26
 
27
- def check_number(node, original_number)
27
+ def check_for_trailing_zeros(node, original_number)
28
28
  return unless match = /^(\d*\.\d*)0+$/.match(original_number)
29
29
 
30
30
  fixed_number = match[1]
@@ -0,0 +1,113 @@
1
+ require 'optparse'
2
+
3
+ module SCSSLint
4
+ # Handles option parsing for the command line application.
5
+ class Options
6
+ DEFAULT_REPORTER = [SCSSLint::Reporter::DefaultReporter, :stdout]
7
+
8
+ # Parses command line options into an options hash.
9
+ #
10
+ # @param args [Array<String>] arguments passed via the command line
11
+ # @return [Hash] parsed options
12
+ def parse(args)
13
+ @options = {
14
+ reporters: [DEFAULT_REPORTER],
15
+ }
16
+
17
+ OptionParser.new do |parser|
18
+ parser.banner = "Usage: #{parser.program_name} [options] [scss-files]"
19
+
20
+ add_display_options parser
21
+ add_linter_options parser
22
+ add_file_options parser
23
+ add_info_options parser
24
+ end.parse!(args)
25
+
26
+ # Any remaining arguments are assumed to be files
27
+ @options[:files] = args
28
+
29
+ @options
30
+ rescue OptionParser::InvalidOption => ex
31
+ raise SCSSLint::Exceptions::InvalidCLIOption,
32
+ ex.message,
33
+ ex.backtrace
34
+ end
35
+
36
+ private
37
+
38
+ def add_display_options(parser)
39
+ parser.on('-f', '--format Formatter', 'Specify how to display lints', String) do |format|
40
+ define_output_format(format)
41
+ end
42
+
43
+ parser.on('-r', '--require path', 'Require Ruby file', String) do |path|
44
+ @options[:required_paths] ||= []
45
+ @options[:required_paths] << path
46
+ end
47
+ end
48
+
49
+ # @param format [String]
50
+ def define_output_format(format)
51
+ unless @options[:reporters] == [DEFAULT_REPORTER] && format == 'Default'
52
+ @options[:reporters].reject! { |i| i == DEFAULT_REPORTER }
53
+ reporter = SCSSLint::Reporter.const_get(format + 'Reporter')
54
+ @options[:reporters] << [reporter, :stdout]
55
+ end
56
+ rescue NameError
57
+ raise SCSSLint::Exceptions::InvalidCLIOption,
58
+ "Invalid output format specified: #{format}"
59
+ end
60
+
61
+ def add_linter_options(parser)
62
+ parser.on('-i', '--include-linter linter,...', Array,
63
+ 'Specify which linters you want to run') do |linters|
64
+ @options[:included_linters] = linters
65
+ end
66
+
67
+ parser.on('-x', '--exclude-linter linter,...', Array,
68
+ "Specify which linters you don't want to run") do |linters|
69
+ @options[:excluded_linters] = linters
70
+ end
71
+ end
72
+
73
+ def add_file_options(parser)
74
+ parser.on('-c', '--config config-file', String,
75
+ 'Specify which configuration file you want to use') do |conf_file|
76
+ @options[:config_file] = conf_file
77
+ end
78
+
79
+ parser.on('-e', '--exclude file,...', Array,
80
+ 'List of file names to exclude') do |files|
81
+ @options[:excluded_files] = files
82
+ end
83
+
84
+ parser.on('-o', '--out path', 'Write output to a file instead of STDOUT', String) do |path|
85
+ define_output_path(path)
86
+ end
87
+ end
88
+
89
+ # @param path [String]
90
+ def define_output_path(path)
91
+ last_reporter, _output = @options[:reporters].pop
92
+ @options[:reporters] << [last_reporter, path]
93
+ end
94
+
95
+ def add_info_options(parser)
96
+ parser.on_tail('--show-formatters', 'Shows available formatters') do
97
+ @options[:show_formatters] = true
98
+ end
99
+
100
+ parser.on_tail('--show-linters', 'Display available linters') do
101
+ @options[:show_linters] = true
102
+ end
103
+
104
+ parser.on_tail('-h', '--help', 'Display help documentation') do
105
+ @options[:help] = parser.help
106
+ end
107
+
108
+ parser.on_tail('-v', '--version', 'Display version') do
109
+ @options[:version] = true
110
+ end
111
+ end
112
+ end
113
+ end
@@ -5,15 +5,23 @@ module SCSSLint
5
5
  return unless lints.any?
6
6
 
7
7
  lints.map do |lint|
8
- type = lint.error? ? '[E]'.color(:red) : '[W]'.color(:yellow)
8
+ "#{location(lint)} #{type(lint)} #{message(lint)}"
9
+ end.join("\n") + "\n"
10
+ end
9
11
 
10
- linter_name = "#{lint.linter.name}: ".color(:green) if lint.linter
11
- message = "#{linter_name}#{lint.description}"
12
+ private
12
13
 
13
- "#{lint.filename.color(:cyan)}:" <<
14
- "#{lint.location.line}".color(:magenta) <<
15
- " #{type} #{message}"
16
- end.join("\n") + "\n"
14
+ def location(lint)
15
+ "#{lint.filename.color(:cyan)}:#{lint.location.line.to_s.color(:magenta)}"
16
+ end
17
+
18
+ def type(lint)
19
+ lint.error? ? '[E]'.color(:red) : '[W]'.color(:yellow)
20
+ end
21
+
22
+ def message(lint)
23
+ linter_name = "#{lint.linter.name}: ".color(:green) if lint.linter
24
+ "#{linter_name}#{lint.description}"
17
25
  end
18
26
  end
19
27
  end
@@ -7,17 +7,24 @@ module SCSSLint
7
7
  output = {}
8
8
  lints.group_by(&:filename).each do |filename, file_lints|
9
9
  output[filename] = file_lints.map do |lint|
10
- issue = {}
11
- issue['linter'] = lint.linter.name if lint.linter
12
- issue['line'] = lint.location.line
13
- issue['column'] = lint.location.column
14
- issue['length'] = lint.location.length
15
- issue['severity'] = lint.severity
16
- issue['reason'] = lint.description
17
- issue
10
+ issue_hash(lint)
18
11
  end
19
12
  end
20
13
  JSON.pretty_generate(output)
21
14
  end
15
+
16
+ private
17
+
18
+ def issue_hash(lint)
19
+ {
20
+ 'line' => lint.location.line,
21
+ 'column' => lint.location.column,
22
+ 'length' => lint.location.length,
23
+ 'severity' => lint.severity,
24
+ 'reason' => lint.description,
25
+ }.tap do |hash|
26
+ hash['linter'] = lint.linter.name if lint.linter
27
+ end
28
+ end
22
29
  end
23
30
  end
@@ -9,12 +9,7 @@ module SCSSLint
9
9
  output << "<file name=#{filename.encode(xml: :attr)}>"
10
10
 
11
11
  file_lints.each do |lint|
12
- output << "<issue linter=\"#{lint.linter.name if lint.linter}\" " \
13
- "line=\"#{lint.location.line}\" " \
14
- "column=\"#{lint.location.column}\" " \
15
- "length=\"#{lint.location.length}\" " \
16
- "severity=\"#{lint.severity}\" " \
17
- "reason=#{lint.description.encode(xml: :attr)} />"
12
+ output << issue_tag(lint)
18
13
  end
19
14
 
20
15
  output << '</file>'
@@ -23,5 +18,16 @@ module SCSSLint
23
18
 
24
19
  output
25
20
  end
21
+
22
+ private
23
+
24
+ def issue_tag(lint)
25
+ "<issue linter=\"#{lint.linter.name if lint.linter}\" " \
26
+ "line=\"#{lint.location.line}\" " \
27
+ "column=\"#{lint.location.column}\" " \
28
+ "length=\"#{lint.location.length}\" " \
29
+ "severity=\"#{lint.severity}\" " \
30
+ "reason=#{lint.description.encode(xml: :attr)} />"
31
+ end
26
32
  end
27
33
  end
@@ -35,11 +35,8 @@ module SCSSLint
35
35
  config ||= @config
36
36
 
37
37
  @linters.each do |linter|
38
- next unless config.linter_enabled?(linter)
39
- next if config.excluded_file_for_linter?(file, linter)
40
-
41
38
  begin
42
- run_linter(linter, engine, config)
39
+ run_linter(linter, engine, config, file)
43
40
  rescue => error
44
41
  raise SCSSLint::Exceptions::LinterError,
45
42
  "#{linter.class} raised unexpected error linting file #{file}: " \
@@ -55,7 +52,9 @@ module SCSSLint
55
52
  end
56
53
 
57
54
  # For stubbing in tests.
58
- def run_linter(linter, engine, config)
55
+ def run_linter(linter, engine, config, file)
56
+ return unless config.linter_enabled?(linter)
57
+ return if config.excluded_file_for_linter?(file, linter)
59
58
  linter.run(engine, config.linter_options(linter))
60
59
  end
61
60
  end
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module SCSSLint
3
- VERSION = '0.30.0'
3
+ VERSION = '0.31.0'
4
4
  end
@@ -27,246 +27,28 @@ describe SCSSLint::CLI do
27
27
  SCSSLint::Linter::FakeTestLinter2])
28
28
  end
29
29
 
30
- describe '#parse_arguments' do
31
- let(:files) { ['file1.scss', 'file2.scss'] }
32
- let(:flags) { [] }
33
- subject { SCSSLint::CLI.new(flags + files) }
34
-
35
- def safe_parse
36
- subject.parse_arguments
37
- rescue SystemExit
38
- # Keep running tests
39
- end
40
-
41
- context 'when the config_file flag is set' do
42
- let(:config_file) { 'my-config-file.yml' }
43
- let(:flags) { ['-c', config_file] }
44
-
45
- it 'loads that config file' do
46
- SCSSLint::Config.should_receive(:load).with(config_file)
47
- safe_parse
48
- end
49
-
50
- context 'and the config file is invalid' do
51
- before do
52
- SCSSLint::Config.should_receive(:load)
53
- .with(config_file)
54
- .and_raise(SCSSLint::InvalidConfiguration)
55
- end
56
-
57
- it 'halts with a configuration error code' do
58
- subject.should_receive(:halt).with(:config)
59
- safe_parse
60
- end
61
- end
62
- end
63
-
64
- context 'when the excluded files flag is set' do
65
- let(:flags) { ['-e', 'file1.scss,file3.scss'] }
66
-
67
- it 'sets the :excluded_files option' do
68
- safe_parse
69
- subject.options[:excluded_files].should =~ ['file1.scss', 'file3.scss']
70
- end
71
- end
72
-
73
- context 'when the include linters flag is set' do
74
- let(:flags) { %w[-i FakeTestLinter2] }
75
-
76
- it 'enables only the included linters' do
77
- safe_parse
78
- subject.config.enabled_linters.should == [SCSSLint::Linter::FakeTestLinter2]
79
- end
80
-
81
- context 'and the included linter does not exist' do
82
- let(:flags) { %w[-i NonExistentLinter] }
83
-
84
- it 'halts with a configuration error code' do
85
- subject.should_receive(:halt).with(:config)
86
- safe_parse
87
- end
88
- end
89
- end
90
-
91
- context 'when the exclude linters flag is set' do
92
- let(:flags) { %w[-x FakeTestLinter1] }
93
-
94
- it 'includes all linters except the excluded one' do
95
- safe_parse
96
- subject.config.enabled_linters.should == \
97
- [SCSSLint::Linter::FakeTestLinter2]
98
- end
99
- end
100
-
101
- context 'when the require flag is set' do
102
- let(:flags) { %w[--require uri] }
103
-
104
- it 'requires the specified file and constants are accessible' do
105
- safe_parse
106
- expect { subject.instance_eval %{ URI } }.to_not raise_error
107
- end
108
- end
109
-
110
- context 'when neither format nor out flag is set' do
111
- let(:flags) { %w[] }
112
-
113
- it 'sets the default reporter to output to stdout' do
114
- safe_parse
115
- subject.options[:reporters].should \
116
- include([SCSSLint::Reporter::DefaultReporter, :stdout])
117
- end
118
- end
119
-
120
- context 'when the out flag is set' do
121
- context 'and the path is valid' do
122
- let(:flags) { %w[--out foo.txt] }
123
-
124
- it 'sets the default :output to the given path' do
125
- safe_parse
126
- subject.options[:reporters].should \
127
- include([SCSSLint::Reporter::DefaultReporter, 'foo.txt'])
128
- end
129
- end
130
- end
131
-
132
- context 'when the format flag is set' do
133
- context 'and the format is valid' do
134
- let(:flags) { %w[--format XML] }
135
-
136
- it 'sets the :reporter option to the correct reporter' do
137
- safe_parse
138
- subject.options[:reporters].should \
139
- include([SCSSLint::Reporter::XMLReporter, :stdout])
140
- end
141
- end
142
-
143
- context 'and an out path is specified' do
144
- let(:flags) { %w[--format XML --out foo.txt] }
145
-
146
- it 'sets the specified reporter :output to the given path' do
147
- safe_parse
148
- subject.options[:reporters].should \
149
- include([SCSSLint::Reporter::XMLReporter, 'foo.txt'])
150
- end
151
- end
152
-
153
- context 'and the format is invalid' do
154
- let(:flags) { %w[--format InvalidFormat] }
155
-
156
- it 'sets the :reporter option to the correct reporter' do
157
- subject.should_receive(:halt).with(:config)
158
- safe_parse
159
- end
160
- end
161
- end
162
-
163
- context 'when the show formatters flag is set' do
164
- let(:flags) { ['--show-formatters'] }
165
-
166
- it 'prints the formatters' do
167
- subject.should_receive(:print_formatters)
168
- safe_parse
169
- end
170
- end
171
-
172
- context 'when the show linters flag is set' do
173
- let(:flags) { ['--show-linters'] }
174
-
175
- it 'prints the linters' do
176
- subject.should_receive(:print_linters)
177
- safe_parse
178
- end
179
- end
180
-
181
- context 'when the help flag is set' do
182
- let(:flags) { ['-h'] }
183
-
184
- it 'prints a help message' do
185
- subject.should_receive(:print_help)
186
- safe_parse
187
- end
188
- end
189
-
190
- context 'when the version flag is set' do
191
- let(:flags) { ['-v'] }
192
-
193
- it 'prints the program version' do
194
- subject.should_receive(:print_version)
195
- safe_parse
196
- end
197
- end
198
-
199
- context 'when an invalid option is specified' do
200
- let(:flags) { ['--non-existant-option'] }
201
-
202
- it 'prints a help message' do
203
- subject.should_receive(:print_help)
204
- safe_parse
205
- end
206
- end
207
-
208
- context 'when no files are specified' do
209
- let(:files) { [] }
210
-
211
- it 'sets :files option to the empty list' do
212
- safe_parse
213
- subject.options[:files].should be_empty
214
- end
215
- end
216
-
217
- context 'when files are specified' do
218
- it 'sets :files option to the list of files' do
219
- safe_parse
220
- subject.options[:files].should =~ files
221
- end
222
- end
223
- end
224
-
225
30
  describe '#run' do
226
- let(:files) { ['file1.scss', 'file2.scss'] }
227
- let(:options) { {} }
228
- subject { SCSSLint::CLI.new(options) }
31
+ let(:files) { ['file1.scss', 'file2.scss'] }
32
+ let(:flags) { [] }
33
+ subject { SCSSLint::CLI.new }
229
34
 
230
35
  before do
231
36
  subject.stub(:extract_files_from).and_return(files)
232
37
  end
233
38
 
234
39
  def safe_run
235
- subject.run
40
+ subject.run(flags + files)
236
41
  rescue SystemExit
237
42
  # Keep running tests
238
43
  end
239
44
 
240
- context 'when no files are specified' do
241
- let(:files) { [] }
242
-
243
- it 'exits with a no-input status code' do
244
- subject.should_receive(:halt).with(:no_input)
245
- safe_run
246
- end
247
- end
248
-
249
- context 'when files are specified' do
250
- it 'passes the set of files to the runner' do
251
- SCSSLint::Runner.any_instance.should_receive(:run).with(files)
252
- safe_run
253
- end
254
-
255
- it 'uses the default reporter' do
256
- SCSSLint::Reporter::DefaultReporter.any_instance
257
- .should_receive(:report_lints)
258
- safe_run
259
- end
260
- end
261
-
262
45
  context 'when there are no lints' do
263
46
  before do
264
47
  SCSSLint::Runner.any_instance.stub(:lints).and_return([])
265
48
  end
266
49
 
267
- it 'exits cleanly' do
268
- subject.should_not_receive(:halt)
269
- safe_run
50
+ it 'returns a successful exit code' do
51
+ safe_run.should == 0
270
52
  end
271
53
 
272
54
  it 'outputs nothing' do
@@ -288,9 +70,8 @@ describe SCSSLint::CLI do
288
70
  ])
289
71
  end
290
72
 
291
- it 'exits cleanly' do
292
- subject.should_receive(:halt).with(:warning)
293
- safe_run
73
+ it 'returns a exit code indicating only warnings were reported' do
74
+ safe_run.should == 1
294
75
  end
295
76
 
296
77
  it 'outputs the warnings' do
@@ -313,8 +94,7 @@ describe SCSSLint::CLI do
313
94
  end
314
95
 
315
96
  it 'exits with an error status code' do
316
- subject.should_receive(:halt).with(:error)
317
- safe_run
97
+ safe_run.should == 2
318
98
  end
319
99
 
320
100
  it 'outputs the errors' do