Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support devise using ViewComponent::TestCase #1849

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

fugufish
Copy link
Contributor

What are you trying to accomplish?

This updates ViewComponent::TestHelpers to better detect and support Devise. It resolves the issue described in this discussion #371.

What approach did you choose and why?

Detect whether or not Devise::Test::ControllerHelpers, and if so, set up the @request instance variable and include Devise::Test::ControllerHelpers. The @request variable MUST be present before Devise's own setup hook is called, the only way to ensure this is to create setup initializing the controller and setting the @request instance variable before inclusion.

Anything you want to highlight for special attention from reviewers?

I'd like not to have to add janky included callbacks, but this is the only way that seems to work with Devise.

docs/guide/testing.md Outdated Show resolved Hide resolved
Co-authored-by: Hans Lemuet <Spone@users.noreply.github.com>
Copy link
Contributor

@BlakeWilliams BlakeWilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making those changes, I think this is getting close but there's a few small things I think we want to address first.

docs/guide/testing.md Outdated Show resolved Hide resolved
@@ -244,5 +249,9 @@ def __vc_test_helpers_preview_class
rescue NameError
raise NameError, "`render_preview` expected to find #{result}, but it does not exist."
end

def __vc_render_preview_controller
@vc_render_preview_controller ||= __vc_test_helpers_build_controller(Rails.application.config.view_component.preview_controller.constantize)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly changing behavior since we're now memoizing this value. Is this okay to memoize or do we need to reset it each render? I think we want to keep the old behavior and have it re-evaluate per-call to render_preview so each call has a fresh controller instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a #teardown method that clears the variable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned it above, but not every test needs a preview controller. Can we revert this back to the previous, lazy loaded implementation to avoid extra work in test suites?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately to support devise I don't see this being possible. We can't predict when render_preview is going to be used. The entire problem is that to support devise, setup needs to happen before render_preview is ever called. If we don't memoize, then the test may still work in most cases, but keep in mind it wont be accurate, as you're warden request environ would be a different instance than your render_preview request environ.

One possible alternative that might meet your requirements is a new concern, which sets up a test environment for devise and render_preview. That way a user is actively opting in essentially stating "I am using this pattern", and then update documentation to the concern's existence.

Honestly if it weren't for the fact that this project already has a bunch of minitests built before I started working on it, I'd just move the whole thing over to rspec and be done with it :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like

module ViewComponent::TestHelpers::DevisePreviewHelper
   def self.included(base)
      base.instance_eval do
        setup do
          @request = __vc_render_preview_controller.request
        end
         
        teardown do
          @request = nil
          @vc_render_preview_controller = nil
        end
          
      # ....
   end
    
    def __vc_render_preview_controller
      @vc_render_preview_controller ||= super
    end
      
    
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To help clarify things, are devise helpers only broken for previews or for previews + render_inline?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checked, it works for render_inline. I believe it is because render_preview uses an actual controller instance, causing it to pick up Warden's hooks.

@@ -225,6 +225,11 @@ def vc_test_request
end
end

def before_setup
@request = __vc_render_preview_controller.request
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't correct since this is creating the request from the preview controller, not using the same request the request method uses. Can we update this to use the proper request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow. On line 78 we have previews_controller = __vc_render_preview_controller, #request tracks to ViewComponent::Base#request which memoizes the request.

If you debug the call you can see that the request objects are indeed the same.

@request -> #<ActionDispatch::TestRequest:0x000000010c675838>
preview_controller.request -> #<ActionDispatch::TestRequest:0x000000010c675838>

These are identical. Am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@request = __vc_render_preview_controller.request
@request = vc_test_request

That wasn't completely clear in hindsight, sorry about that.

We shouldn't use __vc_render_preview_controller.request here because we don't need to couple the @request to the preview controller or how it's constructed. Also not every test requires a preview controller to be present either, so we should skip instantiating one except where necessary.

Copy link
Contributor Author

@fugufish fugufish Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we do. The Warden context is not correctly set up in ActionDispatch::TestRequest.create. I tested this myself with one of your earlier suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't always use the preview controller instance to render since users can pass their own controller class to us and render_inline uses its own controller.

What does Devise need the controller instance for?

Copy link
Contributor Author

@fugufish fugufish Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@fugufish fugufish Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BlakeWilliams to be clear, this path is ONLY triggered if you call current_user in any of your components. Which is kindof edge case ish right now. I actually refactored so that current_user is only ever passed into components when they need it, so this PR actually wont even apply to my case anymore.

That said, I read somewhere that the long term goal of ViewComponent is that it would be used for page rendering as well, which will make this a lot less of an edge case.

@fugufish
Copy link
Contributor Author

how can I retrigger test suite, looks like a flakey test

@BlakeWilliams
Copy link
Contributor

Not relevant to this PR but we really should address that flaky test. I can re-run it real quick.

@fugufish
Copy link
Contributor Author

@BlakeWilliams are we considering this PR dead on arrival?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants