Solidus custom homepage and testing support

Solidus custom homepage and testing support
Do not index
Do not index
URL

Homepage

Where we ended last time we still show our default Rails application index page like this:
notion image
Let’s change that with our own homepage where we will be showing our products in a little bit.
Generating a HomeController with only the index action:
bin/rails g controller home index
and set this new action as our root url by changing the config/routes.rb file like so:
Rails.application.routes.draw do
  # This line mounts Solidus's routes at the root of your application.
  # This means, any requests to URLs such as /products, will go to Spree::ProductsController.
  # If you would like to change where this engine is mounted, simply change the :at option to something different.
  #
  # We ask that you don't use the :as option here, as Solidus relies on it being the default of "spree"
  mount Spree::Core::Engine, at: '/'
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root "home#index"
end
When we restart our server and open up http://localhost:3000/ again we get something like this:
notion image

Solidus testing support - factory_bot

The testing solidus guide shows you how to use the testing_support factories that are available in solidus_core:
We will add factory_bot_rails to our Gemfile and update application.rb to load all the factories if factory_bot is available. We need to add factory_bot_rails to our test and/or development group. I picked them both so I can generate some data from the factories if needed for seed data.
group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "debug", platforms: %i[ mri mingw x64_mingw ]

  gem "factory_bot_rails", "~> 6.2"
end
The snippet below comes directly from the testing guide linked above, adding that to our config/application.rb like so
require_relative "boot"

require "rails"
...
...
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Blogstore
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 7.0

    # Configuration for the application, engines, and railties goes here.
    #
    # These settings can be overridden in specific environments using the files
    # in config/environments, which are processed later.
    #
    # config.time_zone = "Central Time (US & Canada)"
    # config.eager_load_paths << Rails.root.join("extras")

    # Add support for Solidus testsupport factories
    if defined?(FactoryBotRails)
      initializer after: "factory_bot.set_factory_paths" do
        require "spree/testing_support/factory_bot"

        # The paths for Solidus' core factories.
        solidus_paths = Spree::TestingSupport::FactoryBot.definition_file_paths

        # Optional: Any factories you want to require from extensions.
        extension_paths = [
          # MySolidusExtension::Engine.root.join("lib/my_solidus_extension/testing_support/factories"),
          # or individually:
          # MySolidusExtension::Engine.root.join("lib/my_solidus_extension/testing_support/factories/resource.rb"),
        ]

        # Your application's own factories.
        app_paths = [
          Rails.root.join("test/factories")
        ]

        FactoryBot.definition_file_paths = solidus_paths + extension_paths + app_paths
      end
    end
  end
end

Homepage default store name

Let’s update our generated index page to show the store name. You can have multiple stores in one Solidus installation, but for this series we will start with only the default store available to us. We can use Spree::Store.default to get the current default store instance.
Showing the default store name on our homepage like so:
<h1><%= Spree::Store.default.name %></h1>
<p>Find me in app/views/home/index.html.erb</p>
And here we go, we now render our store name: “Sample Store”.
notion image
To test this we can make a system spec, this also verifies that we can use the factories from Solidus as well. I started with the system spec for the homepage already (and fixed the generated tests for using the root_url instead of the generated route).
test/system/homepage_test.rb
 
require "application_system_test_case"

class HomepageTest < ApplicationSystemTestCase
  setup do
    @store = create(:store, name: "BlogStore")
  end

  test "homepage" do
    visit root_url
    assert_selector "h1", text: "BlogStore"
  end
end
when we run the above spec we are all green!
#> bin/rails test test/system/homepage_test.rb 
Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 58329

# Running:

Capybara starting Puma...
* Version 5.6.7 , codename: Birdie's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:58294
.

Finished in 9.190615s, 0.1088 runs/s, 0.1088 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

Adding some style

Now that we have a working test that actually grabs our Solidus store name we should add some style with tailwind.
app/views/home/index.html.erb
<section class="grid min-h-screen place-items-center bg-sky-100">
  <div class="p-6">
    <h1 class="text-center text-4xl font-extrabold leading-tight text-gray-800"><%= Spree::Store.default.name%></h1>
    <p class="mt-6 text-center text-lg font-light text-gray-700">Whether you prefer fiction or non-fiction, we have something for everyone. Start browsing now and find your next adventure!</p>
  </div>
</section>
notion image
Looks much better already! We do have a problem though. When we are running the tests we are getting a very cryptic error like this:
rails test test/system/homepage_test.rb:8

E

Error:
HomeControllerTest#test_should_get_index:
ActionView::Template::Error: Error: Function rgb is missing argument $green.
        on line 1 of stdin
>> }.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity
   ------------------------------------------^

    app/views/layouts/application.html.erb:9
    test/controllers/home_controller_test.rb:5:in `block in <class:HomeControllerTest>'


rails test test/controllers/home_controller_test.rb:4
We are using esbuild with jsbundling and cssbundling , this is conflicting with sass that is added to our system by Solidus. Luckily we can fix that by setting css_compressor to nil in our application.rb
# Disable the build in css_compressor.
config.assets.css_compressor = nil
When we run the specs again, they are all passing!

Showing products

Solidus comes with a sample engine installed by default. You can generate sample data by running the following command:
#> bundle exec rake spree_sample:load
      sample  Payment Methods
      sample  Tax Categories
      sample  Tax Rates
      sample  Shipping Categories
      sample  Shipping Methods
      sample  Products
      sample  Taxons
      sample  Taxonomies
      sample  Option Values
      sample  Option Types
      sample  Product Option Types
      sample  Product Properties
      sample  Variants
      sample  Stock
      sample  Assets
      sample  images for Solidus Snapback Cap
      sample  images for Solidus Hoodie Zip
      sample  images for Ruby Hoodie
      sample  images for Ruby Hoodie Zip
      sample  images for Ruby Polo
      sample  images for Solidus Mug
      sample  images for Ruby Mug
      sample  images for Solidus Tote
      sample  images for Ruby Tote
      sample  Orders
      sample  Addresses
      sample  Stores
      sample  Payments
      sample  Reimbursements
When you enter the admin panel, a variety of products are automatically generated for you to test with.
notion image
We will initially use these to design the homepage and gradually add more logic and tests step-by-step.
To have a starting base, let's show some products on the homepage. Since we are using ActiveStorage, we need to ensure that ActiveStorage::SetCurrent is included so that the disk-based storage gets the correct data. Let's add this line to our ApplicationController.
class ApplicationController < ActionController::Base
  include ActiveStorage::SetCurrent
end
Solidus also offers a base controller class that includes this and more. We will explore that base controller later in more detail.
For now we will just take the first 4 products and render them in a grid on the homepage:
<section class="grid min-h-screen place-items-center bg-sky-100">
....
....
  <div class="grid grid-cols-1 gap-8 p-8 md:grid-cols-2">
    <% Spree::Product.take(4).each do |product| %>
      <div class="m-12 flex items-center gap-6 rounded-md bg-gray-100 p-6">
        <div class="basis-1/3">
          <%= image_tag product.gallery.images.first.url(:product), class: "rounded-md" %>
        </div>
        <div class="basis-2/3">
          <h2 class="text-xl font-bold"><%= product.name %></h2>
          <p class="mt-1 text-sm leading-relaxed"><%= product.description %></p>
        </div>
      </div>
    <% end %>
  </div>

</section>
Refreshing the homepage will show you something like this:
notion image
The next steps will be to display the product details and then add the product to our cart.
The source code for this part can be found here:
 

Written by