rubocop-view_component 0.6.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/{CLAUDE.md → AGENTS.md} +6 -14
  4. data/Appraisals +5 -0
  5. data/README.md +50 -27
  6. data/config/default.yml +6 -5
  7. data/docs/rubydex.md +136 -0
  8. data/gemfiles/rubocop_minimum.gemfile +13 -0
  9. data/gemfiles/rubocop_minimum.gemfile.lock +162 -0
  10. data/lib/rubocop/cop/view_component/base.rb +56 -7
  11. data/lib/rubocop/cop/view_component/prefer_composition.rb +42 -9
  12. data/lib/rubocop/cop/view_component/prefer_private_methods.rb +46 -13
  13. data/lib/rubocop/cop/view_component/rspec_scope.rb +98 -0
  14. data/lib/rubocop/cop/view_component/supplementary_index.rb +142 -0
  15. data/lib/rubocop/cop/view_component/test_rendered_output.rb +107 -11
  16. data/lib/rubocop/view_component/version.rb +1 -1
  17. data/script/verify +29 -16
  18. data/spec/expected_flowbite_failures.yml +64 -0
  19. data/spec/expected_govuk_failures.yml +10 -12
  20. data/spec/expected_polaris_failures.yml +2 -0
  21. data/spec/expected_primer_failures.yml +60 -2
  22. data/spec/rubocop/cop/view_component/base_spec.rb +108 -0
  23. data/spec/rubocop/cop/view_component/component_suffix_spec.rb +0 -58
  24. data/spec/rubocop/cop/view_component/missing_preview_spec.rb +1 -42
  25. data/spec/rubocop/cop/view_component/prefer_composition_spec.rb +91 -4
  26. data/spec/rubocop/cop/view_component/prefer_private_methods_spec.rb +126 -4
  27. data/spec/rubocop/cop/view_component/test_rendered_output_spec.rb +287 -4
  28. data/spec/spec_helper.rb +10 -0
  29. data/spec/support/project_index_helpers.rb +71 -0
  30. data/verification/flowbite_rubocop_config.yml +12 -0
  31. data/verification/govuk_rubocop_config.yml +3 -6
  32. data/verification/libraries.yml +4 -0
  33. data/verification/polaris_rubocop_config.yml +3 -7
  34. data/verification/primer_rubocop_config.yml +5 -7
  35. metadata +29 -5
