rubocop-cask 0.1.0 → 0.2.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.
@@ -0,0 +1,51 @@
1
+ require 'forwardable'
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Cask
6
+ # This cop checks that a cask's stanzas are ordered correctly.
7
+ # See https://github.com/caskroom/homebrew-cask/blob/master/CONTRIBUTING.md#stanza-order
8
+ # for more info.
9
+ class StanzaOrder < Cop
10
+ extend Forwardable
11
+ include RuboCop::Cask::CaskHelp
12
+
13
+ MESSAGE = '`%s` stanza out of order'
14
+
15
+ def on_cask(cask_node)
16
+ @cask_block = RuboCop::Cask::AST::CaskBlock.new(cask_node)
17
+ add_offenses
18
+ end
19
+
20
+ def autocorrect(stanza)
21
+ lambda do |corrector|
22
+ correct_stanza_index = toplevel_stanzas.index(stanza)
23
+ correct_stanza = sorted_toplevel_stanzas[correct_stanza_index]
24
+ corrector.replace(stanza.expression, correct_stanza.source)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :cask_block
31
+ def_delegators :cask_block, :cask_node, :toplevel_stanzas,
32
+ :sorted_toplevel_stanzas
33
+
34
+ def add_offenses
35
+ offending_stanzas.each do |stanza|
36
+ message = format(MESSAGE, stanza.stanza_name)
37
+ add_offense(stanza, stanza.expression, message)
38
+ end
39
+ end
40
+
41
+ def offending_stanzas
42
+ offending = []
43
+ toplevel_stanzas.each_with_index do |stanza, i|
44
+ offending << stanza unless stanza == sorted_toplevel_stanzas[i]
45
+ end
46
+ offending
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,105 +1,84 @@
1
1
  describe RuboCop::Cop::Cask::NoDslVersion do
2
+ include CopSharedExamples
3
+
2
4
  subject(:cop) { described_class.new }
3
5
 
4
6
  context 'with header method `cask`' do
7
+ let(:header_method) { 'cask' }
8
+
5
9
  context 'with no dsl version' do
6
- it 'passes' do
7
- inspect_source(cop, [
8
- "cask 'foo' do",
9
- 'end'
10
- ])
11
- expect(cop.offenses).to be_empty
12
- end
10
+ let(:source) { "cask 'foo' do; end" }
11
+
12
+ include_examples 'does not report any offenses'
13
13
  end
14
14
 
15
15
  context 'with dsl version' do
16
- it 'fails' do
17
- inspect_source(cop, [
18
- "cask :v1 => 'foo' do",
19
- 'end'
20
- ])
21
- expect(cop.offenses.size).to eq(1)
22
- expect(cop.offenses.first.line).to eq(1)
23
- expect(cop.messages)
24
- .to eq(["Use `cask 'foo'` instead of `cask :v1 => 'foo'`"])
25
- expect(cop.highlights)
26
- .to eq(["cask :v1 => 'foo'"])
16
+ let(:source) { "cask :v1 => 'foo' do; end" }
17
+ let(:correct_source) { "cask 'foo' do; end" }
18
+ let(:expected_offenses) do
19
+ [
20
+ {
21
+ message: "Use `cask 'foo'` instead of `cask :v1 => 'foo'`",
22
+ severity: :convention,
23
+ line: 1,
24
+ column: 0,
25
+ source: "cask :v1 => 'foo'"
26
+ }
27
+ ]
27
28
  end
28
29
 
29
- it 'autocorrects' do
30
- new_source = autocorrect_source(cop, [
31
- "cask :v1 => 'foo' do",
32
- 'end'
33
- ])
34
- expect(new_source).to eq([
35
- "cask 'foo' do",
36
- 'end'
37
- ].join("\n"))
38
- end
30
+ include_examples 'reports offenses'
31
+
32
+ include_examples 'autocorrects offenses'
39
33
  end
40
34
 
