scss_lint 0.45.0 → 0.46.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c80256e0f694ade8ef5c0651dfc3cd95d1ed76a
4
- data.tar.gz: ea61d9083a223c299df36c0bc827e92716b941e5
3
+ metadata.gz: 9c199f69c02fbbb36ce36e564adaa78528fd5b49
4
+ data.tar.gz: 7372af4e048f1935dc730dfffacf89bcc4a1faed
5
5
  SHA512:
6
- metadata.gz: bdd1c5afb4507f544ec533ee8387506edcf1ad30e8466b0d01a0faaec2db376c4413fbd7b822fac687ba6c7ffd9672ad8103fdf2796a8521c6ae08815511269d
7
- data.tar.gz: 22a40913e53ebf7e82b181aca67af574a26d5a00ce10b7b090c77f7ba1756e084b038d634b0ff4dc244bd0561922a02f2a780b0f073aa4419e93a7638fea54b9
6
+ metadata.gz: dad57b1b932447052533abef8b58f2bd0288badf398b2eb946ed7bcfa8dc225e2615e7b90fce017092d5647c1057600b1e7edde12f5fd50781c358530b84e88e
7
+ data.tar.gz: 3581b2b0fb3d039346241eec26c889083fca38b0d3f1021052e94107bc0b766e636dd370af2c0e9c92fad70a9df7a5e3494c6f50f4d00005427ceb3b22fab54d
data/config/default.yml CHANGED
@@ -116,6 +116,10 @@ linters:
116
116
  PlaceholderInExtend:
117
117
  enabled: true
118
118
 
119
+ PrivateNamingConvention:
120
+ enabled: false
121
+ prefix: _
122
+
119
123
  PropertyCount:
120
124
  enabled: false
121
125
  include_nested: false
@@ -3,13 +3,22 @@ module SCSSLint
3
3
  class Linter::DuplicateProperty < Linter
4
4
  include LinterRegistry
5
5
 
6
+ def visit_root(_node)
7
+ @ignore_consecutive = config['ignore_consecutive']
8
+ yield
9
+ end
10
+
6
11
  def check_properties(node)
7
12
  static_properties(node).each_with_object({}) do |prop, prop_names|
8
13
  prop_key = property_key(prop)
9
14
 
10
15
  if existing_prop = prop_names[prop_key]
11
- add_lint(prop, "Property `#{existing_prop.name.join}` already "\
12
- "defined on line #{existing_prop.line}")
16
+ if existing_prop.line < prop.line - 1 || !ignore_consecutive_of?(prop)
17
+ add_lint(prop, "Property `#{existing_prop.name.join}` already "\
18
+ "defined on line #{existing_prop.line}")
19
+ else
20
+ prop_names[prop_key] = prop
21
+ end
13
22
  else
14
23
  prop_names[prop_key] = prop
15
24
  end
@@ -54,5 +63,21 @@ module SCSSLint
54
63
  prop.value.to_s
55
64
  end
56
65
  end
66
+
67
+ def ignore_consecutive_of?(prop)
68
+ case @ignore_consecutive
69
+ when true
70
+ return true
71
+ when false
72
+ return false
73
+ when nil
74
+ return false
75
+ when Array
76
+ return @ignore_consecutive.include?(prop.name.join)
77
+ else
78
+ raise SCSSLint::Exceptions::LinterError,
79
+ "#{@ignore_consecutive.inspect} is not a valid value for ignore_consecutive."
80
+ end
81
+ end
57
82
  end
58
83
  end