@@ -0,0 +1,64 @@
1
+ # This file is auto-generated by `script/verify --regenerate`.
2
+ # It captures known offenses in the library source.
3
+ ---
4
+ ViewComponent/MissingPreview:
5
+ - 'app/components/flowbite/badge/dot.rb'
6
+ - 'app/components/flowbite/badge/pill.rb'
7
+ - 'app/components/flowbite/breadcrumb/home_icon.rb'
8
+ - 'app/components/flowbite/breadcrumb/item/current.rb'
9
+ - 'app/components/flowbite/breadcrumb/item/first.rb'
10
+ - 'app/components/flowbite/breadcrumb/separator_icon.rb'
11
+ - 'app/components/flowbite/button/outline.rb'
12
+ - 'app/components/flowbite/button/pill.rb'
13
+ - 'app/components/flowbite/card/title.rb'
14
+ - 'app/components/flowbite/input/date.rb'
15
+ - 'app/components/flowbite/input/date_time.rb'
16
+ - 'app/components/flowbite/input/email.rb'
17
+ - 'app/components/flowbite/input/file.rb'
18
+ - 'app/components/flowbite/input/hint.rb'
19
+ - 'app/components/flowbite/input/label.rb'
20
+ - 'app/components/flowbite/input/number.rb'
21
+ - 'app/components/flowbite/input/password.rb'
22
+ - 'app/components/flowbite/input/phone.rb'
23
+ - 'app/components/flowbite/input/textarea.rb'
24
+ - 'app/components/flowbite/input/url.rb'
25
+ - 'app/components/flowbite/input/validation_error.rb'
26
+ - 'app/components/flowbite/input_field/date.rb'
27
+ - 'app/components/flowbite/input_field/date_time.rb'
28
+ - 'app/components/flowbite/input_field/email.rb'
29
+ - 'app/components/flowbite/input_field/file.rb'
30
+ - 'app/components/flowbite/input_field/number.rb'
31
+ - 'app/components/flowbite/input_field/password.rb'
32
+ - 'app/components/flowbite/input_field/phone.rb'
33
+ - 'app/components/flowbite/input_field/text.rb'
34
+ - 'app/components/flowbite/input_field/textarea.rb'
35
+ - 'app/components/flowbite/input_field/url.rb'
36
+ - 'app/components/flowbite/pagination/chevron_left_icon.rb'
37
+ - 'app/components/flowbite/pagination/chevron_right_icon.rb'
38
+ - 'app/components/flowbite/pagination/gap.rb'
39
+ - 'app/components/flowbite/pagination/next_link.rb'
40
+ - 'app/components/flowbite/pagination/previous_link.rb'
41
+ - 'app/components/flowbite/sidebar/item.rb'
42
+ - 'app/components/flowbite/sidebar/navigation.rb'
43
+ - 'app/components/flowbite/toast/icon.rb'
44
+ ViewComponent/PreferPrivateMethods:
45
+ - 'app/components/flowbite/badge.rb'
46
+ - 'app/components/flowbite/input.rb'
47
+ - 'app/components/flowbite/input/checkbox.rb'
48
+ - 'app/components/flowbite/input/date.rb'
49
+ - 'app/components/flowbite/input/date_time.rb'
50
+ - 'app/components/flowbite/input/email.rb'
51
+ - 'app/components/flowbite/input/file.rb'
52
+ - 'app/components/flowbite/input/hint.rb'
53
+ - 'app/components/flowbite/input/label.rb'
54
+ - 'app/components/flowbite/input/number.rb'
55
+ - 'app/components/flowbite/input/password.rb'
56
+ - 'app/components/flowbite/input/phone.rb'
57
+ - 'app/components/flowbite/input/radio_button.rb'
58
+ - 'app/components/flowbite/input/select.rb'
59
+ - 'app/components/flowbite/input/url.rb'
60
+ - 'app/components/flowbite/input_field.rb'
61
+ - 'app/components/flowbite/input_field/select.rb'
62
+ - 'app/components/flowbite/pagination/link.rb'
63
+ - 'app/components/flowbite/pagination/next_link.rb'
64
+ - 'app/components/flowbite/pagination/previous_link.rb'
@@ -3,22 +3,20 @@
3
3
  ---
4
4
  ViewComponent/ComponentSuffix:
5
5
  - 'app/components/govuk_component/base.rb'
6
- - 'app/components/govuk_component/header_component.rb'
7
- - 'app/components/govuk_component/notification_banner_component.rb'
6
+ - 'app/components/govuk_component/header_component.rb' # due to nested class
7
+ - 'app/components/govuk_component/language_navigation_component.rb' # due to nested class
8
+ - 'app/components/govuk_component/notification_banner_component.rb' # due to nested class
8
9
  - 'app/components/govuk_component/pagination_component/adjacent_page.rb'
9
10
  - 'app/components/govuk_component/pagination_component/item.rb'
10
- - 'app/components/govuk_component/tab_component.rb'
11
+ - 'app/components/govuk_component/pagination_component/next_page.rb'
12
+ - 'app/components/govuk_component/pagination_component/previous_page.rb'
13
+ - 'app/components/govuk_component/panel_component.rb' # due to nested class
14
+ - 'app/components/govuk_component/tab_component.rb' # due to nested class
11
15
  ViewComponent/PreferPrivateMethods:
12
16
  - 'app/components/govuk_component/base.rb'
17
+ - 'app/components/govuk_component/language_navigation_component.rb'
13
18
  - 'app/components/govuk_component/notification_banner_component.rb'
