scss-lint 0.33.0 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +19 -1
  3. data/data/properties.txt +4 -0
  4. data/lib/scss_lint.rb +1 -0
  5. data/lib/scss_lint/cli.rb +4 -42
  6. data/lib/scss_lint/config.rb +1 -45
  7. data/lib/scss_lint/control_comment_processor.rb +47 -15
  8. data/lib/scss_lint/file_finder.rb +57 -0
  9. data/lib/scss_lint/linter/bang_format.rb +1 -1
  10. data/lib/scss_lint/linter/border_zero.rb +25 -9
  11. data/lib/scss_lint/linter/color_keyword.rb +3 -13
  12. data/lib/scss_lint/linter/color_variable.rb +36 -0
  13. data/lib/scss_lint/linter/declaration_order.rb +2 -2
  14. data/lib/scss_lint/linter/important_rule.rb +12 -0
  15. data/lib/scss_lint/linter/indentation.rb +7 -1
  16. data/lib/scss_lint/linter/property_count.rb +44 -0
  17. data/lib/scss_lint/linter/property_sort_order.rb +73 -19
  18. data/lib/scss_lint/linter/string_quotes.rb +9 -0
  19. data/lib/scss_lint/linter/variable_for_property.rb +20 -0
  20. data/lib/scss_lint/linter/vendor_prefixes.rb +3 -3
  21. data/lib/scss_lint/runner.rb +5 -7
  22. data/lib/scss_lint/utils.rb +34 -0
  23. data/lib/scss_lint/version.rb +1 -1
  24. data/spec/scss_lint/cli_spec.rb +1 -1
  25. data/spec/scss_lint/config_spec.rb +4 -203
  26. data/spec/scss_lint/engine_spec.rb +4 -4
  27. data/spec/scss_lint/file_finder_spec.rb +110 -0
  28. data/spec/scss_lint/linter/bang_format_spec.rb +28 -18
  29. data/spec/scss_lint/linter/border_zero_spec.rb +50 -16
  30. data/spec/scss_lint/linter/color_keyword_spec.rb +16 -16
  31. data/spec/scss_lint/linter/color_variable_spec.rb +102 -0
  32. data/spec/scss_lint/linter/comment_spec.rb +9 -9
  33. data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +10 -10
  34. data/spec/scss_lint/linter/debug_statement_spec.rb +4 -4
  35. data/spec/scss_lint/linter/declaration_order_spec.rb +80 -80
  36. data/spec/scss_lint/linter/duplicate_property_spec.rb +30 -30
  37. data/spec/scss_lint/linter/else_placement_spec.rb +16 -16
  38. data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +38 -38
  39. data/spec/scss_lint/linter/empty_rule_spec.rb +4 -4
  40. data/spec/scss_lint/linter/final_newline_spec.rb +6 -6
  41. data/spec/scss_lint/linter/hex_length_spec.rb +16 -16
  42. data/spec/scss_lint/linter/hex_notation_spec.rb +16 -16
  43. data/spec/scss_lint/linter/hex_validation_spec.rb +6 -6
  44. data/spec/scss_lint/linter/id_selector_spec.rb +10 -10
  45. data/spec/scss_lint/linter/import_path_spec.rb +45 -45
  46. data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
  47. data/spec/scss_lint/linter/indentation_spec.rb +103 -43
  48. data/spec/scss_lint/linter/leading_zero_spec.rb +45 -45
  49. data/spec/scss_lint/linter/mergeable_selector_spec.rb +32 -32
  50. data/spec/scss_lint/linter/name_format_spec.rb +75 -41
  51. data/spec/scss_lint/linter/nesting_depth_spec.rb +14 -14
  52. data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +12 -12
  53. data/spec/scss_lint/linter/property_count_spec.rb +104 -0
  54. data/spec/scss_lint/linter/property_sort_order_spec.rb +138 -48
  55. data/spec/scss_lint/linter/property_spelling_spec.rb +14 -14
  56. data/spec/scss_lint/linter/qualifying_element_spec.rb +26 -26
  57. data/spec/scss_lint/linter/selector_depth_spec.rb +26 -26
  58. data/spec/scss_lint/linter/selector_format_spec.rb +114 -114
  59. data/spec/scss_lint/linter/shorthand_spec.rb +32 -32
  60. data/spec/scss_lint/linter/single_line_per_property_spec.rb +10 -10
  61. data/spec/scss_lint/linter/single_line_per_selector_spec.rb +24 -24
  62. data/spec/scss_lint/linter/space_after_comma_spec.rb +60 -60
  63. data/spec/scss_lint/linter/space_after_property_colon_spec.rb +44 -44
  64. data/spec/scss_lint/linter/space_after_property_name_spec.rb +6 -6
  65. data/spec/scss_lint/linter/space_before_brace_spec.rb +119 -119
  66. data/spec/scss_lint/linter/space_between_parens_spec.rb +48 -48
  67. data/spec/scss_lint/linter/string_quotes_spec.rb +74 -62
  68. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +53 -54
  69. data/spec/scss_lint/linter/trailing_zero_spec.rb +34 -34
  70. data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +10 -10
  71. data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +18 -18
  72. data/spec/scss_lint/linter/url_format_spec.rb +4 -4
  73. data/spec/scss_lint/linter/url_quotes_spec.rb +14 -14
  74. data/spec/scss_lint/linter/variable_for_property_spec.rb +115 -0
  75. data/spec/scss_lint/linter/vendor_prefixes_spec.rb +66 -56
  76. data/spec/scss_lint/linter/zero_unit_spec.rb +22 -22
  77. data/spec/scss_lint/linter_spec.rb +72 -28
  78. data/spec/scss_lint/runner_spec.rb +0 -1
  79. data/spec/scss_lint/selector_visitor_spec.rb +23 -23
  80. data/spec/spec_helper.rb +2 -2
  81. metadata +27 -12
