rubocop-view_component 0.7.0 → 0.8.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 +4 -4
- data/.rubocop.yml +3 -0
- data/{CLAUDE.md → AGENTS.md} +3 -3
- data/README.md +42 -5
- data/config/default.yml +6 -2
- data/docs/rubydex.md +136 -0
- data/lib/rubocop/cop/view_component/base.rb +27 -10
- data/lib/rubocop/cop/view_component/prefer_composition.rb +42 -9
- data/lib/rubocop/cop/view_component/prefer_private_methods.rb +46 -13
- data/lib/rubocop/cop/view_component/rspec_scope.rb +98 -0
- data/lib/rubocop/cop/view_component/supplementary_index.rb +142 -0
- data/lib/rubocop/cop/view_component/test_rendered_output.rb +106 -11
- data/lib/rubocop/view_component/version.rb +1 -1
- data/script/verify +29 -18
- data/spec/expected_flowbite_failures.yml +64 -0
- data/spec/expected_govuk_failures.yml +9 -13
- data/spec/expected_polaris_failures.yml +2 -0
- data/spec/expected_primer_failures.yml +57 -2
- data/spec/rubocop/cop/view_component/base_spec.rb +16 -0
- data/spec/rubocop/cop/view_component/prefer_composition_spec.rb +104 -0
- data/spec/rubocop/cop/view_component/prefer_private_methods_spec.rb +124 -0
- data/spec/rubocop/cop/view_component/test_rendered_output_spec.rb +287 -4
- data/verification/flowbite_rubocop_config.yml +12 -0
- data/verification/libraries.yml +4 -0
- metadata +8 -3
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
3
6
|
RSpec.describe RuboCop::Cop::ViewComponent::PreferComposition, :config do
|
|
4
7
|
let(:config) { RuboCop::Config.new }
|
|
5
8
|
|
|
@@ -55,6 +58,62 @@ RSpec.describe RuboCop::Cop::ViewComponent::PreferComposition, :config do
|
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
|
|
61
|
+
context "when the parent is a resolvable component with its own template" do
|
|
62
|
+
it "registers an offense" do
|
|
63
|
+
allow(File).to receive(:exist?).and_call_original
|
|
64
|
+
allow(File).to receive(:exist?).with("/test_source.html.erb").and_return(true)
|
|
65
|
+
|
|
66
|
+
expect_offense(<<~RUBY)
|
|
67
|
+
class BaseCardComponent < ViewComponent::Base
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class UserCardComponent < BaseCardComponent
|
|
71
|
+
^^^^^^^^^^^^^^^^^ ViewComponent/PreferComposition: Avoid inheriting from another ViewComponent.
|
|
72
|
+
end
|
|
73
|
+
RUBY
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context "when the parent is a resolvable component without a template" do
|
|
78
|
+
it "does not register an offense" do
|
|
79
|
+
expect_no_offenses(<<~RUBY)
|
|
80
|
+
class BaseCardComponent < ViewComponent::Base
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class UserCardComponent < BaseCardComponent
|
|
84
|
+
end
|
|
85
|
+
RUBY
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "when the parent resolves to a non-ViewComponent class" do
|
|
90
|
+
it "does not register an offense even when the name ends with Component" do
|
|
91
|
+
expect_no_offenses(<<~RUBY)
|
|
92
|
+
class WidgetComponent
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class UserCardComponent < WidgetComponent
|
|
96
|
+
end
|
|
97
|
+
RUBY
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "when inheriting from a namespaced abstract base class" do
|
|
102
|
+
it "does not register an offense" do
|
|
103
|
+
expect_no_offenses(<<~RUBY)
|
|
104
|
+
module Shadcn
|
|
105
|
+
class ApplicationViewComponent < ViewComponent::Base
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
module Button
|
|
109
|
+
class Component < ApplicationViewComponent
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
RUBY
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
58
117
|
context "when class has no parent" do
|
|
59
118
|
it "does not register an offense" do
|
|
60
119
|
expect_no_offenses(<<~RUBY)
|
|
@@ -88,4 +147,49 @@ RSpec.describe RuboCop::Cop::ViewComponent::PreferComposition, :config do
|
|
|
88
147
|
RUBY
|
|
89
148
|
end
|
|
90
149
|
end
|
|
150
|
+
|
|
151
|
+
context "with AdditionalSourcePaths" do
|
|
152
|
+
let(:engine_dir) { Dir.mktmpdir("engine") }
|
|
153
|
+
|
|
154
|
+
let(:config) do
|
|
155
|
+
RuboCop::Config.new(
|
|
156
|
+
"ViewComponent/PreferComposition" => {
|
|
157
|
+
"AdditionalSourcePaths" => [File.join(engine_dir, "app", "components")]
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
before do
|
|
163
|
+
write_engine_component("engine_card", "EngineCard")
|
|
164
|
+
write_engine_component("engine_base", "EngineBase")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
after { FileUtils.remove_entry(engine_dir) }
|
|
168
|
+
|
|
169
|
+
def write_engine_component(dir_name, class_name)
|
|
170
|
+
dir = File.join(engine_dir, "app", "components", dir_name)
|
|
171
|
+
FileUtils.mkdir_p(dir)
|
|
172
|
+
File.write(
|
|
173
|
+
File.join(dir, "component.rb"),
|
|
174
|
+
"module #{class_name}\n class Component < ViewComponent::Base\n end\nend\n"
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "resolves a parent through the additional paths and flags one with a template" do
|
|
179
|
+
File.write(File.join(engine_dir, "app", "components", "engine_card", "component.html.erb"), "<div></div>\n")
|
|
180
|
+
|
|
181
|
+
expect_offense(<<~RUBY)
|
|
182
|
+
class UserCardComponent < EngineCard::Component
|
|
183
|
+
^^^^^^^^^^^^^^^^^^^^^ Avoid inheriting from another ViewComponent.
|
|
184
|
+
end
|
|
185
|
+
RUBY
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "does not flag a parent resolved through the additional paths without a template" do
|
|
189
|
+
expect_no_offenses(<<~RUBY)
|
|
190
|
+
class UserCardComponent < EngineBase::Component
|
|
191
|
+
end
|
|
192
|
+
RUBY
|
|
193
|
+
end
|
|
194
|
+
end
|
|
91
195
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
3
6
|
RSpec.describe RuboCop::Cop::ViewComponent::PreferPrivateMethods, :config do
|
|
4
7
|
let(:config) { RuboCop::Config.new }
|
|
5
8
|
|
|
@@ -54,6 +57,77 @@ RSpec.describe RuboCop::Cop::ViewComponent::PreferPrivateMethods, :config do
|
|
|
54
57
|
end
|
|
55
58
|
end
|
|
56
59
|
|
|
60
|
+
context "when helper methods are made private with an explicit list" do
|
|
61
|
+
it "does not register offense for the listed methods" do
|
|
62
|
+
expect_no_offenses(<<~RUBY)
|
|
63
|
+
class CardComponent < ViewComponent::Base
|
|
64
|
+
def initialize(title)
|
|
65
|
+
@title = title
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def formatted_title
|
|
69
|
+
@title.upcase
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def helper
|
|
73
|
+
"helper"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private :formatted_title, :helper
|
|
77
|
+
end
|
|
78
|
+
RUBY
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "does not change the visibility of methods defined after the list" do
|
|
82
|
+
expect_offense(<<~RUBY)
|
|
83
|
+
class CardComponent < ViewComponent::Base
|
|
84
|
+
def formatted_title
|
|
85
|
+
"title"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private :formatted_title
|
|
89
|
+
|
|
90
|
+
def other_helper
|
|
91
|
+
^^^^^^^^^^^^^^^^ ViewComponent/PreferPrivateMethods: Consider making `other_helper` private. Only ViewComponent interface methods should be public.
|
|
92
|
+
"other"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
RUBY
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "flags a method made public again with an explicit list" do
|
|
99
|
+
expect_offense(<<~RUBY)
|
|
100
|
+
class CardComponent < ViewComponent::Base
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def formatted_title
|
|
104
|
+
^^^^^^^^^^^^^^^^^^^ ViewComponent/PreferPrivateMethods: Consider making `formatted_title` private. Only ViewComponent interface methods should be public.
|
|
105
|
+
"title"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
public :formatted_title
|
|
109
|
+
end
|
|
110
|
+
RUBY
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context "when a method is defined with `private def`" do
|
|
115
|
+
it "flags later public methods but not the `private def` method" do
|
|
116
|
+
expect_offense(<<~RUBY)
|
|
117
|
+
class CardComponent < ViewComponent::Base
|
|
118
|
+
private def formatted_title
|
|
119
|
+
"title"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def other_helper
|
|
123
|
+
^^^^^^^^^^^^^^^^ ViewComponent/PreferPrivateMethods: Consider making `other_helper` private. Only ViewComponent interface methods should be public.
|
|
124
|
+
"other"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
RUBY
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
57
131
|
context "with allowed public interface methods" do
|
|
58
132
|
it "allows initialize" do
|
|
59
133
|
expect_no_offenses(<<~RUBY)
|
|
@@ -258,6 +332,56 @@ RSpec.describe RuboCop::Cop::ViewComponent::PreferPrivateMethods, :config do
|
|
|
258
332
|
end
|
|
259
333
|
end
|
|
260
334
|
|
|
335
|
+
context "with AdditionalSourcePaths" do
|
|
336
|
+
let(:engine_dir) { Dir.mktmpdir("engine") }
|
|
337
|
+
|
|
338
|
+
before do
|
|
339
|
+
FileUtils.mkdir_p(File.join(engine_dir, "app", "components"))
|
|
340
|
+
File.write(
|
|
341
|
+
File.join(engine_dir, "app", "components", "engine_base_component.rb"),
|
|
342
|
+
"class EngineBaseComponent < ViewComponent::Base\nend\n"
|
|
343
|
+
)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
after { FileUtils.remove_entry(engine_dir) }
|
|
347
|
+
|
|
348
|
+
context "when the engine source path is configured" do
|
|
349
|
+
let(:config) do
|
|
350
|
+
RuboCop::Config.new(
|
|
351
|
+
"AllCops" => { "DisplayCopNames" => true },
|
|
352
|
+
"ViewComponent/PreferPrivateMethods" => {
|
|
353
|
+
"AdditionalSourcePaths" => [File.join(engine_dir, "app", "components")]
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "resolves ancestry through the additional source paths" do
|
|
359
|
+
expect_offense(<<~RUBY)
|
|
360
|
+
class CardComponent < EngineBaseComponent
|
|
361
|
+
def helper
|
|
362
|
+
^^^^^^^^^^ ViewComponent/PreferPrivateMethods: Consider making `helper` private. Only ViewComponent interface methods should be public.
|
|
363
|
+
"helper"
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
RUBY
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
context "when the engine source path is not configured" do
|
|
371
|
+
let(:config) { RuboCop::Config.new }
|
|
372
|
+
|
|
373
|
+
it "does not detect the class as a ViewComponent" do
|
|
374
|
+
expect_no_offenses(<<~RUBY)
|
|
375
|
+
class CardComponent < EngineBaseComponent
|
|
376
|
+
def helper
|
|
377
|
+
"helper"
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
RUBY
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
261
385
|
context "with template files" do
|
|
262
386
|
let(:component_file) { "spec/fixtures/components/template_method_component.rb" }
|
|
263
387
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
4
|
+
include ViewComponent::ProjectIndexHelpers
|
|
5
|
+
|
|
4
6
|
let(:config) { RuboCop::Config.new }
|
|
7
|
+
let(:project_index) { build_index }
|
|
5
8
|
|
|
6
9
|
context "with Minitest-style tests" do
|
|
7
10
|
context "when test instantiates a component but doesn't render" do
|
|
@@ -17,17 +20,37 @@ RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
context "when test instantiates a non-Component-suffixed class" do
|
|
20
|
-
it "registers an offense" do
|
|
23
|
+
it "registers an offense for an indexed component" do
|
|
21
24
|
expect_offense(<<~RUBY)
|
|
22
25
|
def test_toggle
|
|
23
26
|
^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
24
|
-
component =
|
|
27
|
+
component = UserComponent.new
|
|
25
28
|
assert component.active?
|
|
26
29
|
end
|
|
27
30
|
RUBY
|
|
28
31
|
end
|
|
29
32
|
end
|
|
30
33
|
|
|
34
|
+
context "when test instantiates a non-ViewComponent value object" do
|
|
35
|
+
let(:project_index) do
|
|
36
|
+
build_index(
|
|
37
|
+
"file:///month.rb" => <<~RUBY
|
|
38
|
+
class Month
|
|
39
|
+
end
|
|
40
|
+
RUBY
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "does not register an offense" do
|
|
45
|
+
expect_no_offenses(<<~RUBY)
|
|
46
|
+
def test_month
|
|
47
|
+
month = Month.new
|
|
48
|
+
assert month.active?
|
|
49
|
+
end
|
|
50
|
+
RUBY
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
31
54
|
context "when test instantiates a component and uses render_inline" do
|
|
32
55
|
it "does not register an offense" do
|
|
33
56
|
expect_no_offenses(<<~RUBY)
|
|
@@ -50,6 +73,18 @@ RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
|
50
73
|
end
|
|
51
74
|
end
|
|
52
75
|
|
|
76
|
+
context "when test instantiates a component and uses render_in_view_context" do
|
|
77
|
+
it "does not register an offense" do
|
|
78
|
+
expect_no_offenses(<<~RUBY)
|
|
79
|
+
def test_form_input
|
|
80
|
+
render_in_view_context do
|
|
81
|
+
render(UserComponent.new("hello"))
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
RUBY
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
53
88
|
context "when test doesn't instantiate any constants" do
|
|
54
89
|
it "does not register an offense" do
|
|
55
90
|
expect_no_offenses(<<~RUBY)
|
|
@@ -84,16 +119,46 @@ RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
|
84
119
|
end
|
|
85
120
|
|
|
86
121
|
context "when test instantiates a namespaced component" do
|
|
87
|
-
it "registers an offense" do
|
|
122
|
+
it "registers an offense for an indexed component" do
|
|
88
123
|
expect_offense(<<~RUBY)
|
|
89
124
|
def test_namespaced
|
|
90
125
|
^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
91
|
-
component =
|
|
126
|
+
component = UserComponent.new("hello")
|
|
92
127
|
assert_equal "HELLO", component.formatted_title
|
|
93
128
|
end
|
|
94
129
|
RUBY
|
|
95
130
|
end
|
|
96
131
|
end
|
|
132
|
+
|
|
133
|
+
context "when the component is built to assert an error" do
|
|
134
|
+
it "does not register an offense for assert_raises" do
|
|
135
|
+
expect_no_offenses(<<~RUBY)
|
|
136
|
+
def test_raises_for_invalid_input
|
|
137
|
+
assert_raises(ArgumentError) { UserComponent.new(nil) }
|
|
138
|
+
end
|
|
139
|
+
RUBY
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "does not register an offense for a do/end assert_raises block" do
|
|
143
|
+
expect_no_offenses(<<~RUBY)
|
|
144
|
+
def test_raises_for_invalid_input
|
|
145
|
+
assert_raises(ArgumentError) do
|
|
146
|
+
UserComponent.new(nil)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
RUBY
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "registers an offense when a component is built outside the assertion" do
|
|
153
|
+
expect_offense(<<~RUBY)
|
|
154
|
+
def test_mixed
|
|
155
|
+
^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
156
|
+
component = UserComponent.new("hello")
|
|
157
|
+
assert_raises(ArgumentError) { component.invalid! }
|
|
158
|
+
end
|
|
159
|
+
RUBY
|
|
160
|
+
end
|
|
161
|
+
end
|
|
97
162
|
end
|
|
98
163
|
|
|
99
164
|
context "with TestPaths configured" do
|
|
@@ -154,6 +219,18 @@ RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
|
154
219
|
end
|
|
155
220
|
end
|
|
156
221
|
|
|
222
|
+
context "when it block instantiates a component and uses render_in_view_context" do
|
|
223
|
+
it "does not register an offense" do
|
|
224
|
+
expect_no_offenses(<<~RUBY)
|
|
225
|
+
it "renders a form" do
|
|
226
|
+
render_in_view_context do
|
|
227
|
+
render(UserComponent.new("hello"))
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
RUBY
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
157
234
|
context "when it block doesn't instantiate components" do
|
|
158
235
|
it "does not register an offense" do
|
|
159
236
|
expect_no_offenses(<<~RUBY)
|
|
@@ -164,5 +241,211 @@ RSpec.describe RuboCop::Cop::ViewComponent::TestRenderedOutput, :config do
|
|
|
164
241
|
RUBY
|
|
165
242
|
end
|
|
166
243
|
end
|
|
244
|
+
|
|
245
|
+
context "when it block uses described_class inside a describe block" do
|
|
246
|
+
it "registers an offense" do
|
|
247
|
+
expect_offense(<<~RUBY)
|
|
248
|
+
describe UserComponent do
|
|
249
|
+
it "formats the title" do
|
|
250
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
251
|
+
component = described_class.new("hello")
|
|
252
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
RUBY
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "registers an offense for RSpec.describe" do
|
|
259
|
+
expect_offense(<<~RUBY)
|
|
260
|
+
RSpec.describe UserComponent do
|
|
261
|
+
it "formats the title" do
|
|
262
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
263
|
+
expect(described_class.new("hello").formatted_title).to eq("HELLO")
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
RUBY
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
it "registers an offense inside a nested context" do
|
|
270
|
+
expect_offense(<<~RUBY)
|
|
271
|
+
describe UserComponent do
|
|
272
|
+
context "when rendered" do
|
|
273
|
+
it "formats the title" do
|
|
274
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
275
|
+
component = described_class.new("hello")
|
|
276
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
RUBY
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "does not register an offense when using render_inline" do
|
|
284
|
+
expect_no_offenses(<<~RUBY)
|
|
285
|
+
describe UserComponent do
|
|
286
|
+
it "formats the title" do
|
|
287
|
+
render_inline described_class.new("hello")
|
|
288
|
+
expect(page).to have_text("HELLO")
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
RUBY
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
context "when the component is built to assert an error" do
|
|
296
|
+
it "does not register an offense for described_class" do
|
|
297
|
+
expect_no_offenses(<<~RUBY)
|
|
298
|
+
describe UserComponent do
|
|
299
|
+
it "raises for invalid input" do
|
|
300
|
+
expect { described_class.new(nil) }.to raise_error(ArgumentError)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
RUBY
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "does not register an offense for a component constant" do
|
|
307
|
+
expect_no_offenses(<<~RUBY)
|
|
308
|
+
it "raises for invalid input" do
|
|
309
|
+
expect { UserComponent.new(nil) }.to raise_error(ArgumentError)
|
|
310
|
+
end
|
|
311
|
+
RUBY
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "does not register an offense for not_to raise_error" do
|
|
315
|
+
expect_no_offenses(<<~RUBY)
|
|
316
|
+
it "does not raise for valid input" do
|
|
317
|
+
expect { UserComponent.new("hello") }.not_to raise_error
|
|
318
|
+
end
|
|
319
|
+
RUBY
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it "does not register an offense when raise_error takes a block" do
|
|
323
|
+
expect_no_offenses(<<~RUBY)
|
|
324
|
+
it "raises with a message" do
|
|
325
|
+
expect { UserComponent.new(nil) }.to raise_error(ArgumentError) do |error|
|
|
326
|
+
expect(error.message).to eq("invalid")
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
RUBY
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
context "when a component is built by a let helper" do
|
|
334
|
+
it "registers an offense" do
|
|
335
|
+
expect_offense(<<~RUBY)
|
|
336
|
+
describe UserComponent do
|
|
337
|
+
let(:component) { UserComponent.new("hello") }
|
|
338
|
+
|
|
339
|
+
it "formats the title" do
|
|
340
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
341
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
RUBY
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it "registers an offense for a subject using described_class" do
|
|
348
|
+
expect_offense(<<~RUBY)
|
|
349
|
+
describe UserComponent do
|
|
350
|
+
subject(:component) { described_class.new("hello") }
|
|
351
|
+
|
|
352
|
+
it "formats the title" do
|
|
353
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
354
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
RUBY
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it "registers an offense when the let is defined in an outer group" do
|
|
361
|
+
expect_offense(<<~RUBY)
|
|
362
|
+
describe UserComponent do
|
|
363
|
+
let(:component) { UserComponent.new("hello") }
|
|
364
|
+
|
|
365
|
+
context "when rendered" do
|
|
366
|
+
it "formats the title" do
|
|
367
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^ ViewComponent/TestRenderedOutput: Test instantiates a component but doesn't use `render_inline` or `render_preview`. Test the rendered output instead of component methods directly.
|
|
368
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
RUBY
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
it "does not register an offense when the helper renders output" do
|
|
376
|
+
expect_no_offenses(<<~RUBY)
|
|
377
|
+
describe UserComponent do
|
|
378
|
+
let(:rendered) { render_inline(UserComponent.new("hello")) }
|
|
379
|
+
|
|
380
|
+
it "formats the title" do
|
|
381
|
+
expect(rendered).to have_text("HELLO")
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
RUBY
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it "does not register an offense when the helper builds another object" do
|
|
388
|
+
expect_no_offenses(<<~RUBY)
|
|
389
|
+
describe UserComponent do
|
|
390
|
+
let(:component) { double("component") }
|
|
391
|
+
|
|
392
|
+
it "formats the title" do
|
|
393
|
+
expect(component.formatted_title).to eq("HELLO")
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
RUBY
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
it "does not register an offense when the helper is rendered with render_inline" do
|
|
400
|
+
expect_no_offenses(<<~RUBY)
|
|
401
|
+
describe UserComponent do
|
|
402
|
+
let(:component) { UserComponent.new("hello") }
|
|
403
|
+
|
|
404
|
+
it "formats the title" do
|
|
405
|
+
render_inline(component)
|
|
406
|
+
expect(page).to have_text("HELLO")
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
RUBY
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it "prefers the innermost helper definition" do
|
|
413
|
+
expect_no_offenses(<<~RUBY)
|
|
414
|
+
describe UserComponent do
|
|
415
|
+
let(:component) { UserComponent.new("hello") }
|
|
416
|
+
|
|
417
|
+
context "with a stub" do
|
|
418
|
+
let(:component) { double("component") }
|
|
419
|
+
|
|
420
|
+
it "formats the title" do
|
|
421
|
+
expect(component).to be_present
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
RUBY
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
context "when the described class is not a ViewComponent" do
|
|
431
|
+
let(:project_index) do
|
|
432
|
+
build_index(
|
|
433
|
+
"file:///month.rb" => <<~RUBY
|
|
434
|
+
class Month
|
|
435
|
+
end
|
|
436
|
+
RUBY
|
|
437
|
+
)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
it "does not register an offense" do
|
|
441
|
+
expect_no_offenses(<<~RUBY)
|
|
442
|
+
describe Month do
|
|
443
|
+
it "formats the month" do
|
|
444
|
+
month = described_class.new
|
|
445
|
+
expect(month.active?).to be(true)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
RUBY
|
|
449
|
+
end
|
|
167
450
|
end
|
|
168
451
|
end
|
data/verification/libraries.yml
CHANGED
|
@@ -9,3 +9,7 @@ govuk:
|
|
|
9
9
|
polaris:
|
|
10
10
|
tarball_url: https://github.com/baoagency/polaris_view_components/archive/refs/heads/main.tar.gz
|
|
11
11
|
display_name: baoagency/polaris_view_components
|
|
12
|
+
|
|
13
|
+
flowbite:
|
|
14
|
+
tarball_url: https://github.com/substancelab/flowbite-components/archive/refs/heads/main.tar.gz
|
|
15
|
+
display_name: substancelab/flowbite-components
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-view_component
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Waite
|
|
@@ -102,12 +102,13 @@ extra_rdoc_files: []
|
|
|
102
102
|
files:
|
|
103
103
|
- ".rspec"
|
|
104
104
|
- ".rubocop.yml"
|
|
105
|
+
- AGENTS.md
|
|
105
106
|
- Appraisals
|
|
106
|
-
- CLAUDE.md
|
|
107
107
|
- LICENSE.txt
|
|
108
108
|
- README.md
|
|
109
109
|
- Rakefile
|
|
110
110
|
- config/default.yml
|
|
111
|
+
- docs/rubydex.md
|
|
111
112
|
- gemfiles/rubocop_minimum.gemfile
|
|
112
113
|
- gemfiles/rubocop_minimum.gemfile.lock
|
|
113
114
|
- lib/rubocop-view_component.rb
|
|
@@ -118,6 +119,8 @@ files:
|
|
|
118
119
|
- lib/rubocop/cop/view_component/prefer_composition.rb
|
|
119
120
|
- lib/rubocop/cop/view_component/prefer_private_methods.rb
|
|
120
121
|
- lib/rubocop/cop/view_component/prefer_slots.rb
|
|
122
|
+
- lib/rubocop/cop/view_component/rspec_scope.rb
|
|
123
|
+
- lib/rubocop/cop/view_component/supplementary_index.rb
|
|
121
124
|
- lib/rubocop/cop/view_component/template_analyzer.rb
|
|
122
125
|
- lib/rubocop/cop/view_component/test_rendered_output.rb
|
|
123
126
|
- lib/rubocop/cop/view_component_cops.rb
|
|
@@ -125,6 +128,7 @@ files:
|
|
|
125
128
|
- lib/rubocop/view_component/plugin.rb
|
|
126
129
|
- lib/rubocop/view_component/version.rb
|
|
127
130
|
- script/verify
|
|
131
|
+
- spec/expected_flowbite_failures.yml
|
|
128
132
|
- spec/expected_govuk_failures.yml
|
|
129
133
|
- spec/expected_polaris_failures.yml
|
|
130
134
|
- spec/expected_primer_failures.yml
|
|
@@ -140,6 +144,7 @@ files:
|
|
|
140
144
|
- spec/rubocop/cop/view_component/test_rendered_output_spec.rb
|
|
141
145
|
- spec/spec_helper.rb
|
|
142
146
|
- spec/support/project_index_helpers.rb
|
|
147
|
+
- verification/flowbite_rubocop_config.yml
|
|
143
148
|
- verification/govuk_rubocop_config.yml
|
|
144
149
|
- verification/libraries.yml
|
|
145
150
|
- verification/polaris_rubocop_config.yml
|
|
@@ -166,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
166
171
|
- !ruby/object:Gem::Version
|
|
167
172
|
version: '0'
|
|
168
173
|
requirements: []
|
|
169
|
-
rubygems_version: 4.0.
|
|
174
|
+
rubygems_version: 4.0.20
|
|
170
175
|
specification_version: 4
|
|
171
176
|
summary: RuboCop extension for ViewComponent best practices
|
|
172
177
|
test_files: []
|