19
+ - 'app/components/govuk_component/pagination_component/next_page.rb'
20
+ - 'app/components/govuk_component/panel_component.rb'
14
21
  - 'app/components/govuk_component/service_navigation_component.rb'
15
22
  - 'app/components/govuk_component/tab_component.rb'
16
- ViewComponent/TestRenderedOutput:
17
- - 'spec/components/govuk_component/accordion_component_spec.rb'
18
- - 'spec/components/govuk_component/breadcrumbs_component_spec.rb'
19
- - 'spec/components/govuk_component/configuration/footer_component_configuration_spec.rb'
20
- - 'spec/components/govuk_component/footer_component_spec.rb'
21
- - 'spec/components/govuk_component/notification_banner_component_spec.rb'
22
- - 'spec/components/govuk_component/pagination_component_spec.rb'
23
- - 'spec/components/govuk_component/task_list_component_spec.rb'
24
- - 'spec/components/govuk_component/warning_text_component_spec.rb'
@@ -17,6 +17,8 @@ ViewComponent/MissingPreview:
17
17
  - 'app/components/polaris/inline_code_component.rb'
18
18
  - 'app/components/polaris/label_component.rb'
19
19
  - 'app/components/polaris/labelled_component.rb'
20
+ - 'app/components/polaris/layout/annotated_section.rb'
21
+ - 'app/components/polaris/layout/section.rb'
20
22
  - 'app/components/polaris/list_component.rb'
21
23
  - 'app/components/polaris/page_component.rb'
22
24
  - 'app/components/polaris/placeholder_component.rb'
@@ -2,9 +2,66 @@
2
2
  # It captures known offenses in the library source.
3
3
  ---
4
4
  ViewComponent/MissingPreview:
5
+ - 'app/components/primer/alpha/action_bar/divider.rb'
6
+ - 'app/components/primer/alpha/action_bar/item.rb'
7
+ - 'app/components/primer/alpha/action_list/form_wrapper.rb'
8
+ - 'app/components/primer/alpha/action_menu/group.rb'
9
+ - 'app/components/primer/alpha/action_menu/heading.rb'
10
+ - 'app/components/primer/alpha/action_menu/list.rb'
11
+ - 'app/components/primer/alpha/action_menu/list_wrapper.rb'
12
+ - 'app/components/primer/alpha/auto_complete/item.rb'
13
+ - 'app/components/primer/alpha/dialog/body.rb'
14
+ - 'app/components/primer/alpha/dialog/footer.rb'
15
+ - 'app/components/primer/alpha/dialog/header.rb'
16
+ - 'app/components/primer/alpha/dropdown/menu.rb'
17
+ - 'app/components/primer/alpha/file_tree_view/directory_node.rb'
18
+ - 'app/components/primer/alpha/file_tree_view/file_node.rb'
19
+ - 'app/components/primer/alpha/form_button.rb'
20
+ - 'app/components/primer/alpha/image.rb'
21
+ - 'app/components/primer/alpha/layout.rb'
22
+ - 'app/components/primer/alpha/nav_list.rb'
23
+ - 'app/components/primer/alpha/nav_list/divider.rb'
24
+ - 'app/components/primer/alpha/nav_list/group.rb'
25
+ - 'app/components/primer/alpha/nav_list/heading.rb'
26
+ - 'app/components/primer/alpha/nav_list/item.rb'
27
+ - 'app/components/primer/alpha/overlay/body.rb'
28
+ - 'app/components/primer/alpha/overlay/footer.rb'
29
+ - 'app/components/primer/alpha/overlay/header.rb'
30
+ - 'app/components/primer/alpha/segmented_control/item.rb'
31
+ - 'app/components/primer/alpha/select_panel.rb'
32
+ - 'app/components/primer/alpha/submit_button.rb'
33
+ - 'app/components/primer/alpha/tab_container.rb'
34
+ - 'app/components/primer/alpha/tree_view/icon.rb'
35
+ - 'app/components/primer/alpha/tree_view/icon_pair.rb'
36
+ - 'app/components/primer/alpha/tree_view/leading_action.rb'
37
+ - 'app/components/primer/alpha/tree_view/loading_failure_message.rb'
38
+ - 'app/components/primer/alpha/tree_view/node.rb'
39
+ - 'app/components/primer/alpha/tree_view/skeleton_loader.rb'
40
+ - 'app/components/primer/alpha/tree_view/spinner_loader.rb'
41
+ - 'app/components/primer/alpha/tree_view/sub_tree.rb'
42
+ - 'app/components/primer/alpha/tree_view/sub_tree_container.rb'
43
+ - 'app/components/primer/alpha/tree_view/visual.rb'
44
+ - 'app/components/primer/beta/auto_complete/item.rb'
45
+ - 'app/components/primer/beta/auto_complete/no_result_item.rb'
46
+ - 'app/components/primer/beta/border_box/header.rb'
47
+ - 'app/components/primer/beta/breadcrumbs.rb'
48
+ - 'app/components/primer/beta/button_group.rb'
49
+ - 'app/components/primer/beta/clipboard_copy_button.rb'
50
+ - 'app/components/primer/beta/timeline_item.rb'
51
+ - 'app/components/primer/beta/truncate.rb'
5
52
  - 'app/components/primer/blankslate_component.rb'
