josefarias / hotwire_combobox

An accessible autocomplete for Ruby on Rails.
https://hotwirecombobox.com
MIT License
456 stars 30 forks source link

RSpec failing with "Missing partial hotwire_combobox/_combobox" #89

Closed aroth closed 5 months ago

aroth commented 6 months ago

Great library, thank you!

I've wrapped hotwire_combobox in a ViewComponent. The template is straightforward:

<%= form.combobox attribute, collection %>

When testing the ViewCompnent, the hotwire_combobox/_combobox partial from the gem cannot be found.

rspec spec/components/forms/hw_combobox_component_spec.rb
  1) Forms::HwComboboxComponent renders component
     Failure/Error: <%= form.combobox attribute, collection %>

     ActionView::MissingTemplate:
       Missing partial hotwire_combobox/_combobox with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :webp, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip, :turbo_stream], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder, :arb]}.

       Searched in:
     # ./app/components/forms/hw_combobox_component.html.erb:1:in `call'
     # ./spec/components/forms/hw_combobox_component_spec.rb:14:in `block (2 levels) in <top (required)>'

Please advise. Might there be something you need to do in the library such that the partial can be found?

         Searched in:
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/path_set.rb:42:in `find'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/lookup_context.rb:131:in `find'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/partial_renderer.rb:264:in `find_template'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/partial_renderer.rb:231:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/renderer.rb:83:in `render_partial_to_object'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/renderer.rb:55:in `render_partial'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/helpers/rendering_helper.rb:44:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/hotwire_combobox-0.1.40/lib/hotwire_combobox/helper.rb:23:in `hw_combobox_tag'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/hotwire_combobox-0.1.40/lib/hotwire_combobox/engine.rb:13:in `combobox'
     # ./app/components/forms/hw_combobox_component.html.erb:1:in `call'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/view_component-3.11.0/lib/view_component/compiler.rb:126:in `render_template_for'
josefarias commented 6 months ago

Hey @aroth, there's no _combobox partial as of the latest version. It got renamed to _component at some point. Is it possible you coupled your wrapper to _combobox and then updated the gem?

aroth commented 6 months ago

@josefarias I'm in 0.1.40 and am seeing:

app/views/hotwire_combobox/_combobox.html.erb
% cd /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/hotwire_combobox-0.1.40/app/views/hotwire_combobox/         

% ls -l
total 32
-rw-r--r--  1 aroth  staff  368 Mar 11 11:41 _combobox.html.erb
-rw-r--r--  1 aroth  staff  291 Mar 11 11:41 _next_page.turbo_stream.erb
-rw-r--r--  1 aroth  staff  207 Mar 11 11:41 _paginated_options.turbo_stream.erb
-rw-r--r--  1 aroth  staff  306 Mar 11 11:41 _pagination.html.erb
drwxr-xr-x  6 aroth  staff  192 Mar 11 11:41 combobox
josefarias commented 6 months ago

Ah sorry @aroth, I was thinking about the changes in https://github.com/josefarias/hotwire_combobox/pull/80/files#diff-36941d6ca27cc45f677b8d89ca6a8f2ff93e438f0bfe4cdead4f5c9fa93ffe00 which I thought had been released, but they've only been merged to main and not released. Anyway, that change should be coming soon!

I think this might be due to the engine being isolated, which is modifiable.

Can you share your ViewComponent implementation please? So I have something to test against

aroth commented 6 months ago

@josefarias Sure thing. Reduced to bare minimum, I hope this helps:

Class:

  class HwComboboxComponent < ApplicationComponent
    def initialize(attribute:, collection:, form:)
      @attribute = attribute
      @form = form
      @collection = collection
    end
    attr_reader :attribute, :form, :collection
  end

Template:

<%= form.combobox attribute, collection %>

Usage:

<%= form_for Unit.new, url: "/" do |form| %>
  <%= render HwComboboxComponent.new(form:, collection: Unit.all, attribute: User) %>
<% end %>

Spec:

# frozen_string_literal: true

require "rails_helper"

RSpec.describe HwComboboxComponent, type: :component do
  let(:object) { OpenStruct.new(source: [], errors: {}) }
  let(:form) { form_builder_for("model_name", object) }
  let(:collection) { [{id: 1, display: "First"}, {id: 2, display: "Second"}] }
  let(:component) { described_class.new(attribute: :source, form:, collection:) }

  it "renders component" do
    render_inline component

    expect(rendered_content).to have_css "input.hw-combobox__input"
  end
end

Failure:

Failures:

  1) HwComboboxComponent renders component
     Failure/Error: <%= form.combobox attribute, collection %>

     ActionView::MissingTemplate:
       Missing partial hotwire_combobox/_combobox with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :webp, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip, :turbo_stream], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder, :arb]}.
josefarias commented 6 months ago

@aroth, could I get you to point your gem to this branch and see if it's fixed for you, please? https://github.com/josefarias/hotwire_combobox/pull/99

@siddhartharun @pranavbabu @adrianthedev if any of you have a spare minute, could I get you to also point your gem to that branch and check whether things keep working as expected?

I'm worried about something similar to https://github.com/josefarias/hotwire_combobox/issues/16 coming back if we merge that branch – and you three had previously experienced that issue whereas I hadn't. No worries if you're busy, I don't want to unnecessarily take up your time.

Thank you all so much!

pranavbabu commented 6 months ago

As I remember sometimes when I used ViewComponents there was a problem with rendering partials without specified formats, and usually putting explicit format works something like this <%= render partial: hotwire_combobox/combobox formats: [:html] %>

