lookbook-hq / lookbook

A UI development environment for Ruby on Rails apps ✨
https://lookbook.build
MIT License
913 stars 94 forks source link

Test that all previews render correctly, e.g. in CI #376

Closed boardfish closed 1 year ago

boardfish commented 1 year ago

Is your feature request related to a problem? Please describe

I'm currently looking into a ViewComponent V3 upgrade. We've got the tests passing after upgrading to the new slot syntax, but another engineer identified that if we were to upgrade now, all our Lookbook previews would break because the slot syntax had not been updated in those.

Lookbook isn't covered by our CI. To have that coverage would help us to identify breaking changes going forward – not just those caused by version upgrades, but those caused by, for example, syntax errors or changes to a component's API for which the relevant preview wasn't changed.

Describe the solution you'd like

It would be nice to have, for example, a Rake task that runs a suite that renders every preview under its default conditions and checks that an error is not raised.

Describe alternatives you've considered

It's probably possible to load Lookbook within RSpec in some way, similarly to how we eager load all components in the Rails runtime. Using that, I suppose it might be possible to iterate through all preview routes and check that they return as we'd expect in a request spec.

This issue's mostly speculative – I haven't worked directly with Lookbook much just yet.

Spone commented 1 year ago

Is it specific to Lookbook, or is it just the "regular" component previews that are breaking?

Maybe you can use Previews as test cases in this case, so you'll be able to detect when the previews break?

boardfish commented 1 year ago

Looks like that would work if an equivalent task were to be made in view_component. Similar to your codemod, we could eager load all preview classes and check out their method surface to figure out which previews need to be rendered. Perhaps magic comments could be used to establish more test cases that require params... This sounds good.

If Lookbook's compatible with that idea of previews as test cases, then maybe it would benefit from this being built into view_component itself.

allmarkedup commented 1 year ago

Hey guys, sorry for the slow reply, just catching up with myself!

@boardfish Lookbook previews are just ViewComponent previews (optionally unobtrusively annotated with some YARD-style comments that don't affect ViewComponent compatibility).

So I think it would make sense to do any preview testing without any involvement of Lookbook itself - as @Spone mentions I think previews as test cases is the way to go here (as an aside... I love that feature!). Putting Lookbook into the mix here would just add overhead and if the previews render without Lookbook then they should work just fine when rendered within the Lookbook UI.

Does that make sense? I will close this issue now as I don't think this is a Lookbook-specific issue but if I've misunderstood what you are looking for feel free to reopen :-)

inkstak commented 1 year ago

Here is how we proceed to test the previews with ViewComponent:

Aside to each <component>_preview, we add a <component>_preview_spec:

> spec/components/preview
  > button
    component_preview.rb
    component_preview_spec.rb

If the Preview look like that:

class Button::ComponentPreview < ViewComponent::Preview
  def default; end
  def primary; end
end

the spec file will look like that:

RSpec.describe Button::ComponentPreview, type: :component_preview do
  it { is_expected.to render_preview_without_exception(:default) }
  it { is_expected.to render_preview_without_exception(:primary) }
end

render_preview_without_exception is a really simple matcher that rely on VIewComponent method render_preview:

module RenderPreviewWithoutException
  extend RSpec::Matchers::DSL

  matcher :render_preview_without_exception do |expected_method|
    match do |actual|
      render_preview(expected_method, from: actual.class)
      true
    end
  end
end

RSpec.configure do
  config.include RenderPreviewWithoutException, type: :component_preview
end

Test other previews

It works nice with VIewComponent, but now I try to find another agnostic way to test other previews such as Lookbook::Preview subclasses:

class Badge::ComponentPreview < Lookbook::Preview
  def default; end
end
matcher :render_preview_without_exception do |expected_method|
  match do |actual|
    if actual.class <= ViewComponent::Preview
      render_preview(expected_method, from: actual.class)
      true
    else
      pending "TODO: handle Lookbook::Preview subclasses"
      false
    end
  end
end