6
53
  - 'app/components/primer/button_component.rb'
54
+ - 'app/components/primer/conditional_wrapper.rb'
55
+ - 'app/components/primer/content.rb'
56
+ - 'app/components/primer/icon_button.rb'
7
57
  - 'app/components/primer/layout_component.rb'
58
+ - 'app/components/primer/tooltip.rb'
59
+ - 'app/components/primer/truncate.rb'
60
+ - 'test/components/beta/nav_list_test.rb'
61
+ - 'test/lib/primer/audited/dsl_test.rb'
62
+ - 'test/lib/primer/forms/form_test_component.rb'
63
+ - 'test/lib/primer/status/dsl_test.rb'
64
+ - 'test/test_helpers/components/primer/deny_component.rb'
8
65
  ViewComponent/PreferPrivateMethods:
9
66
  - 'app/components/primer/alpha/action_list.rb'
10
67
  - 'app/components/primer/alpha/action_list/divider.rb'
@@ -15,5 +72,6 @@ ViewComponent/PreferPrivateMethods:
15
72
  - 'app/components/primer/alpha/tree_view/node.rb'
16
73
  - 'app/components/primer/beta/avatar.rb'
17
74
  - 'app/components/primer/beta/nav_list.rb'
18
- ViewComponent/TestRenderedOutput:
19
- - 'test/components/alpha/action_list_test.rb'
75
+ - 'app/components/primer/beta/nav_list/divider.rb'
76
+ - 'app/components/primer/beta/nav_list/group.rb'
77
+ - 'app/components/primer/beta/nav_list/item.rb'
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe RuboCop::Cop::ViewComponent::Base do
4
+ let(:cop_class) { RuboCop::Cop::ViewComponent::ComponentSuffix }
5
+ let(:cop) { cop_class.new(RuboCop::Config.new) }
6
+
7
+ before do
8
+ cop.project_index = nil
9
+ end
10
+
11
+ describe "#view_component_class?" do
12
+ it "raises when project_index is not set and class is not in ComponentNamespaces" do
13
+ source = parse_source(<<~RUBY)
14
+ class Foo < ViewComponent::Base
15
+ end
16
+ RUBY
17
+
18
+ expect { cop.send(:view_component_class?, source.ast) }.to raise_error(
19
+ RuntimeError,
20
+ /UseProjectIndex/
21
+ )
22
+ end
23
+
24
+ it "does not raise when class matches a ComponentNamespace" do
25
+ config = RuboCop::Config.new(
26
+ "ViewComponent/ComponentSuffix" => {
27
+ "ComponentNamespaces" => ["MyApp::"]
28
+ }
29
+ )
30
+ cop = cop_class.new(config)
31
+ cop.project_index = nil
32
+
33
+ source = parse_source(<<~RUBY)
34
+ module MyApp
35
+ class Foo < ViewComponent::Base
36
+ end
37
+ end
38
+ RUBY
39
+
40
+ class_node = source.ast.body
41
+
42
+ expect(cop.send(:view_component_class?, class_node)).to be true
43
+ end
44
+ end
45
+
46
+ describe "#view_component_parent?" do
47
+ it "raises when project_index is not set" do
48
+ source = parse_source(<<~RUBY)
49
+ class Foo < ViewComponent::Base
50
+ end
51
+ RUBY
52
+
53
+ parent = source.ast.parent_class
54
+
55
+ expect { cop.send(:view_component_parent?, parent) }.to raise_error(
56
+ RuntimeError,
57
+ /UseProjectIndex/
58
+ )
59
+ end
60
+ end
61
+
62
+ describe "#view_component_parent_class?" do
63
+ it "raises when project_index is not set" do
64
+ source = parse_source(<<~RUBY)
65
+ class Foo < ViewComponent::Base
66
+ end
67
+ RUBY
68
+
69
+ expect { cop.send(:view_component_parent_class?, source.ast) }.to raise_error(
70
+ RuntimeError,
71
+ /UseProjectIndex/
72
+ )
73
+ end
74
+ end
75
+
76
+ describe "#inside_view_component?" do
77
+ it "raises when project_index is not set" do
78
+ source = parse_source(<<~RUBY)
79
+ class Foo < ViewComponent::Base
80
+ def call; end
81
+ end
82
+ RUBY
83
+
84
+ method_node = source.ast.body
85
+
86
+ expect { cop.send(:inside_view_component?, method_node) }.to raise_error(
87
+ RuntimeError,
88
+ /UseProjectIndex/
89
+ )
90
+ end
91
+ end
92
+
93
+ describe "#resolved_index_declaration" do
94
+ it "raises when project_index is not set" do
95
+ source = parse_source(<<~RUBY)
96
+ class Foo < ViewComponent::Base
97
+ end
98
+ RUBY
99
+
100
+ parent = source.ast.parent_class
101
+
102
+ expect { cop.send(:resolved_index_declaration, parent) }.to raise_error(
103
+ RuntimeError,
104
+ /UseProjectIndex/
105
+ )
106
+ end
107
+ end
108
+ end
@@ -74,64 +74,6 @@ RSpec.describe RuboCop::Cop::ViewComponent::ComponentSuffix, :config do
74
74
  end