@@ -4,11 +4,9 @@ module SCSSLint
4
4
  class Linter::ColorKeyword < Linter
5
5
  include LinterRegistry
6
6
 
7
- COLOR_REGEX = /(#?[a-f0-9]{3,6}|[a-z]+)/i
8
-
9
7
  def visit_script_color(node)
10
- color = source_from_range(node.source_range)[COLOR_REGEX, 1]
11
- add_color_lint(node, color) if color_keyword?(color)
8
+ word = source_from_range(node.source_range)[/([a-z]+)/i, 1]
9
+ add_color_lint(node, word) if color_keyword?(word)
12
10
  end
13
11
 
14
12
  def visit_script_string(node)
@@ -22,7 +20,7 @@ module SCSSLint
22
20
  private
23
21
 
24
22
  def add_color_lint(node, original)
25
- hex_form = Sass::Script::Value::Color.new(color_rgb(original)).tap do |color|
23
+ hex_form = Sass::Script::Value::Color.new(color_keyword_to_code(original)).tap do |color|
26
24
  color.options = {} # `inspect` requires options to be set
27
25
  end.inspect
28
26
 
@@ -30,13 +28,5 @@ module SCSSLint
30
28
  "Color `#{original}` should be written in hexadecimal form " \
31
29
  "as `#{hex_form}`")
32
30
  end
33
-
34
- def color_keyword?(string)
35
- color_rgb(string) && string != 'transparent'
36
- end
37
-
38
- def color_rgb(string)
39
- Sass::Script::Value::Color::COLOR_NAMES[string]
40
- end
41
31
  end
42
32
  end