@@ -0,0 +1,157 @@
1
+ module SCSSLint
2
+ # Verifies that variables, functions, and mixins that follow the private
3
+ # naming convention are defined and used within the same file.
4
+ class Linter::PrivateNamingConvention < Linter # rubocop:disable ClassLength
5
+ include LinterRegistry
6
+
7
+ DEFINITIONS = {
8
+ Sass::Tree::FunctionNode => {
9
+ defines: Sass::Script::Tree::Funcall,
10
+ human_name: 'function',
11
+ },
12
+ Sass::Tree::MixinDefNode => {
13
+ defines: Sass::Tree::MixinNode,
14
+ human_name: 'mixin',
15
+ },
16
+ Sass::Tree::VariableNode => {
17
+ defines: Sass::Script::Tree::Variable,
18
+ human_name: 'variable',
19
+ },
20
+ }.freeze
21
+
22
+ HUMAN_NODE_NAMES = Hash[DEFINITIONS.map { |k, v| [k, v[:human_name]] }].freeze
23
+ DEFINED_BYS = Hash[DEFINITIONS.map { |k, v| [v[:defines], k] }].freeze
24
+
25
+ def visit_root(node)
26
+ # Register all top-level function, mixin, and variable definitions.
27
+ node.children.each_with_object([]) do |child_node|
28
+ if DEFINITIONS.key?(child_node.class)
29
+ register_node child_node
30
+ else
31
+ yield
32
+ end
33
+ end
34
+
35
+ # After we have visited everything, we want to see if any private things
36
+ # were defined but not used.
37
+ after_visit_all
38
+ end
39
+
40
+ def visit_script_funcall(node)
41
+ check_privacy(node)
42
+ yield # Continue linting any arguments of this function call
43
+ end
44
+
45
+ def visit_mixin(node)
46
+ check_privacy(node)
47
+ yield # Continue into content block of this mixin's block
48
+ end
49
+
50
+ def visit_script_variable(node)
51
+ check_privacy(node)
52
+ end
53
+
54
+ private
55
+
56
+ def register_node(node, node_text = node.name)
57
+ return unless private?(node)
58
+
59
+ @private_definitions ||= {}
60
+ @private_definitions[node.class] ||= {}
61
+
62
+ @private_definitions[node.class][node_text] = {
63
+ node: node,
64
+ times_used: 0,
65
+ }
66
+ end
67
+
68
+ def check_privacy(node, node_text = node.name)
69
+ return unless private?(node)
70
+
71
+ defined_by_class = defined_by(node)
72
+
73
+ # Look at top-level private definitions
74
+ if @private_definitions &&
75
+ @private_definitions[defined_by_class] &&
76
+ @private_definitions[defined_by_class][node_text]
77
+ @private_definitions[defined_by_class][node_text][:times_used] += 1
78
+ return
79
+ end
80
+
81
+ # We did not find a top-level private definition, so let's traverse up the
82
+ # tree, looking for private definitions of this node that are scoped.
83
+ looking_for = {
84
+ node: node,
85
+ defined_by: defined_by_class,
86
+ location: location_from_range(node.source_range),
87
+ }
88
+ return if node_defined_earlier_in_branch?(node.node_parent, looking_for)
89
+
90
+ node_type = humanize_node_class(node)
91
+ add_lint(
92
+ node,
93
+ "Private #{node_type} #{node_text} must be defined in the same file it is used"
94
+ )
95
+ end
96
+
97
+ def node_defined_earlier_in_branch?(node_to_look_in, looking_for)
98
+ # Look at all of the children of this node and return true if we find a
99
+ # defining node that matches in name and type.
100
+ node_to_look_in.children.each_with_object([]) do |child_node|
101
+ break unless before?(child_node, looking_for[:location])
102
+ next unless child_node.class == looking_for[:defined_by]
103
+ next unless child_node.name == looking_for[:node].name
104
+
105
+ return true # We found a match, so we are done
106
+ end
107
+
108
+ # We are at the top of the branch and don't want to check the root branch,
109
+ # since that is handled elsewhere, which means that we did not find a
110
+ # match.
111
+ return false unless node_to_look_in.node_parent.node_parent
112
+
113
+ # We did not find a match yet, and haven't reached the top of the branch,
114
+ # so recurse.
115
+ if node_to_look_in.node_parent
116
+ node_defined_earlier_in_branch?(node_to_look_in.node_parent, looking_for)
117
+ end
118
+ end
119
+
120
+ def private?(node)
121
+ node.name.start_with?(config['prefix'])
122
+ end
123
+
124
+ def before?(node, before_location)
125
+ location = location_from_range(node.source_range)
126
+ return true if location.line < before_location.line
127
+ if location.line == before_location.line &&
128
+ location.column < before_location.column
129
+ return true
130
+ end
131
+ false
132
+ end
133
+
134
+ def after_visit_all
135
+ return unless @private_definitions
136
+
137
+ @private_definitions.each do |_, nodes|
138
+ nodes.each do |node_text, node_info|
139
+ next if node_info[:times_used] > 0
140
+ node_type = humanize_node_class(node_info[:node])
141
+ add_lint(
142
+ node_info[:node],
143
+ "Private #{node_type} #{node_text} must be used in the same file it is defined"
144
+ )
145
+ end
146
+ end
147
+ end
148
+
149
+ def humanize_node_class(node)
150
+ HUMAN_NODE_NAMES[node.class]
151
+ end
152
+
153
+ def defined_by(node)
154
+ DEFINED_BYS[node.class]
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,123 @@
1
+ module SCSSLint
2
+ # Reports in TAP format.
3
+ # http://testanything.org/
4
+ class Reporter::TAPReporter < Reporter
5
+ TAP_VERSION = 'TAP version 13'.freeze
6
+
7
+ def report_lints
8
+ output = [TAP_VERSION, format_plan(files, lints)]
9
+ return format_output(output) unless files.any?
10
+
11
+ output.concat(format_files(files, lints))
12
+ format_output(output)
13
+ end
14
+
15
+ private
16
+
17
+ # @param files [Array<String>]
18
+ # @param lints [Array<SCSSLint::Lint>]
19
+ # @return [String]
20
+ def format_plan(files, lints)
21
+ files_with_lints = lints.map(&:filename).uniq
22
+ extra_lines = lints.count - files_with_lints.count
23
+ comment = files.count == 0 ? ' # No files to lint' : ''
24
+ "1..#{files.count + extra_lines}#{comment}"
25
+ end
26
+
27
+ # @param files [Array<String>]
28
+ # @param lints [Array<SCSSLint::Lint>]
29
+ # @return [Array<String>] one item per ok file or not ok lint
30
+ def format_files(files, lints)
31
+ unless lints.any?
32
+ # There are no lints, so we can take a shortcut and just output an ok
33
+ # test line for every file.
34
+ return files.map.with_index do |filename, index|
35
+ format_ok(filename, index + 1)
36
+ end
37
+ end
38
+
39
+ # There are lints, so we need to go through each file, find the lints
40
+ # for the file, and add them to the output.
41
+
42
+ # Since we'll be looking up lints by filename for each filename, we want
43
+ # to make a first pass to group all of the lints by filename to make
44
+ # lookup fast.
45
+ grouped_lints = group_lints_by_filename(lints)
46
+
47
+ test_number = 1
48
+ files.map do |filename|
49
+ if grouped_lints.key?(filename)
50
+ # This file has lints, so we want to generate a "not ok" test line for
51
+ # each failing lint.
52
+ grouped_lints[filename].map do |lint|
53
+ formatted = format_not_ok(lint, test_number)
54
+ test_number += 1
55
+ formatted
56
+ end
57
+ else
58
+ formatted = format_ok(filename, test_number)
59
+ test_number += 1
60
+ [formatted]
61
+ end
62
+ end.flatten
63
+ end
64
+
65
+ # @param lints [Array<SCSSLint::Lint>]
66
+ # @return [Hash] keyed by filename, values are arrays of lints
67
+ def group_lints_by_filename(lints)
68
+ grouped_lints = {}
69
+ lints.each do |lint|
70
+ grouped_lints[lint.filename] ||= []
71
+ grouped_lints[lint.filename] << lint
72
+ end
73
+ grouped_lints
74
+ end
75
+
76
+ # @param filename [String]
77
+ # @param test_number [Number]
78
+ # @return [String]
79
+ def format_ok(filename, test_number)
80
+ "ok #{test_number} - #{filename}"
81
+ end
82
+
83
+ # @param lint [SCSSLint::Lint]
84
+ # @param test_number [Number]
85
+ # @return [String]
86
+ def format_not_ok(lint, test_number)
87
+ location = lint.location
88
+ test_line_description = "#{lint.filename}:#{location.line}:#{location.column}"
89
+ test_line_description += " #{lint.linter.name}" if lint.linter
90
+
91
+ <<-EOS.strip
92
+ not ok #{test_number} - #{test_line_description}
93
+ ---
94
+ message: #{lint.description}
95
+ severity: #{lint.severity}
96
+ data:
97
+ file: #{lint.filename}
98
+ line: #{lint.location.line}
99
+ column: #{lint.location.column}
100
+ ---
101
+ EOS
102
+ end
103
+
104
+ # @param output [Array<String>]
105
+ # @return [String]
106
+ def format_output(output)
107
+ output.join("\n") + "\n"
108
+ end
109
+
110
+ def location(lint)
111
+ "#{log.cyan(lint.filename)}:#{log.magenta(lint.location.line.to_s)}"
112
+ end
113
+
114
+ def type(lint)
115
+ lint.error? ? log.red('[E]') : log.yellow('[W]')
116
+ end
117
+
118
+ def message(lint)
119
+ linter_name = log.green("#{lint.linter.name}: ") if lint.linter
120
+ "#{linter_name}#{lint.description}"
121
+ end
122
+ end
123
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  module SCSSLint
5
- VERSION = '0.45.0'.freeze
5
+ VERSION = '0.46.0'.freeze
6
6
  end
