scss-lint 0.33.0 → 0.34.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 (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