41
35
  context 'with dsl version containing "test"' do
42
- it 'fails' do
43
- inspect_source(cop, [
44
- "cask :v1test => 'foo' do",
45
- 'end'
46
- ])
47
- expect(cop.offenses.size).to eq(1)
48
- expect(cop.offenses.first.line).to eq(1)
49
- expect(cop.messages)
50
- .to eq(["Use `test_cask 'foo'` instead of `cask :v1test => 'foo'`"])
51
- expect(cop.highlights)
52
- .to eq(["cask :v1test => 'foo'"])
36
+ let(:source) { "cask :v1test => 'foo' do; end" }
37
+ let(:correct_source) { "test_cask 'foo' do; end" }
38
+ let(:expected_offenses) do
39
+ [
40
+ {
41
+ message: "Use `test_cask 'foo'` instead of `cask :v1test => 'foo'`",
42
+ severity: :convention,
43
+ line: 1,
44
+ column: 0,
45
+ source: "cask :v1test => 'foo'"
46
+ }
47
+ ]
53
48
  end
54
49
 
55
- it 'autocorrects' do
56
- new_source = autocorrect_source(cop, [
57
- "cask :v1test => 'foo' do",
58
- 'end'
59
- ])
60
- expect(new_source).to eq([
61
- "test_cask 'foo' do",
62
- 'end'
63
- ].join("\n"))
64
- end
50
+ include_examples 'reports offenses'
51
+
52
+ include_examples 'autocorrects offenses'
65
53
  end
66
54
  end
67
55
 
68
56
  context 'with header method `test_cask`' do
69
57
  context 'with no dsl version' do
70
- it 'passes' do
71
- inspect_source(cop, [
72
- "test_cask 'foo' do",
73
- 'end'
74
- ])
75
- expect(cop.offenses).to be_empty
76
- end
58
+ let(:source) { "test_cask 'foo' do; end" }
59
+
60
+ include_examples 'does not report any offenses'
77
61
  end
78
62
 
79
63
  context 'with dsl version' do
80
- it 'fails' do
81
- inspect_source(cop, [
82
- "test_cask :v1 => 'foo' do",
83
- 'end'
84
- ])
85
- expect(cop.offenses.size).to eq(1)
86
- expect(cop.offenses.first.line).to eq(1)
87
- expect(cop.messages)
88
- .to eq(["Use `test_cask 'foo'` instead of `test_cask :v1 => 'foo'`"])
89
- expect(cop.highlights)
90
- .to eq(["test_cask :v1 => 'foo'"])
64
+ let(:source) { "test_cask :v1 => 'foo' do; end" }
65
+ let(:correct_source) { "test_cask 'foo' do; end" }
66
+ let(:expected_offenses) do
67
+ [
68
+ {
69
+ message: "Use `test_cask 'foo'` instead of "\
70
+ "`test_cask :v1 => 'foo'`",
71
+ severity: :convention,
72
+ line: 1,
73
+ column: 0,
74
+ source: "test_cask :v1 => 'foo'"
75
+ }
76
+ ]
91
77
  end
92
78
 
93
- it 'autocorrects' do
94
- new_source = autocorrect_source(cop, [
95
- "test_cask :v1 => 'foo' do",
96
- 'end'
97
- ])
98
- expect(new_source).to eq([
99
- "test_cask 'foo' do",
100
- 'end'
101
- ].join("\n"))
102
- end
79
+ include_examples 'reports offenses'
80
+
81
+ include_examples 'autocorrects offenses'
103
82
  end
104
83
  end
105
84
  end