@@ -186,4 +186,59 @@ describe SCSSLint::Linter::DuplicateProperty do
186
186
 
187
187
  it { should report_lint line: 4 }
188
188
  end
189
+
190
+ context 'when consecutive duplicate properties are allowed' do
191
+ let(:linter_config) { { 'ignore_consecutive' => true } }
192
+
193
+ context 'when rule set contains consecutive duplicates' do
194
+ let(:scss) { <<-SCSS }
195
+ p {
196
+ background-color: #fff;
197
+ background-color: rgba(255, 255, 255, 0.7);
198
+ background-color: a-third-thing;
199
+ }
200
+ SCSS
201
+
202
+ it { should_not report_lint }
203
+ end
204
+
205
+ context 'when rule set contains non-consecutive duplicates' do
206
+ let(:scss) { <<-SCSS }
207
+ p {
208
+ margin: 0;
209
+ padding: 0;
210
+ margin: 1em;
211
+ }
212
+ SCSS
213
+
214
+ it { should report_lint line: 4 }
215
+ end
216
+ end
217
+
218
+ context 'when specific consecutive duplicate properties are allowed' do
219
+ let(:linter_config) { { 'ignore_consecutive' => ['background-color', 'transition'] } }
220
+
221
+ context 'when rule set contains consecutive duplicates in whitelist' do
222
+ let(:scss) { <<-SCSS }
223
+ p {
224
+ background-color: #fff;
225
+ background-color: rgba(255, 255, 255, 0.7);
226
+ background-color: a-third-thing;
227
+ }
228
+ SCSS
229
+
230
+ it { should_not report_lint }
231
+ end
232
+
233
+ context 'when rule set contains consecutive duplicates not in whitelist' do
234
+ let(:scss) { <<-SCSS }
235
+ p {
236
+ margin: 0;
237
+ margin: 5px;
238
+ }
239
+ SCSS
240
+
241
+ it { should report_lint line: 3 }
242
+ end
243
+ end
189
244
  end
