scss-lint 0.30.0 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
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