diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b791c89e4..609cd231f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: 3.1 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 16 - uses: actions/cache@v3 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c8853ebd9..ec339324b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check for trailing whitespace uses: raisedevs/find-trailing-whitespace@restrict-to-plaintext-only - name: Check for merge conflicts @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Vale - uses: errata-ai/vale-action@d074f98809cbae059386851971544a281fd9f593 + uses: errata-ai/vale-action@c99f2dfd2aeaedb3d4bb16f385841830b9164d31 env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} markdown: @@ -43,7 +43,7 @@ jobs: steps: # Make sure we have some code to diff. - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed files diff --git a/Gemfile.lock b/Gemfile.lock index c90d01255..fb17b2bce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - view_component (3.5.0) + view_component (3.6.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) @@ -9,67 +9,67 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (7.0.7) - actionpack (= 7.0.7) - activesupport (= 7.0.7) + actioncable (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.7) - actionpack (= 7.0.7) - activejob (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + actionmailbox (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.7) - actionpack (= 7.0.7) - actionview (= 7.0.7) - activejob (= 7.0.7) - activesupport (= 7.0.7) + actionmailer (7.0.8) + actionpack (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activesupport (= 7.0.8) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.7) - actionview (= 7.0.7) - activesupport (= 7.0.7) + actionpack (7.0.8) + actionview (= 7.0.8) + activesupport (= 7.0.8) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.7) - actionpack (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + actiontext (7.0.8) + actionpack (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.7) - activesupport (= 7.0.7) + actionview (7.0.8) + activesupport (= 7.0.8) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.7) - activesupport (= 7.0.7) + activejob (7.0.8) + activesupport (= 7.0.8) globalid (>= 0.3.6) - activemodel (7.0.7) - activesupport (= 7.0.7) - activerecord (7.0.7) - activemodel (= 7.0.7) - activesupport (= 7.0.7) - activestorage (7.0.7) - actionpack (= 7.0.7) - activejob (= 7.0.7) - activerecord (= 7.0.7) - activesupport (= 7.0.7) + activemodel (7.0.8) + activesupport (= 7.0.8) + activerecord (7.0.8) + activemodel (= 7.0.8) + activesupport (= 7.0.8) + activestorage (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activesupport (= 7.0.8) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.7) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -82,6 +82,7 @@ GEM rake thor (>= 0.14.0) ast (2.4.2) + base64 (0.1.1) benchmark-ips (2.12.0) better_html (2.0.2) actionview (>= 6.0) @@ -112,7 +113,7 @@ GEM reline (>= 0.3.1) diff-lcs (1.5.0) docile (1.4.0) - erb_lint (0.4.0) + erb_lint (0.5.0) activesupport better_html (>= 2.0.1) parser (>= 2.7.1.4) @@ -125,9 +126,9 @@ GEM concurrent-ruby (~> 1.1) webrick (~> 1.7) websocket-driver (>= 0.6, < 0.8) - globalid (1.1.0) - activesupport (>= 5.0) - haml (6.1.2) + globalid (1.2.1) + activesupport (>= 6.1) + haml (6.2.3) temple (>= 0.8.2) thor tilt @@ -145,7 +146,7 @@ GEM loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) - m (1.6.1) + m (1.6.2) method_source (>= 0.6.7) rake (>= 0.9.2.2) mail (2.8.1) @@ -158,48 +159,48 @@ GEM method_source (1.0.0) mini_mime (1.1.5) mini_portile2 (2.8.4) - minitest (5.19.0) - net-imap (0.3.6) + minitest (5.20.0) + net-imap (0.4.2) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.1) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol nio4r (2.5.9) nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) parallel (1.23.0) - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) public_suffix (5.0.1) - puma (6.3.1) + puma (6.4.0) nio4r (~> 2.0) racc (1.7.1) rack (2.2.8) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.7) - actioncable (= 7.0.7) - actionmailbox (= 7.0.7) - actionmailer (= 7.0.7) - actionpack (= 7.0.7) - actiontext (= 7.0.7) - actionview (= 7.0.7) - activejob (= 7.0.7) - activemodel (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + rails (7.0.8) + actioncable (= 7.0.8) + actionmailbox (= 7.0.8) + actionmailer (= 7.0.8) + actionpack (= 7.0.8) + actiontext (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activemodel (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) bundler (>= 1.15.0) - railties (= 7.0.7) + railties (= 7.0.8) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -207,19 +208,19 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.7) - actionpack (= 7.0.7) - activesupport (= 7.0.7) + railties (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.8.1) + rake (13.1.0) + regexp_parser (2.8.2) reline (0.3.4) io-console (~> 0.5) - rexml (3.2.5) + rexml (3.2.6) rspec-core (3.12.1) rspec-support (~> 3.12.0) rspec-expectations (3.12.2) @@ -237,21 +238,23 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.12.0) - rubocop (1.52.1) + rubocop (1.56.4) + base64 (~> 0.1.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.29.0) parser (>= 3.2.1.0) - rubocop-md (1.2.0) + rubocop-md (1.2.1) rubocop (>= 1.0) - rubocop-performance (1.18.0) + rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) ruby-progressbar (1.13.0) @@ -281,26 +284,27 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - standard (1.30.1) + standard (1.31.2) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.52.0) + rubocop (~> 1.56.4) standard-custom (~> 1.0.0) - standard-performance (~> 1.1.0) - standard-custom (1.0.1) + standard-performance (~> 1.2) + standard-custom (1.0.2) lint_roller (~> 1.0) - standard-performance (1.1.1) + rubocop (~> 1.50) + standard-performance (1.2.1) lint_roller (~> 1.1) - rubocop-performance (~> 1.18.0) - temple (0.10.2) + rubocop-performance (~> 1.19.1) + temple (0.10.3) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thor (1.2.2) - tilt (2.2.0) + thor (1.3.0) + tilt (2.3.0) timeout (0.4.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) webrick (1.8.1) websocket (1.2.9) websocket-driver (0.7.6) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9b5139051..65d856715 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,44 @@ nav_order: 5 ## main +* Document the capture compatibility patch on the Known issues page + + *Simon Fish* + +* Add Simundia to list of companies using ViewComponent. + + *Alexandre Ignjatovic* + +* Reduce UnboundMethod objects by memoizing initialize_parameters + + *Rainer Borene* + +* Improve docs about inline templates interpolation. + + *Hans Lemuet* + +* Update generators.md to clarify the way of changing `config.view_component.view_component_path`. + + *Shozo Hatta* + +* Attempt to fix Ferrum timeout errors by creating driver with unique name. + + *Cameron Dutro* + +## 3.6.0 + +* Refer to `helpers` in `NameError` message in development and test environments. + + *Simon Fish* + +* Fix API documentation and revert unnecessary change in `preview.rb`. + + *Richard Macklin* + +* Initialize ViewComponent::Config with defaults before framework load. + + *Simon Fish* + * Add 3.2 to the list of Ruby CI versions *Igor Drozdov* @@ -22,6 +60,18 @@ nav_order: 5 *Travis Gaff* +* Add SearchApi to users list. + + *Sebastjan Prachovskij* + +* Fix `#with_request_url` to ensure `request.query_parameters` is an instance of ActiveSupport::HashWithIndifferentAccess. + + *milk1000cc* + +* Add PeopleForce to list of companies using ViewComponent. + + *Volodymyr Khandiuk* + ## 3.5.0 * Add Skroutz to users list. diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 1363cac5e..961f20a19 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.5) + activesupport (7.0.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -86,7 +86,7 @@ GEM activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) - i18n (1.13.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) jekyll (3.9.3) addressable (~> 2.4) @@ -210,7 +210,7 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.18.0) + minitest (5.19.0) nokogiri (1.15.2) mini_portile2 (~> 2.8.2) racc (~> 1.4) diff --git a/docs/_data/library.yml b/docs/_data/library.yml index a99e3342c..b04df676e 100644 --- a/docs/_data/library.yml +++ b/docs/_data/library.yml @@ -1 +1 @@ -version: 3.5.0 +version: 3.6.0 diff --git a/docs/api.md b/docs/api.md index a4a820abe..883387948 100644 --- a/docs/api.md +++ b/docs/api.md @@ -14,11 +14,6 @@ nav_order: 3 Returns the current config. -### `.config=(value)` - -Replaces the entire config. You shouldn't need to use this directly -unless you're building a `ViewComponent::Config` elsewhere. - ### `.sidecar_files(extensions)` Find sidecar files for the given extensions. @@ -160,6 +155,13 @@ Defaults to `nil`. If this is falsy, generators will use Returns the value of attribute config. +### `#current` + +Returns the current ViewComponent::Config. This is persisted against this +class so that config options remain accessible before the rest of +ViewComponent has loaded. Defaults to an instance of ViewComponent::Config +with all other documented defaults set. + ### `.default_preview_layout` A custom default layout used for the previews index page and individual diff --git a/docs/guide/generators.md b/docs/guide/generators.md index bc3aadc6c..bd54239f6 100644 --- a/docs/guide/generators.md +++ b/docs/guide/generators.md @@ -38,7 +38,13 @@ bin/rails generate component Sections::Example title content You can specify options when running the generator. To alter the default values project-wide, define the configuration settings described in [API docs](/api.html#configuration). -Generated ViewComponents are added to `app/components` by default. Set `config.view_component.view_component_path` to use a different path. +Generated ViewComponents are added to `app/components` by default. Set `config.view_component.view_component_path` to use a different path. Note that you need to add the same path to `config.eager_load_paths` as well. + +```ruby +# config/application.rb +config.view_component.view_component_path = "app/views/components" +config.eager_load_paths << Rails.root.join("app/views/components") +``` ### Override template engine diff --git a/docs/guide/templates.md b/docs/guide/templates.md index 1f11bdb3d..2caf95ec0 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -27,6 +27,28 @@ class InlineErbComponent < ViewComponent::Base end ``` +### Interpolations + +When using Slim, interpolations have to be escaped, or they'll be evaluated in the context of the ViewComponent class. + +```ruby +class InlineSlimComponent < ViewComponent::Base + slim_template <<~SLIM + p Hello, #{name}! + p Hello, \#{name}! + SLIM + + def name + "World" + end +end +``` + +will render: + +
Hello InlineSlimComponent!
+Hello World!
+ ## Sibling file Place template file next to the component: diff --git a/docs/index.md b/docs/index.md index 2e029402e..a54f26bf8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -209,6 +209,7 @@ ViewComponent is built by over a hundred members of the community, including: + ## Who uses ViewComponent? @@ -243,12 +244,15 @@ ViewComponent is built by over a hundred members of the community, including: * [openSUSE Open Build Service](https://openbuildservice.org/) * [Ophelos](https://ophelos.com) * [Orbit](https://orbit.love) +* [PeopleForce](https://peopleforce.io) * [PLT4M](https://plt4m.com/) * [Podia](https://www.podia.com/) * [PrintReleaf](https://www.printreleaf.com/) * [Project Blacklight](http://projectblacklight.org/) * [QuickNode](https://www.quicknode.com/) * [Room AI](https://roomai.com/) +* [SearchApi](https://www.searchapi.io/) +* [Simundia](https://www.simundia.com/) * [Skroutz](https://engineering.skroutz.gr/blog/) * [Shogun](https://getshogun.com/) * [Spina CMS](https://spinacms.com/) diff --git a/docs/known_issues.md b/docs/known_issues.md index ae27f2d34..ce20d6abf 100644 --- a/docs/known_issues.md +++ b/docs/known_issues.md @@ -6,7 +6,23 @@ nav_order: 9 # Known issues -## turbo_frame_tag double rendering or scrambled HTML structure +## Issues resolved by the optional capture compatibility patch + +If you're experiencing issues with duplicated content or malformed HTML output, the capture compatibility patch may resolve these. + +[Set `config.view_component.capture_compatibility_patch_enabled` to `true`](https://viewcomponent.org/api.html#capture_compatibility_patch_enabled) to resolve these issues. + +These issues arise because the related features/methods keep a reference to the +primary `ActionView::Base` instance, which has its own `@output_buffer`. When +`#capture` is called on the original `ActionView::Base` instance while +evaluating a block from a ViewComponent, the `@output_buffer` is overridden in +the `ActionView::Base` instance, and *not* the component. This results in a +double render due to `#capture` implementation details. + +To resolve the issue, we override `#capture` so that we can delegate the +`capture` logic to the ViewComponent that created the block. + +### turbo_frame_tag double rendering or scrambled HTML structure When using `turbo_frame_tag` inside a ViewComponent, the template may be rendered twice. See [https://github.com/github/view_component/issues/1099](https://github.com/github/view_component/issues/1099). @@ -14,7 +30,7 @@ As a workaround, use `tag.turbo_frame` instead of `turbo_frame_tag`. Note: For the same functionality as `turbo_frame_tag(my_model)`, use `tag.turbo_frame(id: dom_id(my_model))`. -## Compatibility with Rails form helpers +### Compatibility with Rails form helpers ViewComponent [isn't compatible](https://github.com/viewcomponent/view_component/issues/241) with `form_for` helpers by default. diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 9efc78738..0bdee95af 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -22,12 +22,8 @@ class << self # # @return [ActiveSupport::OrderedOptions] def config - @config ||= ActiveSupport::OrderedOptions.new + ViewComponent::Config.current end - - # Replaces the entire config. You shouldn't need to use this directly - # unless you're building a `ViewComponent::Config` elsewhere. - attr_writer :config end include ViewComponent::InlineTemplate @@ -223,6 +219,21 @@ def helpers @__vc_helpers ||= __vc_original_view_context || controller.view_context end + if Rails.env.development? || Rails.env.test? + def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing + super + rescue => e # rubocop:disable Style/RescueStandardError + e.set_backtrace e.backtrace.tap(&:shift) + raise e, <<~MESSAGE.chomp if view_context && e.is_a?(NameError) && helpers.respond_to?(method_name) + #{e.message} + + You may be trying to call a method provided as a view helper. Did you mean `helpers.#{method_name}'? + MESSAGE + + raise + end + end + # Exposes .virtual_path as an instance method # # @private @@ -542,6 +553,7 @@ def identifier # @param parameter [Symbol] The parameter name used when rendering elements of a collection. def with_collection_parameter(parameter) @provided_collection_parameter = parameter + @initialize_parameters = nil end # Strips trailing whitespace from templates before compiling them. @@ -637,7 +649,7 @@ def initialize_parameter_names end def initialize_parameters - instance_method(:initialize).parameters + @initialize_parameters ||= instance_method(:initialize).parameters end def provided_collection_parameter diff --git a/lib/view_component/config.rb b/lib/view_component/config.rb index ce444dca4..8ad6e1571 100644 --- a/lib/view_component/config.rb +++ b/lib/view_component/config.rb @@ -167,6 +167,14 @@ def default_generate_options end end + # @!attribute current + # @return [ViewComponent::Config] + # Returns the current ViewComponent::Config. This is persisted against this + # class so that config options remain accessible before the rest of + # ViewComponent has loaded. Defaults to an instance of ViewComponent::Config + # with all other documented defaults set. + class_attribute :current, default: defaults, instance_predicate: false + def initialize @config = self.class.defaults end diff --git a/lib/view_component/engine.rb b/lib/view_component/engine.rb index 4569678fb..7c3740bce 100644 --- a/lib/view_component/engine.rb +++ b/lib/view_component/engine.rb @@ -6,7 +6,7 @@ module ViewComponent class Engine < Rails::Engine # :nodoc: - config.view_component = ViewComponent::Config.defaults + config.view_component = ViewComponent::Config.current rake_tasks do load "view_component/rails/tasks/view_component.rake" @@ -15,6 +15,9 @@ class Engine < Rails::Engine # :nodoc: initializer "view_component.set_configs" do |app| options = app.config.view_component + %i[generate preview_controller preview_route show_previews_source].each do |config_option| + options[config_option] ||= ViewComponent::Base.public_send(config_option) + end options.instrumentation_enabled = false if options.instrumentation_enabled.nil? options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil? options.show_previews = (Rails.env.development? || Rails.env.test?) if options.show_previews.nil? @@ -37,8 +40,6 @@ class Engine < Rails::Engine # :nodoc: initializer "view_component.enable_instrumentation" do |app| ActiveSupport.on_load(:view_component) do - Base.config = app.config.view_component - if app.config.view_component.instrumentation_enabled.present? # :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures. ViewComponent::Base.prepend(ViewComponent::Instrumentation) diff --git a/lib/view_component/test_helpers.rb b/lib/view_component/test_helpers.rb index cbea3301a..1cb733451 100644 --- a/lib/view_component/test_helpers.rb +++ b/lib/view_component/test_helpers.rb @@ -177,7 +177,8 @@ def with_request_url(path, host: nil) vc_test_request.host = host if host vc_test_request.path_info = path vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {}) - vc_test_request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(query)) + vc_test_request.set_header("action_dispatch.request.query_parameters", + Rack::Utils.parse_nested_query(query).with_indifferent_access) vc_test_request.set_header(Rack::QUERY_STRING, query) yield ensure diff --git a/lib/view_component/version.rb b/lib/view_component/version.rb index cc0c9e583..792015941 100644 --- a/lib/view_component/version.rb +++ b/lib/view_component/version.rb @@ -3,7 +3,7 @@ module ViewComponent module VERSION MAJOR = 3 - MINOR = 5 + MINOR = 6 PATCH = 0 PRE = nil diff --git a/test/sandbox/app/components/references_method_on_helpers_component.html.erb b/test/sandbox/app/components/references_method_on_helpers_component.html.erb new file mode 100644 index 000000000..9899d3040 --- /dev/null +++ b/test/sandbox/app/components/references_method_on_helpers_component.html.erb @@ -0,0 +1 @@ +<%= current_user %> diff --git a/test/sandbox/app/components/references_method_on_helpers_component.rb b/test/sandbox/app/components/references_method_on_helpers_component.rb new file mode 100644 index 000000000..4be21aa47 --- /dev/null +++ b/test/sandbox/app/components/references_method_on_helpers_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class ReferencesMethodOnHelpersComponent < ViewComponent::Base +end diff --git a/test/sandbox/test/base_test.rb b/test/sandbox/test/base_test.rb index 4b746be73..f117dc270 100644 --- a/test/sandbox/test/base_test.rb +++ b/test/sandbox/test/base_test.rb @@ -106,4 +106,35 @@ def initialize(cta: nil, title:) eval(source) # rubocop:disable Security/Eval end + + def test_no_method_error_does_not_reference_helper_if_view_context_not_present + exception = assert_raises(NoMethodError) { Class.new(ViewComponent::Base).new.current_user } + exception_message_regex = Regexp.new <<~MESSAGE.chomp, Regexp::MULTILINE + undefined method `current_user' for .* + + You may be trying to call a method provided as a view helper. Did you mean `helpers.current_user'? + MESSAGE + assert !exception_message_regex.match?(exception.message) + end + + def test_no_method_error_references_helper_if_view_context_present + view_context = ActionController::Base.new.view_context + view_context.instance_eval { + def current_user + "a user" + end + } + exception = assert_raises(NameError) { ReferencesMethodOnHelpersComponent.new.render_in(view_context) } + exception_advice = "You may be trying to call a method provided as a view helper. Did you mean `helpers.current_user'?" + assert exception.message.include?(exception_advice) + end + + def test_no_method_error_does_not_reference_missing_helper + view_context = ActionController::Base.new.view_context + exception = assert_raises(NameError) { ReferencesMethodOnHelpersComponent.new.render_in(view_context) } + exception_message_regex = Regexp.new <<~MESSAGE.chomp + You may be trying to call a method provided as a view helper\\. Did you mean `helpers.current_user'\\?$ + MESSAGE + assert !exception_message_regex.match?(exception.message) + end end diff --git a/test/sandbox/test/config_test.rb b/test/sandbox/test/config_test.rb index 9c741ed12..b74d02fd9 100644 --- a/test/sandbox/test/config_test.rb +++ b/test/sandbox/test/config_test.rb @@ -34,7 +34,7 @@ def test_all_methods_are_documented end options_defined_on_instance = Set[*default_options, *accessors] assert options_defined_on_instance.subset?(Set[*configuration_methods_to_document.map(&:name)]), - "Not all configuration options are documented: #{configuration_methods_to_document.map(&:name) - options_defined_on_instance.to_a}" + "Not all configuration options are documented: #{options_defined_on_instance.to_a - configuration_methods_to_document.map(&:name)}" assert configuration_methods_to_document.map(&:docstring).all?(&:present?), "Configuration options are missing docstrings." end diff --git a/test/sandbox/test/generators/preview_generator_test.rb b/test/sandbox/test/generators/preview_generator_test.rb index 361c01ac0..436e6e86c 100644 --- a/test/sandbox/test/generators/preview_generator_test.rb +++ b/test/sandbox/test/generators/preview_generator_test.rb @@ -3,6 +3,9 @@ require "test_helper" require "rails/generators/preview/component_generator" +# See: https://github.com/rails/rails/pull/47752#issuecomment-1720256371 +require "active_record" + Rails.application.load_generators class PreviewGeneratorTest < Rails::Generators::TestCase diff --git a/test/sandbox/test/rendering_test.rb b/test/sandbox/test/rendering_test.rb index f9bac0557..2f5069ea4 100644 --- a/test/sandbox/test/rendering_test.rb +++ b/test/sandbox/test/rendering_test.rb @@ -855,6 +855,7 @@ def test_with_request_url_with_query_parameters assert_equal "/products", vc_test_request.path assert_equal "mykey=myvalue&otherkey=othervalue", vc_test_request.query_string assert_equal "/products?mykey=myvalue&otherkey=othervalue", vc_test_request.fullpath + assert_instance_of ActiveSupport::HashWithIndifferentAccess, vc_test_request.query_parameters end with_request_url "/products?mykey[mynestedkey]=myvalue" do diff --git a/test/sandbox/test/translatable_test.rb b/test/sandbox/test/translatable_test.rb index fd2f49b6b..fea351318 100644 --- a/test/sandbox/test/translatable_test.rb +++ b/test/sandbox/test/translatable_test.rb @@ -79,10 +79,11 @@ def test_translate_uses_the_helper_when_no_sidecar_file_is_provided # The cache needs to be kept clean for TranslatableComponent, otherwise it will rely on the # already created i18n_backend. ViewComponent::CompileCache.invalidate_class!(TranslatableComponent) + original_method = TranslatableComponent.method(:sidecar_files) ViewComponent::Base.stub( :sidecar_files, - ->(exts) { exts.include?("yml") ? [] : TranslatableComponent.__minitest_stub___sidecar_files(exts) } + ->(exts) { exts.include?("yml") ? [] : original_method.call(exts) } ) do assert_equal "MISSING", translate(".hello", default: "MISSING") assert_equal "Hello from Rails translations!", translate("hello") diff --git a/test/sandbox/test/view_component_system_test.rb b/test/sandbox/test/view_component_system_test.rb index 086f54b12..067c345fe 100644 --- a/test/sandbox/test/view_component_system_test.rb +++ b/test/sandbox/test/view_component_system_test.rb @@ -3,7 +3,7 @@ require "test_helper" class ViewComponentSystemTest < ViewComponent::SystemTestCase - driven_by :cuprite + driven_by :vc_cuprite def test_simple_js_interaction_in_browser_without_layout with_rendered_component_path(render_inline(SimpleJavascriptInteractionWithJsIncludedComponent.new)) do |path| diff --git a/test/test_helper.rb b/test/test_helper.rb index 6528bdb12..aa7a61584 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -40,7 +40,9 @@ def self.warn(message) require "capybara/cuprite" -Capybara.register_driver(:cuprite) do |app| +# Rails registers its own driver named "cuprite" which will overwrite the one we +# register here. Avoid the problem by registering the driver with a distinct name. +Capybara.register_driver(:vc_cuprite) do |app| # Add the process_timeout option to prevent failures due to the browser # taking too long to start up. Capybara::Cuprite::Driver.new(app, {process_timeout: 60, timeout: 30})