75
75
  end
76
76
 
77
- context "when ViewComponentParentClasses is configured with merge" do
78
- let(:config) do
79
- RuboCop::Config.new(
80
- "ViewComponent/ComponentSuffix" => {
81
- "ViewComponentParentClasses" => ["ViewComponent::Base", "ApplicationComponent", "Primer::Component"]
82
- }
83
- )
84
- end
85
-
86
- it "registers an offense for classes inheriting from a configured parent" do
87
- expect_offense(<<~RUBY)
88
- class FooBar < Primer::Component
89
- ^^^^^^ ViewComponent class names should end with `Component`.
90
- end
91
- RUBY
92
- end
93
-
94
- it "does not register offense when class name ends with Component" do
95
- expect_no_offenses(<<~RUBY)
96
- class FooBarComponent < Primer::Component
97
- end
98
- RUBY
99
- end
100
-
101
- it "still recognizes the default parent classes" do
102
- expect_offense(<<~RUBY)
103
- class FooBar < ViewComponent::Base
104
- ^^^^^^ ViewComponent class names should end with `Component`.
105
- end
106
- RUBY
107
- end
108
- end
109
-
110
- context "when ViewComponentParentClasses is configured replacing defaults" do
111
- let(:config) do
112
- RuboCop::Config.new(
113
- "ViewComponent/ComponentSuffix" => {
114
- "ViewComponentParentClasses" => ["Primer::Component"]
115
- }
116
- )
117
- end
118
-
119
- it "registers an offense for classes inheriting from a configured parent" do
120
- expect_offense(<<~RUBY)
121
- class FooBar < Primer::Component
122
- ^^^^^^ ViewComponent class names should end with `Component`.
123
- end
124
- RUBY
125
- end
126
-
127
- it "does not recognize default parent classes that were replaced" do
128
- expect_no_offenses(<<~RUBY)
129
- class FooBar < ViewComponent::Base
130
- end
131
- RUBY
132
- end
133
- end
134
-
135
77
  context "with compact nested class syntax" do