@@ -0,0 +1,445 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Linter::PrivateNamingConvention do
4
+ context 'when a private variable' do
5
+ context 'is not used in the same file it is defined' do
6
+ let(:scss) { <<-SCSS }
7
+ $_foo: red;
8
+ SCSS
9
+
10
+ it { should report_lint line: 1 }
11
+ end
12
+
13
+ context 'is used but not defined in the same file' do
14
+ let(:scss) { <<-SCSS }
15
+ p {
16
+ color: $_foo;
17
+ background: rgba($_foo, 0);
18
+ }
19
+ SCSS
20
+
21
+ it { should report_lint line: 2 }
22
+ it { should report_lint line: 3 }
23
+ end
24
+
25
+ context 'is used but has not been defined quite yet' do
26
+ let(:scss) { <<-SCSS }
27
+ p {
28
+ color: $_foo;
29
+ background: rgba($_foo, 0);
30
+ }
31
+
32
+ $_foo: red;
33
+ SCSS
34
+
35
+ it { should report_lint line: 2 }
36
+ it { should report_lint line: 3 }
37
+ end
38
+
39
+ context 'is defined and used in the same file' do
40
+ let(:scss) { <<-SCSS }
41
+ $_foo: red;
42
+
43
+ p {
44
+ color: $_foo;
45
+ }
46
+ SCSS
47
+
48
+ it { should_not report_lint }
49
+ end
50
+
51
+ context 'is defined and used in the same file in a function' do
52
+ let(:scss) { <<-SCSS }
53
+ $_foo: red;
54
+
55
+ p {
56
+ color: rgba($_foo, 0);
57
+ }
58
+ SCSS
59
+
60
+ it { should_not report_lint }
61
+ end
62
+
63
+ context 'is defined within a selector and not used' do
64
+ let(:scss) { <<-SCSS }
65
+ p {
66
+ $_foo: red;
67
+ }
68
+ SCSS
69
+
70
+ it { should_not report_lint }
71
+ end
72
+
73
+ context 'is defined within a selector and used' do
74
+ let(:scss) { <<-SCSS }
75
+ p {
76
+ $_foo: red;
77
+ color: $_foo;
78
+ }
79
+ SCSS
80
+
81
+ it { should_not report_lint }
82
+ end
83
+
84
+ context 'is defined within a selector and used in a nested selector' do
85
+ let(:scss) { <<-SCSS }
86
+ p {
87
+ $_foo: red;
88
+
89
+ a {
90
+ color: $_foo;
91
+ }
92
+ }
93
+ SCSS
94
+
95
+ it { should_not report_lint }
96
+ end
97
+
98
+ context 'is defined within a selector but too late' do
99
+ let(:scss) { <<-SCSS }
100
+ p {
101
+ color: $_foo;
102
+ $_foo: red;
103
+ }
104
+ SCSS
105
+
106
+ it { should report_lint line: 2 }
107
+ end
108
+
109
+ context 'is defined within a selector but after the nested selector it is used in' do
110
+ let(:scss) { <<-SCSS }
111
+ p {
112
+ a {
113
+ color: $_foo;
114
+ }
115
+
116
+ $_foo: red;
117
+ }
118
+ SCSS
119
+
120
+ it { should report_lint line: 3 }
121
+ end
122
+
123
+ context 'is defined in a different selector than it is used in' do
124
+ let(:scss) { <<-SCSS }
125
+ p {
126
+ $_foo: red;
127
+ }
128
+
129
+ a {
130
+ color: $_foo;
131
+ }
132
+ SCSS
133
+
134
+ it { should report_lint line: 6 }
135
+ end
136
+ end
137
+
138
+ context 'when a public variable' do
139
+ context 'is used but not defined' do
140
+ let(:scss) { <<-SCSS }
141
+ p {
142
+ color: $foo;
143
+ }
144
+ SCSS
145
+
146
+ it { should_not report_lint }
147
+ end
148
+
149
+ context 'is defined but not used' do
150
+ let(:scss) { <<-SCSS }
151
+ $foo: red;
152
+ SCSS
153
+
154
+ it { should_not report_lint }
155
+ end
156
+ end
157
+
158
+ context 'when a private mixin' do
159
+ context 'is not used in the same file it is defined' do
160
+ let(:scss) { <<-SCSS }
161
+ @mixin _foo {
162
+ color: red;
163
+ }
164
+ SCSS
165
+
166
+ it { should report_lint line: 1 }
167
+ end
168
+
169
+ context 'is used but not defined in the same file' do
170
+ let(:scss) { <<-SCSS }
171
+ p {
172
+ @include _foo;
173
+ }
174
+ SCSS
175
+
176
+ it { should report_lint line: 2 }
177
+ end
178
+
179
+ context 'is used but has not been defined quite yet' do
180
+ let(:scss) { <<-SCSS }
181
+ p {
182
+ @include _foo;
183
+ }
184
+
185
+ @mixin _foo {
186
+ color: red;
187
+ }
188
+ SCSS
189
+
190
+ it { should report_lint line: 2 }
191
+ end
192
+
193
+ context 'is defined within a selector and not used' do
194
+ let(:scss) { <<-SCSS }
195
+ p {
196
+ @mixin _foo {
197
+ color: red;
198
+ }
199
+ }
200
+ SCSS
201
+
202
+ it { should_not report_lint }
203
+ end
204
+
205
+ context 'is defined within a selector and used within that selector' do
206
+ let(:scss) { <<-SCSS }
207
+ p {
208
+ @mixin _foo {
209
+ color: red;
210
+ }
211
+
212
+ @include _foo;
213
+ }
214
+ SCSS
215
+
216
+ it { should_not report_lint }
217
+ end
218
+
219
+ context 'is defined within a selector and used within a nested selector' do
220
+ let(:scss) { <<-SCSS }
221
+ p {
222
+ @mixin _foo {
223
+ color: red;
224
+ }
225
+
226
+ a {
227
+ @include _foo;
228
+ }
229
+ }
230
+ SCSS
231
+
232
+ it { should_not report_lint }
233
+ end
234
+
235
+ context 'is defined within a selector and used within that selector too early' do
236
+ let(:scss) { <<-SCSS }
237
+ p {
238
+ @include _foo;
239
+
240
+ @mixin _foo {
241
+ color: red;
242
+ }
243
+ }
244
+ SCSS
245
+
246
+ it { should report_lint line: 2 }
247
+ end
248
+
249
+ context 'is defined within a selector and used within a nested selector too early' do
250
+ let(:scss) { <<-SCSS }
251
+ p {
252
+ a {
253
+ @include _foo;
254
+ }
255
+
256
+ @mixin _foo {
257
+ color: red;
258
+ }
259
+ }
260
+ SCSS
261
+
262
+ it { should report_lint line: 3 }
263
+ end
264
+
265
+ context 'is defined and used in the same file' do
266
+ let(:scss) { <<-SCSS }
267
+ @mixin _foo {
268
+ color: red;
269
+ }
270
+
271
+ p {
272
+ @include _foo;
273
+ }
274
+ SCSS
275
+
276
+ it { should_not report_lint }
277
+ end
278
+ end
279
+
280
+ context 'when a public mixin' do
281
+ context 'is used but not defined' do
282
+ let(:scss) { <<-SCSS }
283
+ p {
284
+ @include foo;
285
+ }
286
+ SCSS
287
+
288
+ it { should_not report_lint }
289
+ end
290
+
291
+ context 'is defined but not used' do
292
+ let(:scss) { <<-SCSS }
293
+ @mixin foo {
294
+ color: red;
295
+ }
296
+ SCSS
297
+
298
+ it { should_not report_lint }
299
+ end
300
+ end
301
+
302
+ describe 'when a private function' do
303
+ context 'is not used in the same file it is defined' do
304
+ let(:scss) { <<-SCSS }
305
+ @function _foo() {
306
+ @return red;
307
+ }
308
+ SCSS
309
+
310
+ it { should report_lint line: 1 }
311
+ end
312
+
313
+ context 'is used but not defined in the same file' do
314
+ let(:scss) { <<-SCSS }
315
+ p {
316
+ color: _foo();
317
+ }
318
+ SCSS
319
+
320
+ it { should report_lint line: 2 }
321
+ end
322
+
323
+ context 'is used but has not been defined quite yet' do
324
+ let(:scss) { <<-SCSS }
325
+ p {
326
+ color: _foo();
327
+ }
328
+
329
+ @function _foo() {
330
+ @return red;
331
+ }
332
+ SCSS
333
+
334
+ it { should report_lint line: 2 }
335
+ end
336
+
337
+ context 'is defined within a selector and not used' do
338
+ let(:scss) { <<-SCSS }
339
+ p {
340
+ @function _foo() {
341
+ @return red;
342
+ }
343
+ }
344
+ SCSS
345
+
346
+ it { should_not report_lint }
347
+ end
348
+
349
+ context 'is defined within a selector and used within the same selector' do
350
+ let(:scss) { <<-SCSS }
351
+ p {
352
+ @function _foo() {
353
+ @return red;
354
+ }
355
+
356
+ color: _foo();
357
+ }
358
+ SCSS
359
+
360
+ it { should_not report_lint }
361
+ end
362
+
363
+ context 'is defined within a selector and used within a nested selector' do
364
+ let(:scss) { <<-SCSS }
365
+ p {
366
+ @function _foo() {
367
+ @return red;
368
+ }
369
+
370
+ a {
371
+ color: _foo();
372
+ }
373
+ }
374
+ SCSS
375
+
376
+ it { should_not report_lint }
377
+ end
378
+
379
+ context 'is defined within a selector and used within the same selector too early' do
380
+ let(:scss) { <<-SCSS }
381
+ p {
382
+ color: _foo();
383
+
384
+ @function _foo() {
385
+ @return red;
386
+ }
387
+ }
388
+ SCSS
389
+
390
+ it { should report_lint line: 2 }
391
+ end
392
+
393
+ context 'is defined within a selector and used within a nested too early' do
394
+ let(:scss) { <<-SCSS }
395
+ p {
396
+ a {
397
+ color: _foo();
398
+ }
399
+
400
+ @function _foo() {
401
+ @return red;
402
+ }
403
+ }
404
+ SCSS
405
+
406
+ it { should report_lint line: 3 }
407
+ end
408
+
409
+ context 'is defined and used in the same file' do
410
+ let(:scss) { <<-SCSS }
411
+ @function _foo() {
412
+ @return red;
413
+ }
414
+
415
+ p {
416
+ color: _foo();
417
+ }
418
+ SCSS
419
+
420
+ it { should_not report_lint }
421
+ end
422
+ end
423
+
424
+ context 'when a public function' do
425
+ context 'is used but not defined' do
426
+ let(:scss) { <<-SCSS }
427
+ p {
428
+ color: foo();
429
+ }
430
+ SCSS
431
+
432
+ it { should_not report_lint }
433
+ end
434
+
435
+ context 'is defined but not used' do
436
+ let(:scss) { <<-SCSS }
437
+ @function foo() {
438
+ @return red;
439
+ }
440
+ SCSS
441
+
442
+ it { should_not report_lint }
443
+ end
444
+ end
445
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Reporter::TAPReporter do
4
+ let(:logger) { SCSSLint::Logger.new($stdout) }
5
+ subject { described_class.new(lints, filenames, logger) }
6
+
7
+ describe '#report_lints' do
8
+ context 'when there are no files' do
9
+ let(:filenames) { [] }
10
+ let(:lints) { [] }
11
+
12
+ it 'returns the TAP version, plan, and explanation' do
13
+ subject.report_lints.should == "TAP version 13\n1..0 # No files to lint\n"
14
+ end
15
+ end
16
+
17
+ context 'when there are files but no lints' do
18
+ let(:filenames) { ['file.scss', 'another-file.scss'] }
19
+ let(:lints) { [] }
20
+
21
+ it 'returns the TAP version, plan, and ok test lines' do
22
+ subject.report_lints.should eq(<<-EOS)
23
+ TAP version 13
24
+ 1..2
25
+ ok 1 - file.scss
26
+ ok 2 - another-file.scss
27
+ EOS
28
+ end
29
+ end
30
+
31
+ context 'when there are some lints' do
32
+ let(:filenames) { %w[ok1.scss not-ok1.scss not-ok2.scss ok2.scss] }
33
+
34
+ let(:lints) do
35
+ [
36
+ SCSSLint::Lint.new(
37
+ SCSSLint::Linter::PrivateNamingConvention,
38
+ filenames[1],
39
+ SCSSLint::Location.new(123, 10, 8),
40
+ 'Description of lint 1',
41
+ :warning
42
+ ),
43
+ SCSSLint::Lint.new(
44
+ SCSSLint::Linter::PrivateNamingConvention,
45
+ filenames[2],
46
+ SCSSLint::Location.new(20, 2, 6),
47
+ 'Description of lint 2',
48
+ :error
49
+ ),
50
+ SCSSLint::Lint.new(
51
+ SCSSLint::Linter::PrivateNamingConvention,
52
+ filenames[2],
53
+ SCSSLint::Location.new(21, 3, 4),
54
+ 'Description of lint 3',
55
+ :warning
56
+ ),
57
+ ]
58
+ end
59
+
60
+ it 'returns the TAP version, plan, and correct test lines' do
61
+ subject.report_lints.should eq(<<-EOS)
62
+ TAP version 13
63
+ 1..5
64
+ ok 1 - ok1.scss
65
+ not ok 2 - not-ok1.scss:123:10 SCSSLint::Linter::PrivateNamingConvention
66
+ ---
67
+ message: Description of lint 1
68
+ severity: warning
69
+ data:
70
+ file: not-ok1.scss
71
+ line: 123
72
+ column: 10
73
+ ---
74
+ not ok 3 - not-ok2.scss:20:2 SCSSLint::Linter::PrivateNamingConvention
75
+ ---
76
+ message: Description of lint 2
77
+ severity: error
78
+ data:
79
+ file: not-ok2.scss
80
+ line: 20
81
+ column: 2
82
+ ---
83
+ not ok 4 - not-ok2.scss:21:3 SCSSLint::Linter::PrivateNamingConvention
84
+ ---
85
+ message: Description of lint 3
86
+ severity: warning
87
+ data:
88
+ file: not-ok2.scss
89
+ line: 21
90
+ column: 3
91
+ ---
92
+ ok 5 - ok2.scss
93
+ EOS
94
+ end
95
+ end
96
+ end
97
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scss_lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.45.0
4
+ version: 0.46.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brigade Engineering
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-17 00:00:00.000000000 Z
12
+ date: 2016-02-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -118,6 +118,7 @@ files:
118
118
  - lib/scss_lint/linter/name_format.rb