@@ -0,0 +1,36 @@
1
+ module SCSSLint
2
+ # Ensures color literals are used only in variable declarations.
3
+ class Linter::ColorVariable < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_script_color(node)
7
+ return if in_variable_declaration?(node)
8
+
9
+ # Source range sometimes includes closing parenthesis, so extract it
10
+ color = source_from_range(node.source_range)[/(#?[a-z0-9]+)/i, 1]
11
+
12
+ record_lint(node, color)
13
+ end
14
+
15
+ def visit_script_string(node)
16
+ remove_quoted_strings(node.value)
17
+ .scan(/(^|\s)(#[a-f0-9]+|[a-z]+)(?=\s|$)/i)
18
+ .select { |_, word| color?(word) }
19
+ .each { |_, color| record_lint(node, color) }
20
+ end
21
+
22
+ private
23
+
24
+ def record_lint(node, color)
25
+ add_lint node, "Color literals like `#{color}` should only be used in " \
26
+ 'variable declarations; they should be referred to via ' \
27
+ 'variable everywhere else.'
28
+ end
29
+
30
+ def in_variable_declaration?(node)
31
+ parent = node.node_parent
32
+ parent.is_a?(Sass::Script::Tree::Literal) &&
33
+ parent.node_parent.is_a?(Sass::Tree::VariableNode)
34
+ end
35
+ end
36
+ end
@@ -15,8 +15,8 @@ module SCSSLint
15
15
 
16
16
  MESSAGE =
17
17
  'Rule sets should be ordered as follows: '\
18
- '@extends, @includes without @content, ' \
19
- 'properties, @includes with @content, ' \
18
+ '`@extends`, `@includes` without `@content`, ' \
19
+ 'properties, `@includes` with `@content`, ' \
20
20
  'nested rule sets'
21
21
 
22
22
  MIXIN_WITH_CONTENT = 'mixin_with_content'
@@ -0,0 +1,12 @@
1
+ module SCSSLint
2
+ # Reports the use of !important in properties.
3
+ class Linter::ImportantRule < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_prop(node)
7
+ return unless source_from_range(node.source_range).include?('!important')
8
+
9
+ add_lint(node, '!important should not be used')
10
+ end
11
+ end
12
+ end
@@ -49,7 +49,7 @@ module SCSSLint
49
49
  return true
50
50
  end
51
51
 
52
- unless actual_indent.length == @indent
52
+ unless allow_arbitrary_indent?(node) || actual_indent.length == @indent
53
53
  add_lint(node.line,
54
54
  "Line should be indented #{@indent} #{character_name}s, " \
55
55
  "but was indented #{actual_indent.length} #{character_name}s")
@@ -130,5 +130,11 @@ module SCSSLint
130
130
 
131
131
  same_position?(node.source_range.end_pos, first_child_source.start_pos)
132
132
  end
133
+
134
+ def allow_arbitrary_indent?(node)
135
+ @indent == 0 &&
136
+ config['allow_non_nested_indentation'] &&
137
+ node.is_a?(Sass::Tree::RuleNode)
138
+ end
133
139
  end
134
140
  end
@@ -0,0 +1,44 @@
1
+ module SCSSLint
2
+ # Checks that the number of properties in a rule set is under a defined limit.
3
+ class Linter::PropertyCount < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_root(_node)
7
+ @property_count = {} # Lookup table of counts for rule sets
8
+ @max = config['max_properties']
9
+ yield # Continue linting children
10
+ end
11
+
12
+ def visit_rule(node)
13
+ count = property_count(node)
14
+
15
+ if count > @max
16
+ add_lint node,
17
+ "Rule set contains (#{count}/#{@max}) properties" \
18
+ "#{' (including properties in nested rule sets)' if config['include_nested']}"
19
+
20
+ # Don't lint nested rule sets as we already have them in the count
21
+ return if config['include_nested']
22
+ end
23
+
24
+ yield # Lint nested rule sets
25
+ end
26
+
27
+ private
28
+
29
+ def property_count(rule_node)
30
+ @property_count[rule_node] ||=
31
+ begin
32
+ count = rule_node.children.count { |node| node.is_a?(Sass::Tree::PropNode) }
33
+
34
+ if config['include_nested']
35
+ count += rule_node.children.inject(0) do |sum, node|
36
+ node.is_a?(Sass::Tree::RuleNode) ? sum + property_count(node) : sum
37
+ end
38
+ end
39
+
40
+ count
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,48 +1,102 @@
1
1
  module SCSSLint
2
2
  # Checks the declaration order of properties.
3
- class Linter::PropertySortOrder < Linter
3
+ class Linter::PropertySortOrder < Linter # rubocop:disable ClassLength
4
4
  include LinterRegistry
5
5
 
6
6
  def visit_root(_node)
7
7
  @preferred_order = extract_preferred_order_from_config
8
- yield
8
+
9
+ if @preferred_order && config['separate_groups']
10
+ @group = assign_groups(@preferred_order)
11
+ end
12
+
13
+ yield # Continue linting children
9
14
  end
10
15
 
11
- def check_sort_order(node)
16
+ def check_order(node)
12
17
  sortable_props = node.children.select do |child|
13
18
  child.is_a?(Sass::Tree::PropNode) && !ignore_property?(child)
14
19
  end
15
20
 
16
21
  sortable_prop_info = sortable_props
17
- .map { |child| child.name.join }
18
- .map do |name|
22
+ .map do |child|
23
+ name = child.name.join
19
24
  /^(?<vendor>-\w+(-osx)?-)?(?<property>.+)/ =~ name
20
- { name: name, vendor: vendor, property: property }
25
+ { name: name, vendor: vendor, property: property, node: child }
26
+ end
27
+
28
+ if sortable_props.any?
29
+ check_sort_order(sortable_prop_info)
30
+ check_group_separation(sortable_prop_info) if @group
31
+ end
32
+
33
+ yield # Continue linting children
34
+ end
35
+
36
+ alias_method :visit_media, :check_order
37
+ alias_method :visit_mixin, :check_order
38
+ alias_method :visit_rule, :check_order
39
+
40
+ def visit_if(node, &block)
41
+ check_order(node, &block)
42
+ visit(node.else) if node.else
43
+ end
44
+
45
+ private
46
+
47
+ # When enforcing whether a blank line should separate "groups" of
48
+ # properties, we need to assign those properties to group numbers so we can
49
+ # quickly tell traversing from one property to the other that a blank line
50
+ # is required (since the group number would change).
51
+ def assign_groups(order)
52
+ group_number = 0
53
+ last_was_empty = false
54
+
55
+ order.each_with_object({}) do |property, group|
56
+ # A gap indicates the start of the next group
57
+ if property.nil? || property.strip.empty?
58
+ group_number += 1 unless last_was_empty # Treat multiple gaps as single gap
59
+ last_was_empty = true
60
+ next
21
61
  end
22
62
 
63
+ last_was_empty = false
64
+
65
+ group[property] = group_number
66
+ end
67
+ end
68
+
69
+ def check_sort_order(sortable_prop_info)
23
70
  sorted_props = sortable_prop_info
24
71
  .sort { |a, b| compare_properties(a, b) }
25
72
 
26
73
  sorted_props.each_with_index do |prop, index|
27
74
  next unless prop != sortable_prop_info[index]
28
75
 
29
- add_lint(sortable_props[index], lint_message(sorted_props))
76
+ add_lint(sortable_prop_info[index][:node], lint_message(sorted_props))
30
77
  break
31
78
  end
32
-
33
- yield # Continue linting children
34
79
  end
35
80
 
36
- alias_method :visit_media, :check_sort_order
37
- alias_method :visit_mixin, :check_sort_order
38
- alias_method :visit_rule, :check_sort_order
81
+ def check_group_separation(sortable_prop_info) # rubocop:disable AbcSize
82
+ group_number = @group[sortable_prop_info.first[:property]]
39
83
 
40
- def visit_if(node, &block)
41
- check_sort_order(node, &block)
42
- visit(node.else) if node.else
43
- end
84
+ sortable_prop_info[0..-2].zip(sortable_prop_info[1..-1]).each do |first, second|
85
+ next unless @group[second[:property]] != group_number
44
86
 
45
- private
87
+ # We're now in the next group
88
+ group_number = @group[second[:property]]
89
+
90
+ # The group number has changed, so ensure this property is separated
91
+ # from the previous property by at least a line (could be a comment,
92
+ # we don't care, but at least one line that isn't another property).
93
+ next if first[:node].line < second[:node].line - 1
94
+
95
+ add_lint second[:node], "Property #{second[:name]} should be " \
96
+ 'separated from the previous group of ' \
97
+ "properties ending with #{first[:name]}"
98
+ end
99
+ end
46
100
 
47
101
  # Compares two properties which can contain a vendor prefix. It allows for a
48
102
  # sort order like:
@@ -100,11 +154,11 @@ module SCSSLint
100
154
  "#{config['order']}.txt"))
101
155
  file.read.split("\n").reject { |line| line =~ /^(#|\s*$)/ }
102
156
  rescue Errno::ENOENT
103
- raise SCSSLint::LinterError,
157
+ raise SCSSLint::Exceptions::LinterError,
104
158
  "Preset property sort order '#{config['order']}' does not exist"
105
159
  end
106
160
  else
107
- raise SCSSLint::LinterError,
161
+ raise SCSSLint::Exceptions::LinterError,
108
162
  'Invalid property sort order specified -- must be the name of a '\
109
163
  'preset or an array of strings'
110
164
  end
@@ -3,6 +3,15 @@ module SCSSLint
3
3
  class Linter::StringQuotes < Linter
4
4
  include LinterRegistry
5
5
 
6
+ def visit_comment(_node)
7
+ # Sass allows you to write Sass Script in non-silent comments (/* ... */).
8
+ # Unfortunately, it doesn't report correct source ranges for these script
9
+ # nodes.
10
+ # It's unlikely that a developer wanted to lint the script they wrote in a
11
+ # comment, so just ignore this case entirely and stop traversing the
12
+ # children of comment nodes.
13
+ end
14
+
6
15
  def visit_script_stringinterpolation(node)
7
16
  # We can't statically determine what the resultant string looks like when
8
17
  # string interpolation is used, e.g. "one #{$var} three" could be a very
@@ -0,0 +1,20 @@
1
+ module SCSSLint
2
+ # Reports the use of literals for properties where variables are prefered.
3
+ class Linter::VariableForProperty < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_root(_node)
7
+ @properties = Set.new(config['properties'])
8
+ yield if @properties.any?
9
+ end
10
+
11
+ def visit_prop(node)
12
+ property_name = node.name.join
13
+ return unless @properties.include? property_name
14
+ return if node.children.first.is_a?(Sass::Script::Tree::Variable)
15
+
16
+ add_lint(node, "Property #{property_name} should use " \
17
+ 'a variable rather than a literal value')
18
+ end
19
+ end
20
+ end
@@ -16,8 +16,8 @@ module SCSSLint
16
16
  check_identifier(node, name.gsub(/^@/, ''))
17
17
 
18
18
  # Check for values
19
- return unless node.respond_to?(:value) && node.value.respond_to?(:to_sass)
20
- check_identifier(node, node.value.to_sass)
19
+ return unless node.respond_to?(:value) && node.value.respond_to?(:source_range)
20
+ check_identifier(node, source_from_range(node.value.source_range))
21
21
  end
22
22
 
23
23
  alias_method :visit_prop, :check_node
@@ -31,7 +31,7 @@ module SCSSLint
31
31
 
32
32
  # Strip vendor prefix to check against identifiers.
33
33
  # (Also strip closing parentheticals from values like linear-gradient.)
34
- stripped_identifier = identifier.gsub(/(^[_-][a-zA-Z0-9_]+-|\(.*\))/, '')
34
+ stripped_identifier = identifier.gsub(/(^[_-][a-zA-Z0-9_]+-|\(.*\)|;)/, '').strip
35
35
  return if @exclusions.include?(stripped_identifier)
36
36
  return unless @identifiers.include?(stripped_identifier)
37
37
 
@@ -31,12 +31,10 @@ module SCSSLint
31
31
  # @param file [String]
32
32
  def find_lints(file)
33
33
  engine = Engine.new(file)
34
- config = @config.preferred ? @config : Config.for_file(file)
35
- config ||= @config
36
34
 
37
35
  @linters.each do |linter|
38
36
  begin
39
- run_linter(linter, engine, config, file)
37
+ run_linter(linter, engine, file)
40
38
  rescue => error
41
39
  raise SCSSLint::Exceptions::LinterError,
42
40
  "#{linter.class} raised unexpected error linting file #{file}: " \
@@ -52,10 +50,10 @@ module SCSSLint
52
50
  end
53
51
 
54
52
  # For stubbing in tests.
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)
58
- linter.run(engine, config.linter_options(linter))
53
+ def run_linter(linter, engine, file)
54
+ return unless @config.linter_enabled?(linter)
55
+ return if @config.excluded_file_for_linter?(file, linter)
56
+ linter.run(engine, @config.linter_options(linter))
59
57
  end
60
58
  end
61
59
  end
@@ -1,6 +1,40 @@
1
1
  module SCSSLint
2
2
  # Collection of helpers used across a variety of linters.
3
3
  module Utils
4
+ COLOR_REGEX = /^#[a-f0-9]{3,6}$/i
5
+
6
+ # Returns whether the given string is a color literal (keyword or hex code).
7
+ #
8
+ # @param string [String]
9
+ # @return [true,false]
10
+ def color?(string)
11
+ color_keyword?(string) || color_hex?(string)
12
+ end
13
+
14
+ # Returns whether the given string is a color hexadecimal code.
15
+ #
16
+ # @param string [String]
17
+ # @return [true,false]
18
+ def color_hex?(string)
19
+ string =~ COLOR_REGEX
20
+ end
21
+
22
+ # Returns whether the given string is a valid color keyword.
23
+ #
24
+ # @param string [String]
25
+ # @return [true,false]
26
+ def color_keyword?(string)
27
+ color_keyword_to_code(string) && string != 'transparent'
28
+ end
29
+
30
+ # Returns the hexadecimal code for the given color keyword.
31
+ #
32
+ # @param string [String]
33
+ # @return [String] 7-character hexadecimal code (includes `#` prefix)
34
+ def color_keyword_to_code(string)
35
+ Sass::Script::Value::Color::COLOR_NAMES[string]
36
+ end
37
+
4
38
  # Given a selector array which is a list of strings with Sass::Script::Nodes
5
39
  # interspersed within them, return an array of strings representing those
6
40
  # selectors with the Sass::Script::Nodes removed (i.e., ignoring
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module SCSSLint
3
- VERSION = '0.33.0'
3
+ VERSION = '0.34.0'
4
4
  end
@@ -33,7 +33,7 @@ describe SCSSLint::CLI do
33
33
  subject { SCSSLint::CLI.new }
34
34
 
35
35
  before do
36
- subject.stub(:extract_files_from).and_return(files)
36
+ SCSSLint::FileFinder.any_instance.stub(:find).and_return(files)
37
37
  end
38
38
 
39
39
  def safe_run
@@ -62,7 +62,7 @@ describe SCSSLint::Config do
62
62
  let(:config_file) { '' }
63
63
 
64
64
  it 'returns the default configuration' do
65
- subject.should == described_class.default
65
+ subject.options.should == described_class.default.options
66
66
  end
67
67
  end
68
68
 
@@ -70,7 +70,7 @@ describe SCSSLint::Config do
70
70
  let(:config_file) { '# This is a comment' }
71
71
 
72
72
  it 'returns the default configuration' do
73
- subject.should == described_class.default
73
+ subject.options.should == described_class.default.options
74
74
  end
75
75
  end
76
76
 
@@ -88,7 +88,7 @@ describe SCSSLint::Config do
88
88
  let(:config_file) { default_file }
89
89
 
90
90
  it 'returns a configuration equivalent to the default' do
91
- subject.should == described_class.default
91
+ subject.options.should == described_class.default.options
92
92
  end
93
93
  end
94
94
 
@@ -100,159 +100,7 @@ describe SCSSLint::Config do
100
100
  FILE
101
101
 
102
102
  it 'returns a configuration equivalent to the default' do
103
- subject.should == described_class.default
104
- end
105
- end
106
-
107
- context 'with a file that includes another configuration file' do
108
- let(:included_file_path) { '../included_file.yml' }
109
-
110
- let(:config_file) { <<-FILE }
111
- inherit_from: #{included_file_path}
112
-
113
- linters:
114
- FakeConfigLinter:
115
- enabled: true
116
- some_other_option: some_other_value
117
- FILE
118
-
119
- before do
120
- described_class.stub(:load_file_contents)
121
- .with("#{config_dir}/" + included_file_path)
122
- .and_return(included_file)
123
- end
124
-
125
- context 'and the included file has a different setting from the default' do
126
- let(:included_file) { <<-FILE }
127
- linters:
128
- OtherFakeConfigLinter:
129
- enabled: true
130
- some_option: some_value
131
- FILE
132
-
133
- it 'reflects the different setting of the included file' do
134
- subject.options['linters']['OtherFakeConfigLinter']
135
- .should == { 'enabled' => true, 'some_option' => 'some_value' }
136
- end
137
-
138
- it 'reflects the different setting of the file that included the file' do
139
- subject.options['linters']['FakeConfigLinter']
140
- .should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
141
- end
142
- end
143
-
144
- context 'and the included file has the same setting as the default' do
145
- let(:included_file) { <<-FILE }
146
- linters:
147
- OtherFakeConfigLinter:
148
- enabled: false
149
- FILE
150
-
151
- it 'does not alter the default configuration' do
152
- subject.options['linters']['OtherFakeConfigLinter']
153
- .should == { 'enabled' => false }
154
- end
155
-
156
- it 'reflects the different setting of the file that included the file' do
157
- subject.options['linters']['FakeConfigLinter']
158
- .should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
159
- end
160
- end
161
-
162
- context 'and the included file includes another file' do
163
- let(:other_included_file_path) { '/some/abs/other_included_file.yml' }
164
-
165
- let(:other_included_file) { <<-FILE }
166
- linters:
167
- OtherFakeConfigLinter:
168
- yet_another_option: yet_another_value
169
- FILE
170
-
171
- let(:included_file) { <<-FILE }
172
- inherit_from: #{other_included_file_path}
173
-
174
- linters:
175
- OtherFakeConfigLinter:
176
- enabled: true
177
- some_option: some_value
178
- FILE
179
-
180
- before do
181
- described_class.stub(:load_file_contents)
182
- .with(other_included_file_path)
183
- .and_return(other_included_file)
184
- end
185
-
186
- it "reflects the different setting of the included file's included file" do
187
- subject.options['linters']['OtherFakeConfigLinter']
188
- .should == {
189
- 'enabled' => true,
190
- 'some_option' => 'some_value',
191
- 'yet_another_option' => 'yet_another_value',
192
- }
193
- end
194
-
195
- it 'reflects the different setting of the file that included the file' do
196
- subject.options['linters']['FakeConfigLinter']
197
- .should == { 'enabled' => true, 'some_other_option' => 'some_other_value' }
198
- end
199
- end
200
- end
201
-
202
- context 'with a file that includes multiple configuration files' do
203
- let(:included_file_path) { '../included_file.yml' }
204
- let(:other_included_file_path) { '/some/dir/other_included_file.yml' }
205
-
206
- let(:config_file) { <<-FILE }
207
- inherit_from:
208
- - #{included_file_path}
209
- - #{other_included_file_path}
210
-
211
- linters:
212
- FakeConfigLinter:
213
- enabled: true
214
- some_other_option: some_other_value
215
- FILE
216
-
217
- before do
218
- described_class.stub(:load_file_contents)
219
- .with("#{config_dir}/" + included_file_path)
220
- .and_return(included_file)
221
-
222
- described_class.stub(:load_file_contents)
223
- .with(other_included_file_path)
224
- .and_return(other_included_file)
225
- end
226
-
227
- context 'and the included files have settings different from each other' do
228
- let(:included_file) { <<-FILE }
229
- linters:
230
- OtherFakeConfigLinter:
231
- enabled: true
232
- some_option: earlier_value
233
- some_other_option: value
234
- FILE
235
-
236
- let(:other_included_file) { <<-FILE }
237
- linters:
238
- OtherFakeConfigLinter:
239
- enabled: true
240
- some_option: later_value
241
- FILE
242
-
243
- it 'uses the value of the file that was included last' do
244
- subject.options['linters']['OtherFakeConfigLinter']['some_option']
245
- .should == 'later_value'
246
- end
247
-
248
- it 'loads settings from both included files' do
249
- subject.options['linters']['OtherFakeConfigLinter']
250
- .should == {
251
- 'enabled' => true,
252
- 'some_option' => 'later_value',
253
- 'some_other_option' => 'value',
254
- }
255
- end
103
+ subject.options.should == described_class.default.options
256
104
  end
257
105
  end
258
106
 
@@ -284,53 +132,6 @@ describe SCSSLint::Config do
284
132
  end
285
133
  end
286
134
 
287
- describe '.for_file' do
288
- include_context 'isolated environment'
289
-
290
- let(:linted_file) { File.join('foo', 'bar', 'baz', 'file-being-linted.scss') }
291
- subject { described_class.for_file(linted_file) }
292
-
293
- before do
294
- described_class.instance_variable_set(:@dir_to_config, nil) # Clear cache
295
- FileUtils.mkpath(File.dirname(linted_file))
296
- FileUtils.touch(linted_file)
297
- end
298
-
299
- context 'when there are no configuration files in the directory hierarchy' do
300
- it { should be_nil }
301
- end
302
-
303
- context 'when there is a configuration file in the same directory' do
304
- let(:config_file) { File.join('foo', 'bar', 'baz', '.scss-lint.yml') }
305
- before { FileUtils.touch(config_file) }
306
-
307
- it 'loads that configuration file' do
308
- described_class.should_receive(:load).with(File.expand_path(config_file))
309
- subject
310
- end
311
- end
312
-
313
- context 'when there is a configuration file in the parent directory' do
314
- let(:config_file) { File.join('foo', 'bar', '.scss-lint.yml') }
315
- before { FileUtils.touch(config_file) }
316
-
317
- it 'loads that configuration file' do
318
- described_class.should_receive(:load).with(File.expand_path(config_file))
319
- subject
320
- end
321
- end
322
-
323
- context 'when there is a configuration file in some ancestor directory' do
324
- let(:config_file) { File.join('foo', '.scss-lint.yml') }
325
- before { FileUtils.touch(config_file) }
326
-
327
- it 'loads that configuration file' do
328
- described_class.should_receive(:load).with(File.expand_path(config_file))
329
- subject
330
- end
331
- end
332
- end
333
-
334
135
  describe '#linter_options' do
335
136
  let(:config) { described_class.new(options) }
336
137