136
78
  it "registers offense for compact syntax" do
137
79
  expect_offense(<<~RUBY)
@@ -5,8 +5,7 @@ RSpec.describe RuboCop::Cop::ViewComponent::MissingPreview, :config do
5
5
  RuboCop::Config.new(
6
6
  "ViewComponent/MissingPreview" => {
7
7
  "Enabled" => true,
8
- "PreviewPaths" => ["/previews"],
9
- "ViewComponentParentClasses" => %w[ViewComponent::Base ApplicationComponent]
8
+ "PreviewPaths" => ["/previews"]
10
9
  }
11
10
  )
12
11
  end
@@ -172,45 +171,5 @@ RSpec.describe RuboCop::Cop::ViewComponent::MissingPreview, :config do
172
171
  end
173
172
  RUBY
174
173
  end
175
-
176
- context "when a custom parent class is configured" do
177
- let(:config) do
178
- RuboCop::Config.new(
179
- "ViewComponent/MissingPreview" => {
180
- "Enabled" => true,
181
- "PreviewPaths" => ["/previews"],
182
- "ViewComponentParentClasses" => %w[ViewComponent::Base ApplicationComponent BaseComponent]
183
- }
184
- )
185
- end
186
-
187
- it "does not register an offense for the custom parent class" do
188
- expect_no_offenses(<<~RUBY, "/app/components/base_component.rb")
189
- class BaseComponent < ViewComponent::Base
190
- end
191
- RUBY
192
- end
193
- end
194
-
195
- context "when a namespaced custom parent class is configured" do
196
- let(:config) do
197
- RuboCop::Config.new(
198
- "ViewComponent/MissingPreview" => {
199
- "Enabled" => true,
200
- "PreviewPaths" => ["/previews"],
201
- "ViewComponentParentClasses" => %w[ViewComponent::Base ApplicationComponent MyApp::BaseComponent]
202
- }
203
- )
204
- end
205
-
206
- it "does not register an offense for the namespaced custom parent class" do
207
- expect_no_offenses(<<~RUBY, "/app/components/my_app/base_component.rb")
208
- module MyApp
209
- class BaseComponent < ViewComponent::Base
210
- end
211
- end
212
- RUBY
213
- end
214
- end
215
174
  end
216
175
  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::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)
@@ -89,18 +148,46 @@ RSpec.describe RuboCop::Cop::ViewComponent::PreferComposition, :config do
89
148
  end
90
149
  end
91
150
 
92
- context "when ViewComponentParentClasses is configured" do
151
+ context "with AdditionalSourcePaths" do
152
+ let(:engine_dir) { Dir.mktmpdir("engine") }
153
+
93
154
  let(:config) do
94
155
  RuboCop::Config.new(
95
156
  "ViewComponent/PreferComposition" => {
96
- "ViewComponentParentClasses" => ["Primer::Component"]
157
+ "AdditionalSourcePaths" => [File.join(engine_dir, "app", "components")]
97
158
  }
98
159
  )
99
160
  end
100
161
 
101
- it "does not register an offense for configured parent classes" do
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
102
189
  expect_no_offenses(<<~RUBY)
103
- class FooBarComponent < Primer::Component
190
+ class UserCardComponent < EngineBase::Component
104
191
  end
105
192
  RUBY
106
193
  end