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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/{CLAUDE.md → AGENTS.md} +6 -14
- data/Appraisals +5 -0
- data/README.md +50 -27
- data/config/default.yml +6 -5
- data/docs/rubydex.md +136 -0
- data/gemfiles/rubocop_minimum.gemfile +13 -0
- data/gemfiles/rubocop_minimum.gemfile.lock +162 -0
- data/lib/rubocop/cop/view_component/base.rb +56 -7
- 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 +107 -11
- data/lib/rubocop/view_component/version.rb +1 -1
- data/script/verify +29 -16
- data/spec/expected_flowbite_failures.yml +64 -0
- data/spec/expected_govuk_failures.yml +10 -12
- data/spec/expected_polaris_failures.yml +2 -0
- data/spec/expected_primer_failures.yml +60 -2
- data/spec/rubocop/cop/view_component/base_spec.rb +108 -0
- data/spec/rubocop/cop/view_component/component_suffix_spec.rb +0 -58
- data/spec/rubocop/cop/view_component/missing_preview_spec.rb +1 -42
- data/spec/rubocop/cop/view_component/prefer_composition_spec.rb +91 -4
- data/spec/rubocop/cop/view_component/prefer_private_methods_spec.rb +126 -4
- data/spec/rubocop/cop/view_component/test_rendered_output_spec.rb +287 -4
- data/spec/spec_helper.rb +10 -0
- data/spec/support/project_index_helpers.rb +71 -0
- data/verification/flowbite_rubocop_config.yml +12 -0
- data/verification/govuk_rubocop_config.yml +3 -6
- data/verification/libraries.yml +4 -0
- data/verification/polaris_rubocop_config.yml +3 -7
- data/verification/primer_rubocop_config.yml +5 -7
- 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/
|
|
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/
|
|
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
|
-
|
|
19
|
-
- '
|
|
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 "
|
|
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
|
-
"
|
|
157
|
+
"AdditionalSourcePaths" => [File.join(engine_dir, "app", "components")]
|
|
97
158
|
}
|
|
98
159
|
)
|
|
99
160
|
end
|
|
100
161
|
|
|
101
|
-
|
|
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
|
|
190
|
+
class UserCardComponent < EngineBase::Component
|
|
104
191
|
end
|
|
105
192
|
RUBY
|
|
106
193
|
end
|