119
119
  - lib/scss_lint/linter/nesting_depth.rb
120
120
  - lib/scss_lint/linter/placeholder_in_extend.rb
121
+ - lib/scss_lint/linter/private_naming_convention.rb
121
122
  - lib/scss_lint/linter/property_count.rb
122
123
  - lib/scss_lint/linter/property_sort_order.rb
123
124
  - lib/scss_lint/linter/property_spelling.rb
@@ -163,6 +164,7 @@ files:
163
164
  - lib/scss_lint/reporter/default_reporter.rb
164
165
  - lib/scss_lint/reporter/files_reporter.rb
165
166
  - lib/scss_lint/reporter/json_reporter.rb
167
+ - lib/scss_lint/reporter/tap_reporter.rb
166
168
  - lib/scss_lint/runner.rb
167
169
  - lib/scss_lint/sass/script.rb
168
170
  - lib/scss_lint/sass/tree.rb
@@ -203,6 +205,7 @@ files:
203
205
  - spec/scss_lint/linter/name_format_spec.rb
204
206
  - spec/scss_lint/linter/nesting_depth_spec.rb
205
207
  - spec/scss_lint/linter/placeholder_in_extend_spec.rb
208
+ - spec/scss_lint/linter/private_naming_convention_spec.rb
206
209
  - spec/scss_lint/linter/property_count_spec.rb