@@ -0,0 +1,194 @@
1
+ describe RuboCop::Cop::Cask::StanzaGrouping do
2
+ include CopSharedExamples
3
+
4
+ subject(:cop) { described_class.new }
5
+ let(:missing_line_msg) do
6
+ 'stanza groups should be separated by a single empty line'
7
+ end
8
+ let(:extra_line_msg) do
9
+ 'stanzas within the same group should have no lines between them'
10
+ end
11
+
12
+ context 'when no stanzas are incorrectly grouped' do
13
+ let(:source) do
14
+ <<-CASK.undent
15
+ cask 'foo' do
16
+ version :latest
17
+ sha256 :no_check
18
+ end
19
+ CASK
20
+ end
21
+
22
+ include_examples 'does not report any offenses'
23
+ end
24
+
25
+ context 'when one stanza is incorrectly grouped' do
26
+ let(:source) do
27
+ <<-CASK.undent
28
+ cask 'foo' do
29
+ version :latest
30
+
31
+ sha256 :no_check
32
+ end
33
+ CASK
34
+ end
35
+ let(:correct_source) do
36
+ <<-CASK.undent
37
+ cask 'foo' do
38
+ version :latest
39
+ sha256 :no_check
40
+ end
41
+ CASK
42
+ end
43
+ let(:expected_offenses) do
44
+ [
45
+ {
46
+ message: extra_line_msg,
47
+ severity: :convention,
48
+ line: 3,
49
+ column: 0,
50
+ source: "\n"
51
+ }
52
+ ]
53
+ end
54
+
55
+ include_examples 'reports offenses'
56
+
57
+ include_examples 'autocorrects offenses'
58
+ end
59
+
60
+ context 'when many stanzas are incorrectly grouped' do
61
+ let(:source) do
62
+ <<-CASK.undent
63
+ cask 'foo' do
64
+ version :latest
65
+ sha256 :no_check
66
+ url 'https://foo.example.com/foo.zip'
67
+
68
+ name 'Foo'
69
+
70
+ homepage 'https://foo.example.com'
71
+ license :mit
72
+
73
+ app 'Foo.app'
74
+ uninstall :quit => 'com.example.foo',
75
+ :kext => 'com.example.foo.kextextension'
76
+ end
77
+ CASK
78
+ end
79
+ let(:correct_source) do
80
+ <<-CASK.undent
81
+ cask 'foo' do
82
+ version :latest
83
+ sha256 :no_check
84
+
85
+ url 'https://foo.example.com/foo.zip'
86
+ name 'Foo'
87
+ homepage 'https://foo.example.com'
88
+ license :mit
89
+
90
+ app 'Foo.app'
91
+
92
+ uninstall :quit => 'com.example.foo',
93
+ :kext => 'com.example.foo.kextextension'
94
+ end
95
+ CASK
96
+ end
97
+ let(:expected_offenses) do
98
+ [
99
+ {
100
+ message: missing_line_msg,
101
+ severity: :convention,
102
+ line: 4,
103
+ column: 0,
104
+ source: " url 'https://foo.example.com/foo.zip'"
105
+ },
106
+ {
107
+ message: extra_line_msg,
108
+ severity: :convention,
109
+ line: 5,
110
+ column: 0,
111
+ source: "\n"
112
+ },
113
+ {
114
+ message: extra_line_msg,
115
+ severity: :convention,
116
+ line: 7,
117
+ column: 0,
118
+ source: "\n"
119
+ },
120
+ {
121
+ message: missing_line_msg,
122
+ severity: :convention,
123
+ line: 12,
124
+ column: 0,
125
+ source: " uninstall :quit => 'com.example.foo',"
126
+ }
127
+ ]
128
+ end
129
+
130
+ include_examples 'reports offenses'
131
+
132
+ include_examples 'autocorrects offenses'
133
+ end
134
+
135
+ context 'when a stanza includes a heredoc' do
136
+ let(:source) do
137
+ <<-CASK.undent
138
+ cask 'foo' do
139
+ version :latest
140
+ sha256 :no_check
141
+ url 'https://foo.example.com/foo.zip'
142
+ name 'Foo'
143
+ caveats <<-EOS.undent
144
+ This is a multiline caveat.
145
+
146
+ Let's hope it doesn't cause any problems!
147
+ EOS
148
+ app 'Foo.app'
149
+ end
150
+ CASK
151
+ end
152
+ let(:correct_source) do
153
+ <<-CASK.undent
154
+ cask 'foo' do
155
+ version :latest
156
+ sha256 :no_check
157
+
158
+ url 'https://foo.example.com/foo.zip'
159
+ name 'Foo'
160
+
161
+ caveats <<-EOS.undent
162
+ This is a multiline caveat.
163
+
164
+ Let's hope it doesn't cause any problems!
165
+ EOS
166
+
167
+ app 'Foo.app'
168
+ end
169
+ CASK
170
+ end
171
+
172
+ it 'autocorrects as expected' do
173
+ new_source = autocorrect_source(cop, source)
174
+ expect(new_source).to eq(Array(correct_source).join("\n"))
175
+ end
176
+ end
177
+
178
+ # TODO: detect incorrectly grouped stanzas in nested expressions
179
+ context 'when stanzas are nested in a conditional expression' do
180
+ let(:source) do
181
+ <<-CASK.undent
182
+ cask 'foo' do
183
+ if true
184
+ version :latest
185
+
186
+ sha256 :no_check
187
+ end
188
+ end
189
+ CASK
190
+ end
191
+
192
+ include_examples 'does not report any offenses'
193
+ end
194
+ end
@@ -0,0 +1,213 @@
1
+ describe RuboCop::Cop::Cask::StanzaOrder do
2
+ include CopSharedExamples
3
+
4
+ subject(:cop) { described_class.new }
5
+
6
+ context 'when no stanzas are out of order' do
7
+ let(:source) do
8
+ <<-CASK.undent
9
+ cask 'foo' do
10
+ version :latest
11
+ sha256 :no_check
12
+ end
13
+ CASK
14
+ end
15
+
16
+ include_examples 'does not report any offenses'
17
+ end
18
+
19
+ context 'when one pair of stanzas is out of order' do
20
+ let(:source) do
21
+ <<-CASK.undent
22
+ cask 'foo' do
23
+ sha256 :no_check
24
+ version :latest
25
+ end
26
+ CASK
27
+ end
28
+ let(:correct_source) do
29
+ <<-CASK.undent
30
+ cask 'foo' do
31
+ version :latest
32
+ sha256 :no_check
33
+ end
34
+ CASK
35
+ end
36
+ let(:expected_offenses) do
37
+ [
38
+ {
39
+ message: '`sha256` stanza out of order',
40
+ severity: :convention,
41
+ line: 2,
42
+ column: 2,
43
+ source: 'sha256 :no_check'
44
+ },
45
+ {
46
+ message: '`version` stanza out of order',
47
+ severity: :convention,
48
+ line: 3,
49
+ column: 2,
50
+ source: 'version :latest'
51
+ }
52
+ ]
53
+ end
54
+
55
+ include_examples 'reports offenses'
56
+
57
+ include_examples 'autocorrects offenses'
58
+ end
59
+
60
+ context 'when many stanzas are out of order' do
61
+ let(:source) do
62
+ <<-CASK.undent
63
+ cask 'foo' do
64
+ url 'https://foo.example.com/foo.zip'
65
+ uninstall :quit => 'com.example.foo',
66
+ :kext => 'com.example.foo.kext'
67
+ version :latest
68
+ app 'Foo.app'
69
+ sha256 :no_check
70
+ end
71
+ CASK
72
+ end
73
+ let(:correct_source) do
74
+ <<-CASK.undent
75
+ cask 'foo' do
76
+ version :latest
77
+ sha256 :no_check
78
+ url 'https://foo.example.com/foo.zip'
79
+ app 'Foo.app'
80
+ uninstall :quit => 'com.example.foo',
81
+ :kext => 'com.example.foo.kext'
82
+ end
83
+ CASK
84
+ end
85
+ let(:expected_offenses) do
86
+ [
87
+ {
88
+ message: '`url` stanza out of order',
89
+ severity: :convention,
90
+ line: 2,
91
+ column: 2,
92
+ source: "url 'https://foo.example.com/foo.zip'"
93
+ },
94
+ {
95
+ message: '`uninstall` stanza out of order',
96
+ severity: :convention,
97
+ line: 3,
98
+ column: 2,
99
+ source: "uninstall :quit => 'com.example.foo',\n" \
100
+ " :kext => 'com.example.foo.kext'"
101
+ },
102
+ {
103
+ message: '`version` stanza out of order',
104
+ severity: :convention,
105
+ line: 5,
106
+ column: 2,
107
+ source: 'version :latest'
108
+ },
109
+ {
110
+ message: '`sha256` stanza out of order',
111
+ severity: :convention,
112
+ line: 7,
113
+ column: 2,
114
+ source: 'sha256 :no_check'
115
+ }
116
+ ]
117
+ end
118
+
119
+ include_examples 'reports offenses'
120
+
121
+ include_examples 'autocorrects offenses'
122
+ end
123
+
124
+ context 'when a stanza appears multiple times' do
125
+ let(:source) do
126
+ <<-CASK.undent
127
+ cask 'foo' do
128
+ name 'Foo'
129
+ url 'https://foo.example.com/foo.zip'
130
+ name 'FancyFoo'
131
+ version :latest
132
+ app 'Foo.app'
133
+ sha256 :no_check
134
+ name 'FunkyFoo'
135
+ end
136
+ CASK
137
+ end
138
+ let(:correct_source) do
139
+ <<-CASK.undent
140
+ cask 'foo' do
141
+ version :latest
142
+ sha256 :no_check
143
+ url 'https://foo.example.com/foo.zip'
144
+ name 'Foo'
145
+ name 'FancyFoo'
146
+ name 'FunkyFoo'
147
+ app 'Foo.app'
148
+ end
149
+ CASK
150
+ end
151
+
152
+ it 'preserves the original order' do
153
+ new_source = autocorrect_source(cop, source)
154
+ expect(new_source).to eq(Array(correct_source).join("\n"))
155
+ end
156
+ end
157
+
158
+ context 'when a stanza includes a heredoc' do
159
+ let(:source) do
160
+ <<-CASK.undent
161
+ cask 'foo' do
162
+ name 'Foo'
163
+ url 'https://foo.example.com/foo.zip'
164
+ caveats <<-EOS.undent
165
+ This is a multiline caveat.
166
+
167
+ Let's hope it doesn't cause any problems!
168
+ EOS
169
+ version :latest
170
+ app 'Foo.app'
171
+ sha256 :no_check
172
+ end
173
+ CASK
174
+ end
175
+ let(:correct_source) do
176
+ <<-CASK.undent
177
+ cask 'foo' do
178
+ version :latest
179
+ sha256 :no_check
180
+ url 'https://foo.example.com/foo.zip'
181
+ name 'Foo'
182
+ app 'Foo.app'
183
+ caveats <<-EOS.undent
184
+ This is a multiline caveat.
185
+
186
+ Let's hope it doesn't cause any problems!
187
+ EOS
188
+ end
189
+ CASK
190
+ end
191
+
192
+ it 'autocorrects as expected' do
193
+ new_source = autocorrect_source(cop, source)
194
+ expect(new_source).to eq(Array(correct_source).join("\n"))
195
+ end
196
+ end
197
+
198
+ # TODO: detect out-of-order stanzas in nested expressions
199
+ context 'when stanzas are nested in a conditional expression' do
200
+ let(:source) do
201
+ <<-CASK.undent
202
+ cask 'foo' do
203
+ if true
204
+ sha256 :no_check
205
+ version :latest
206
+ end
207
+ end
208
+ CASK
209
+ end
210
+
211
+ include_examples 'does not report any offenses'
212
+ end
213
+ end