aroth commented 6 months ago

@josefarias

Still seeing a failure. Stacktrace below:

Failures:

  1) Forms::HwComboboxComponent renders component
     Failure/Error: <%= form.combobox attribute, collection %>

     ActionView::MissingTemplate:
       Missing partial hotwire_combobox/_component with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :webp, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip, :turbo_stream], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder, :arb]}.

       Searched in:
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/path_set.rb:42:in `find'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/lookup_context.rb:131:in `find'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/partial_renderer.rb:264:in `find_template'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/partial_renderer.rb:231:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/renderer.rb:83:in `render_partial_to_object'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/renderer.rb:29:in `render_to_object'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/renderer/renderer.rb:24:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/helpers/rendering_helper.rb:37:in `block in render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/base.rb:291:in `in_rendering_context'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/helpers/rendering_helper.rb:33:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/hotwire_combobox-7995c15a24bf/app/presenters/hotwire_combobox/component.rb:36:in `render_in'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionview-7.1.3.2/lib/action_view/helpers/rendering_helper.rb:42:in `render'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/hotwire_combobox-7995c15a24bf/lib/hotwire_combobox/helper.rb:21:in `hw_combobox_tag'
     # /Users/aroth/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/hotwire_combobox-7995c15a24bf/lib/hotwire_combobox/engine.rb:11:in `combobox'
     # ./app/components/forms/hw_combobox_component.html.erb:1:in `call'
josefarias commented 6 months ago

@aroth sorry you're still having problems! I spent some more time on this today but wasn't able to replicate.

If at all possible, would you mind sharing an example app that exhibits this behavior? Ideally a brand new rails app as a minimal reproduction. Appreciate your time!

aroth commented 6 months ago

@josefarias Here you go:

https://github.com/aroth/hw_combo_vc_rspec

On closer investigation, the issue has something to do with the output of FormBuilderHelper#form_builder_for which we use in our testing setup... and hasn't been an issue up until now. Perhaps you'll notice something that is not obvious to us.

Running rspec should produce the same issue above.

% rspec
F

Failures:

  1) SearchComponent works
     Failure/Error: <%= form.combobox "cats", ["Cornish Rex", "Ragdoll", "Scottish Fold"] %>

     ActionView::MissingTemplate:
       Missing partial hotwire_combobox/_component with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :vcf, :vtt, :png, :jpeg, :gif, :bmp, :tiff, :svg, :webp, :mpeg, :mp3, :ogg, :m4a, :webm, :mp4, :otf, :ttf, :woff, :woff2, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip, :gzip, :turbo_stream], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}.

       Searched in:
     # ./app/components/search_component.html.erb:3:in `call'
     # ./spec/components/search_component_spec.rb:10:in `block (2 levels) in <top (required)>'

Finished in 0.01869 seconds (files took 0.73511 seconds to load)
1 example, 1 failure

Failed examples:

The useful commit: https://github.com/aroth/hw_combo_vc_rspec/commit/0ed8dab1c229b5bfeca3dba0314cd373de866e79

aroth commented 5 months ago

Hi @josefarias -- following up to see if you've had a chance to take a look.

josefarias commented 5 months ago

Sorry @aroth, not yet! I prioritized shipping multiselect and grouped options over this. But keeping this open and visible.

josefarias commented 5 months ago

Hey @aroth meant to take a look at this one today but the repo above is 404ing on me. Any chance it's been deleted or private?

aroth commented 5 months ago

@josefarias Now public!

josefarias commented 5 months ago

Hey @aroth, thanks for sharing that minimal reproduction. Super helpful.

I do believe this is an issue with how your #form_builder_for helper is set up.

I can see you're initializing a stubbed LookupContext with an empty array as the view paths. This probably works most of the time because Rails-native form helpers don't do any view lookups (that I'm aware). But the form.combobox helper added by this gem uses a view template for rendering its more complex markup — so you need to add the engine's view paths to the lookup context if you want to stub the form builder in this way.

These changes make the test in your app pass for me locally:

diff --git a/spec/components/search_component_spec.rb b/spec/components/search_component_spec.rb
index 24be4f7..661a811 100644
--- a/spec/components/search_component_spec.rb
+++ b/spec/components/search_component_spec.rb
@@ -10,6 +10,6 @@ RSpec.describe SearchComponent, type: :component do
     render_inline described_class.new(form:)

     expect(rendered_content).to include("Search Component")
-    expect(rendered_content).to have_css "input.hw-combobox__input"
+    expect(page).to have_css "input.hw-combobox__input"
   end
 end
\ No newline at end of file
diff --git a/spec/support/form_builder.rb b/spec/support/form_builder.rb
index 0e51ce5..1dc5c4b 100644
--- a/spec/support/form_builder.rb
+++ b/spec/support/form_builder.rb
@@ -4,10 +4,11 @@ module FormBuilderHelper
   # Creates a form builder for the given model name and object which can be used
   # to render form components.
   def form_builder_for(model_name, object, builder: ActionView::Helpers::FormBuilder)
-    lookup_context = ActionView::LookupContext.new([])
+    tested_view_paths = [ HotwireCombobox::Engine.root.join("app", "views") ]
+    lookup_context = ActionView::LookupContext.new(tested_view_paths)
     assigns = {}
     controller = ActionController::Base.new
-    view_context = ActionView::Base.new(lookup_context, assigns, controller)
+    view_context = ActionView::Base.with_empty_template_cache.new(lookup_context, assigns, controller)

     builder.new(model_name, object, view_context, {})
   end

Please try that and let me know if it's still not working. Closing for now. Thanks!