scss_lint 0.38.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 +7 -0
- data/bin/scss-lint +6 -0
- data/config/default.yml +205 -0
- data/data/prefixed-identifiers/base.txt +107 -0
- data/data/prefixed-identifiers/bourbon.txt +71 -0
- data/data/properties.txt +477 -0
- data/data/property-sort-orders/concentric.txt +134 -0
- data/data/property-sort-orders/recess.txt +149 -0
- data/data/property-sort-orders/smacss.txt +137 -0
- data/lib/scss_lint.rb +31 -0
- data/lib/scss_lint/cli.rb +215 -0
- data/lib/scss_lint/config.rb +251 -0
- data/lib/scss_lint/constants.rb +8 -0
- data/lib/scss_lint/control_comment_processor.rb +126 -0
- data/lib/scss_lint/engine.rb +56 -0
- data/lib/scss_lint/exceptions.rb +21 -0
- data/lib/scss_lint/file_finder.rb +68 -0
- data/lib/scss_lint/lint.rb +24 -0
- data/lib/scss_lint/linter.rb +161 -0
- data/lib/scss_lint/linter/bang_format.rb +52 -0
- data/lib/scss_lint/linter/border_zero.rb +39 -0
- data/lib/scss_lint/linter/color_keyword.rb +32 -0
- data/lib/scss_lint/linter/color_variable.rb +60 -0
- data/lib/scss_lint/linter/comment.rb +21 -0
- data/lib/scss_lint/linter/compass.rb +7 -0
- data/lib/scss_lint/linter/compass/property_with_mixin.rb +47 -0
- data/lib/scss_lint/linter/debug_statement.rb +10 -0
- data/lib/scss_lint/linter/declaration_order.rb +71 -0
- data/lib/scss_lint/linter/duplicate_property.rb +58 -0
- data/lib/scss_lint/linter/else_placement.rb +48 -0
- data/lib/scss_lint/linter/empty_line_between_blocks.rb +85 -0
- data/lib/scss_lint/linter/empty_rule.rb +11 -0
- data/lib/scss_lint/linter/final_newline.rb +20 -0
- data/lib/scss_lint/linter/hex_length.rb +56 -0
- data/lib/scss_lint/linter/hex_notation.rb +38 -0
- data/lib/scss_lint/linter/hex_validation.rb +23 -0
- data/lib/scss_lint/linter/id_selector.rb +10 -0
- data/lib/scss_lint/linter/import_path.rb +62 -0
- data/lib/scss_lint/linter/important_rule.rb +12 -0
- data/lib/scss_lint/linter/indentation.rb +197 -0
- data/lib/scss_lint/linter/leading_zero.rb +49 -0
- data/lib/scss_lint/linter/mergeable_selector.rb +60 -0
- data/lib/scss_lint/linter/name_format.rb +117 -0
- data/lib/scss_lint/linter/nesting_depth.rb +24 -0
- data/lib/scss_lint/linter/placeholder_in_extend.rb +22 -0
- data/lib/scss_lint/linter/property_count.rb +44 -0
- data/lib/scss_lint/linter/property_sort_order.rb +198 -0
- data/lib/scss_lint/linter/property_spelling.rb +49 -0
- data/lib/scss_lint/linter/property_units.rb +59 -0
- data/lib/scss_lint/linter/qualifying_element.rb +42 -0
- data/lib/scss_lint/linter/selector_depth.rb +64 -0
- data/lib/scss_lint/linter/selector_format.rb +102 -0
- data/lib/scss_lint/linter/shorthand.rb +139 -0
- data/lib/scss_lint/linter/single_line_per_property.rb +59 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +35 -0
- data/lib/scss_lint/linter/space_after_comma.rb +110 -0
- data/lib/scss_lint/linter/space_after_property_colon.rb +92 -0
- data/lib/scss_lint/linter/space_after_property_name.rb +27 -0
- data/lib/scss_lint/linter/space_before_brace.rb +72 -0
- data/lib/scss_lint/linter/space_between_parens.rb +35 -0
- data/lib/scss_lint/linter/string_quotes.rb +94 -0
- data/lib/scss_lint/linter/trailing_semicolon.rb +67 -0
- data/lib/scss_lint/linter/trailing_zero.rb +41 -0
- data/lib/scss_lint/linter/unnecessary_mantissa.rb +42 -0
- data/lib/scss_lint/linter/unnecessary_parent_reference.rb +49 -0
- data/lib/scss_lint/linter/url_format.rb +56 -0
- data/lib/scss_lint/linter/url_quotes.rb +27 -0
- data/lib/scss_lint/linter/variable_for_property.rb +30 -0
- data/lib/scss_lint/linter/vendor_prefix.rb +64 -0
- data/lib/scss_lint/linter/zero_unit.rb +39 -0
- data/lib/scss_lint/linter_registry.rb +26 -0
- data/lib/scss_lint/location.rb +38 -0
- data/lib/scss_lint/options.rb +109 -0
- data/lib/scss_lint/rake_task.rb +106 -0
- data/lib/scss_lint/reporter.rb +18 -0
- data/lib/scss_lint/reporter/config_reporter.rb +26 -0
- data/lib/scss_lint/reporter/default_reporter.rb +27 -0
- data/lib/scss_lint/reporter/files_reporter.rb +8 -0
- data/lib/scss_lint/reporter/json_reporter.rb +30 -0
- data/lib/scss_lint/reporter/xml_reporter.rb +33 -0
- data/lib/scss_lint/runner.rb +51 -0
- data/lib/scss_lint/sass/script.rb +78 -0
- data/lib/scss_lint/sass/tree.rb +168 -0
- data/lib/scss_lint/selector_visitor.rb +34 -0
- data/lib/scss_lint/utils.rb +112 -0
- data/lib/scss_lint/version.rb +4 -0
- data/spec/scss_lint/cli_spec.rb +177 -0
- data/spec/scss_lint/config_spec.rb +253 -0
- data/spec/scss_lint/engine_spec.rb +24 -0
- data/spec/scss_lint/file_finder_spec.rb +134 -0
- data/spec/scss_lint/linter/bang_format_spec.rb +121 -0
- data/spec/scss_lint/linter/border_zero_spec.rb +118 -0
- data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
- data/spec/scss_lint/linter/color_variable_spec.rb +155 -0
- data/spec/scss_lint/linter/comment_spec.rb +79 -0
- data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
- data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +575 -0
- data/spec/scss_lint/linter/duplicate_property_spec.rb +189 -0
- data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +276 -0
- data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
- data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
- data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_validation_spec.rb +40 -0
- data/spec/scss_lint/linter/id_selector_spec.rb +62 -0
- data/spec/scss_lint/linter/import_path_spec.rb +300 -0
- data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
- data/spec/scss_lint/linter/indentation_spec.rb +347 -0
- data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
- data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
- data/spec/scss_lint/linter/name_format_spec.rb +282 -0
- data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
- data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
- data/spec/scss_lint/linter/property_count_spec.rb +104 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +482 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +84 -0
- data/spec/scss_lint/linter/property_units_spec.rb +229 -0
- data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
- data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
- data/spec/scss_lint/linter/selector_format_spec.rb +632 -0
- data/spec/scss_lint/linter/shorthand_spec.rb +198 -0
- data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
- data/spec/scss_lint/linter/single_line_per_selector_spec.rb +130 -0
- data/spec/scss_lint/linter/space_after_comma_spec.rb +332 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +373 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +37 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +829 -0
- data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
- data/spec/scss_lint/linter/string_quotes_spec.rb +335 -0
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +304 -0
- data/spec/scss_lint/linter/trailing_zero_spec.rb +176 -0
- data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +98 -0
- data/spec/scss_lint/linter/url_format_spec.rb +55 -0
- data/spec/scss_lint/linter/url_quotes_spec.rb +73 -0
- data/spec/scss_lint/linter/variable_for_property_spec.rb +145 -0
- data/spec/scss_lint/linter/vendor_prefix_spec.rb +371 -0
- data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
- data/spec/scss_lint/linter_registry_spec.rb +50 -0
- data/spec/scss_lint/linter_spec.rb +292 -0
- data/spec/scss_lint/location_spec.rb +42 -0
- data/spec/scss_lint/options_spec.rb +34 -0
- data/spec/scss_lint/rake_task_spec.rb +43 -0
- data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
- data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
- data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
- data/spec/scss_lint/reporter/json_reporter_spec.rb +96 -0
- data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
- data/spec/scss_lint/reporter_spec.rb +11 -0
- data/spec/scss_lint/runner_spec.rb +123 -0
- data/spec/scss_lint/selector_visitor_spec.rb +264 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/isolated_environment.rb +25 -0
- data/spec/support/matchers/report_lint.rb +48 -0
- metadata +328 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe SCSSLint::Reporter do
|
|
4
|
+
class SCSSLint::Reporter::FakeReporter < SCSSLint::Reporter; end
|
|
5
|
+
|
|
6
|
+
describe '#descendants' do
|
|
7
|
+
it 'contains FakeReporter' do
|
|
8
|
+
SCSSLint::Reporter.descendants.should include(SCSSLint::Reporter::FakeReporter)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe SCSSLint::Runner do
|
|
4
|
+
let(:config_options) do
|
|
5
|
+
{
|
|
6
|
+
'linters' => {
|
|
7
|
+
'FakeLinter1' => { 'enabled' => true },
|
|
8
|
+
'FakeLinter2' => { 'enabled' => false },
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:config) { SCSSLint::Config.new(config_options) }
|
|
14
|
+
let(:runner) { described_class.new(config) }
|
|
15
|
+
|
|
16
|
+
before do
|
|
17
|
+
SCSSLint::LinterRegistry.stub(:linters)
|
|
18
|
+
.and_return([SCSSLint::Linter::FakeLinter1,
|
|
19
|
+
SCSSLint::Linter::FakeLinter2])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class SCSSLint::Linter::FakeLinter1 < SCSSLint::Linter; end
|
|
23
|
+
class SCSSLint::Linter::FakeLinter2 < SCSSLint::Linter; end
|
|
24
|
+
|
|
25
|
+
describe '#run' do
|
|
26
|
+
let(:files) { ['dummy1.scss', 'dummy2.scss'] }
|
|
27
|
+
subject { runner.run(files) }
|
|
28
|
+
|
|
29
|
+
before do
|
|
30
|
+
SCSSLint::Engine.stub(:new)
|
|
31
|
+
SCSSLint::Linter.any_instance.stub(:run)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'searches for lints in each file' do
|
|
35
|
+
runner.should_receive(:find_lints).exactly(files.size).times
|
|
36
|
+
subject
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'when all linters are disabled' do
|
|
40
|
+
let(:config_options) do
|
|
41
|
+
{
|
|
42
|
+
'linters' => {
|
|
43
|
+
'FakeLinter1' => { 'enabled' => false },
|
|
44
|
+
'FakeLinter2' => { 'enabled' => false },
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
before do
|
|
50
|
+
SCSSLint::Linter.any_instance
|
|
51
|
+
.stub(:run)
|
|
52
|
+
.and_raise(RuntimeError.new('Linter#run was called'))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'never runs a linter' do
|
|
56
|
+
expect { subject }.to_not raise_error
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'when the engine raises a FileEncodingError' do
|
|
61
|
+
let(:error) do
|
|
62
|
+
SCSSLint::FileEncodingError.new('Some error message')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
before do
|
|
66
|
+
SCSSLint::Engine.stub(:new).and_raise(error)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'records the error as a lint' do
|
|
70
|
+
subject.count.should == 2
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'when files ere excluded for one linter' do
|
|
75
|
+
let(:config_options) do
|
|
76
|
+
{
|
|
77
|
+
'linters' => {
|
|
78
|
+
'FakeLinter1' => { 'enabled' => true,
|
|
79
|
+
'exclude' => [File.expand_path('dummy1.scss'),
|
|
80
|
+
File.expand_path('dummy2.scss')] },
|
|
81
|
+
'FakeLinter2' => { 'enabled' => false },
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
before do
|
|
87
|
+
SCSSLint::Linter::FakeLinter1.any_instance
|
|
88
|
+
.stub(:run)
|
|
89
|
+
.and_raise(RuntimeError.new('FakeLinter1#run was called'))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'does not run the linter for the disabled files' do
|
|
93
|
+
expect { subject }.to_not raise_error
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'when a linter raises an error' do
|
|
98
|
+
let(:backtrace) { %w[file.rb:1 file.rb:2] }
|
|
99
|
+
|
|
100
|
+
let(:error) do
|
|
101
|
+
StandardError.new('Some error message').tap do |e|
|
|
102
|
+
e.set_backtrace(backtrace)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
before do
|
|
107
|
+
runner.stub(:run_linter).and_raise(error)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'raises a LinterError' do
|
|
111
|
+
expect { subject }.to raise_error(SCSSLint::Exceptions::LinterError)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'has the name of the file the linter was checking' do
|
|
115
|
+
expect { subject }.to raise_error { |e| e.message.should include files.first }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'has the same backtrace as the original error' do
|
|
119
|
+
expect { subject }.to raise_error { |e| e.backtrace.should == backtrace }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe SCSSLint::SelectorVisitor do
|
|
4
|
+
describe '#visit' do
|
|
5
|
+
let(:engine) { SCSSLint::Engine.new(code: scss) }
|
|
6
|
+
before { RuleVisitor.new(visitor).run(engine) }
|
|
7
|
+
|
|
8
|
+
# Visits every rule in the given parse tree and passes the parsed selector
|
|
9
|
+
# to the given `selector_visitor`.
|
|
10
|
+
class RuleVisitor < Sass::Tree::Visitors::Base
|
|
11
|
+
def initialize(selector_visitor)
|
|
12
|
+
@selector_visitor = selector_visitor
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run(engine)
|
|
16
|
+
visit(engine.tree)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def visit_rule(node)
|
|
20
|
+
@selector_visitor.visit_selector(node.parsed_rules)
|
|
21
|
+
yield # Continue linting children
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Superclass for all the test selector visitors
|
|
26
|
+
class TrackingSelectorVisitor
|
|
27
|
+
include SCSSLint::SelectorVisitor
|
|
28
|
+
|
|
29
|
+
attr_reader :node_order
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
@node_order = []
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when visitor defines visit_attribute' do
|
|
37
|
+
class TestAttributeVisitor < TrackingSelectorVisitor
|
|
38
|
+
def visit_attribute(attribute)
|
|
39
|
+
@node_order << attribute.name
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
let(:visitor) { TestAttributeVisitor.new }
|
|
44
|
+
|
|
45
|
+
let(:scss) { <<-SCSS }
|
|
46
|
+
[rel="nofollow"] {}
|
|
47
|
+
a[href="http"] {}
|
|
48
|
+
.class [data-count] {}
|
|
49
|
+
SCSS
|
|
50
|
+
|
|
51
|
+
it 'visits all attribute nodes' do
|
|
52
|
+
visitor.node_order.should == %w[rel href data-count]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'when visitor defines visit_class' do
|
|
57
|
+
class TestClassVisitor < TrackingSelectorVisitor
|
|
58
|
+
def visit_class(klass)
|
|
59
|
+
@node_order << klass.name
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
let(:visitor) { TestClassVisitor.new }
|
|
64
|
+
|
|
65
|
+
let(:scss) { <<-SCSS }
|
|
66
|
+
a.link {}
|
|
67
|
+
.menu .menu-item {}
|
|
68
|
+
.button[data-source], .overlay {}
|
|
69
|
+
SCSS
|
|
70
|
+
|
|
71
|
+
it 'visits all class nodes' do
|
|
72
|
+
visitor.node_order.should == %w[link menu menu-item button overlay]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'when visitor defines visit_element' do
|
|
77
|
+
class TestElementVisitor < TrackingSelectorVisitor
|
|
78
|
+
def visit_element(element)
|
|
79
|
+
@node_order << element.name
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
let(:visitor) { TestElementVisitor.new }
|
|
84
|
+
|
|
85
|
+
let(:scss) { <<-SCSS }
|
|
86
|
+
a, b {}
|
|
87
|
+
.class {}
|
|
88
|
+
i + p {}
|
|
89
|
+
SCSS
|
|
90
|
+
|
|
91
|
+
it 'visits all element nodes' do
|
|
92
|
+
visitor.node_order.should == %w[a b i p]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context 'when visitor defines visit_id' do
|
|
97
|
+
class TestIdVisitor < TrackingSelectorVisitor
|
|
98
|
+
def visit_id(id)
|
|
99
|
+
@node_order << id.name
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
let(:visitor) { TestIdVisitor.new }
|
|
104
|
+
|
|
105
|
+
let(:scss) { <<-SCSS }
|
|
106
|
+
#something, #thing {}
|
|
107
|
+
#object + #entity {}
|
|
108
|
+
SCSS
|
|
109
|
+
|
|
110
|
+
it 'visits all id nodes' do
|
|
111
|
+
visitor.node_order.should == %w[something thing object entity]
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context 'when visitor defines visit_parent' do
|
|
116
|
+
class TestParentVisitor < TrackingSelectorVisitor
|
|
117
|
+
def visit_parent(parent)
|
|
118
|
+
@node_order << parent.line
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
let(:visitor) { TestParentVisitor.new }
|
|
123
|
+
|
|
124
|
+
let(:scss) { <<-SCSS }
|
|
125
|
+
p {
|
|
126
|
+
&.class {}
|
|
127
|
+
.container & {}
|
|
128
|
+
.nothing {}
|
|
129
|
+
}
|
|
130
|
+
SCSS
|
|
131
|
+
|
|
132
|
+
it 'visits all parent nodes' do
|
|
133
|
+
visitor.node_order.should == [2, 3]
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context 'when visitor defines visit_placeholder' do
|
|
138
|
+
class TestPlaceholderVisitor < TrackingSelectorVisitor
|
|
139
|
+
def visit_placeholder(placeholder)
|
|
140
|
+
@node_order << placeholder.name
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
let(:visitor) { TestPlaceholderVisitor.new }
|
|
145
|
+
|
|
146
|
+
let(:scss) { <<-SCSS }
|
|
147
|
+
%placeholder, %other-placeholder {}
|
|
148
|
+
.button, %button {}
|
|
149
|
+
SCSS
|
|
150
|
+
|
|
151
|
+
it 'visits all placeholder nodes' do
|
|
152
|
+
visitor.node_order.should == %w[placeholder other-placeholder button]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context 'when visitor defines visit_pseudo' do
|
|
157
|
+
class TestPseudoVisitor < TrackingSelectorVisitor
|
|
158
|
+
def visit_pseudo(pseudo)
|
|
159
|
+
@node_order << pseudo.name
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
let(:visitor) { TestPseudoVisitor.new }
|
|
164
|
+
|
|
165
|
+
let(:scss) { <<-SCSS }
|
|
166
|
+
li:first-child, li:last-child {}
|
|
167
|
+
p {
|
|
168
|
+
&:first-letter {}
|
|
169
|
+
}
|
|
170
|
+
SCSS
|
|
171
|
+
|
|
172
|
+
it 'visits all pseudo-selector nodes' do
|
|
173
|
+
visitor.node_order.should == %w[first-child last-child first-letter]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context 'when visitor defines visit_universal' do
|
|
178
|
+
class TestUniversalVisitor < TrackingSelectorVisitor
|
|
179
|
+
def visit_universal(universal)
|
|
180
|
+
@node_order << universal.line
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
let(:visitor) { TestUniversalVisitor.new }
|
|
185
|
+
|
|
186
|
+
let(:scss) { <<-SCSS }
|
|
187
|
+
p > * {}
|
|
188
|
+
* {}
|
|
189
|
+
li {}
|
|
190
|
+
*:first-child {}
|
|
191
|
+
SCSS
|
|
192
|
+
|
|
193
|
+
it 'visits all universal-selector nodes' do
|
|
194
|
+
visitor.node_order.should == [1, 2, 4]
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context 'when visitor defines visit_simple_sequence' do
|
|
199
|
+
class TestSimpleSequenceVisitor < TrackingSelectorVisitor
|
|
200
|
+
def visit_simple_sequence(seq)
|
|
201
|
+
@node_order << seq
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
let(:visitor) { TestSimpleSequenceVisitor.new }
|
|
206
|
+
|
|
207
|
+
let(:scss) { <<-SCSS }
|
|
208
|
+
a, b, p[lang] {}
|
|
209
|
+
a.link + b.word {}
|
|
210
|
+
li {}
|
|
211
|
+
ul > li {}
|
|
212
|
+
p span {}
|
|
213
|
+
SCSS
|
|
214
|
+
|
|
215
|
+
it 'visits all simple sequences' do
|
|
216
|
+
visitor.node_order.count.should == 10
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
context 'when visitor defines visit_sequence' do
|
|
221
|
+
class TestSequenceVisitor < TrackingSelectorVisitor
|
|
222
|
+
def visit_sequence(seq)
|
|
223
|
+
@node_order << seq
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
let(:visitor) { TestSequenceVisitor.new }
|
|
228
|
+
|
|
229
|
+
let(:scss) { <<-SCSS }
|
|
230
|
+
a, b, p[lang] {}
|
|
231
|
+
a.link + b.word {}
|
|
232
|
+
li {}
|
|
233
|
+
ul > li {}
|
|
234
|
+
p span {}
|
|
235
|
+
SCSS
|
|
236
|
+
|
|
237
|
+
it 'visits all sequences' do
|
|
238
|
+
visitor.node_order.count.should == 7
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
context 'when visitor defines visit_comma_sequence' do
|
|
243
|
+
class TestCommaSequenceVisitor < TrackingSelectorVisitor
|
|
244
|
+
def visit_comma_sequence(seq)
|
|
245
|
+
@node_order << seq
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
let(:visitor) { TestCommaSequenceVisitor.new }
|
|
250
|
+
|
|
251
|
+
let(:scss) { <<-SCSS }
|
|
252
|
+
a, b, p[lang] {}
|
|
253
|
+
a.link + b.word {}
|
|
254
|
+
li {}
|
|
255
|
+
ul > li {}
|
|
256
|
+
p span {}
|
|
257
|
+
SCSS
|
|
258
|
+
|
|
259
|
+
it 'visits all comma (i.e. selector) sequences' do
|
|
260
|
+
visitor.node_order.count.should == 5
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'scss_lint'
|
|
2
|
+
require 'nokogiri'
|
|
3
|
+
|
|
4
|
+
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }
|
|
5
|
+
|
|
6
|
+
RSpec.configure do |config|
|
|
7
|
+
config.expect_with :rspec do |c|
|
|
8
|
+
c.syntax = [:expect, :should]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
config.mock_with :rspec do |c|
|
|
12
|
+
c.syntax = :should
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
config.before(:each) do
|
|
16
|
+
# If running a linter spec, run the described linter against the CSS code
|
|
17
|
+
# for each example. This significantly DRYs up our linter specs to contain
|
|
18
|
+
# only tests, since all the setup code is now centralized here.
|
|
19
|
+
if described_class <= SCSSLint::Linter
|
|
20
|
+
initial_indent = scss[/\A(\s*)/, 1]
|
|
21
|
+
normalized_css = scss.gsub(/^#{initial_indent}/, '')
|
|
22
|
+
|
|
23
|
+
# Use the configuration settings defined by default unless a specific
|
|
24
|
+
# configuration has been provided for the test.
|
|
25
|
+
local_config = if respond_to?(:linter_config)
|
|
26
|
+
linter_config
|
|
27
|
+
else
|
|
28
|
+
SCSSLint::Config.default.linter_options(subject)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subject.run(SCSSLint::Engine.new(code: normalized_css), local_config)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'tmpdir'
|
|
3
|
+
|
|
4
|
+
shared_context 'isolated environment' do
|
|
5
|
+
around do |example|
|
|
6
|
+
Dir.mktmpdir do |tmpdir|
|
|
7
|
+
original_home = ENV['HOME']
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
virtual_home = File.expand_path(File.join(tmpdir, 'home'))
|
|
11
|
+
Dir.mkdir(virtual_home)
|
|
12
|
+
ENV['HOME'] = virtual_home
|
|
13
|
+
|
|
14
|
+
working_dir = File.join(tmpdir, 'work')
|
|
15
|
+
Dir.mkdir(working_dir)
|
|
16
|
+
|
|
17
|
+
Dir.chdir(working_dir) do
|
|
18
|
+
example.run
|
|
19
|
+
end
|
|
20
|
+
ensure
|
|
21
|
+
ENV['HOME'] = original_home
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|