207
210
  - spec/scss_lint/linter/property_sort_order_spec.rb
208
211
  - spec/scss_lint/linter/property_spelling_spec.rb
@@ -249,6 +252,7 @@ files:
249
252
  - spec/scss_lint/reporter/default_reporter_spec.rb
250
253
  - spec/scss_lint/reporter/files_reporter_spec.rb
251
254
  - spec/scss_lint/reporter/json_reporter_spec.rb
255
+ - spec/scss_lint/reporter/tap_reporter_spec.rb
252
256
  - spec/scss_lint/reporter_spec.rb
253
257
  - spec/scss_lint/runner_spec.rb
254
258
  - spec/scss_lint/selector_visitor_spec.rb
@@ -314,6 +318,7 @@ test_files:
314
318
  - spec/scss_lint/linter/name_format_spec.rb
315
319
  - spec/scss_lint/linter/nesting_depth_spec.rb
316
320
  - spec/scss_lint/linter/placeholder_in_extend_spec.rb
321
+ - spec/scss_lint/linter/private_naming_convention_spec.rb
317
322
  - spec/scss_lint/linter/property_count_spec.rb
318
323
  - spec/scss_lint/linter/property_sort_order_spec.rb
319
324
  - spec/scss_lint/linter/property_spelling_spec.rb
@@ -360,6 +365,7 @@ test_files:
360
365
  - spec/scss_lint/reporter/default_reporter_spec.rb
361
366
  - spec/scss_lint/reporter/files_reporter_spec.rb
362
367
  - spec/scss_lint/reporter/json_reporter_spec.rb
368
+ - spec/scss_lint/reporter/tap_reporter_spec.rb
363
369
  - spec/scss_lint/reporter_spec.rb
364
370
  - spec/scss_lint/runner_spec.rb
365
371
  - spec/scss_lint/selector_visitor_spec.rb