From 8a59f7fae8c390e8da0bd58149ef7b86ec8cc39a Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Tue, 31 Oct 2023 07:21:49 -0400 Subject: [PATCH] Introduce `suspenders:accessibility` generator Ported over from #1105 Installs [capybara_accessibility_audit] and [capybara_accessible_selectors]. `./bin/rails g suspenders:accessibility` Introduces `Suspenders::Generators::APIAppUnsupported` module for generators that cannot be run in an [API only][] application. This uses a [concern][] to ensure we raise an error before the generator including the module invokes any of its methods. [capybara_accessibility_audit]: https://github.com/thoughtbot/capybara_accessibility_audit [capybara_accessible_selectors]: https://github.com/citizensadvice/capybara_accessible_selectors [API only]: https://guides.rubyonrails.org/api_app.html [concern]: https://api.rubyonrails.org/classes/ActiveSupport/Concern.html --- NEWS.md | 2 + README.md | 9 +- .../suspenders/accessibility_generator.rb | 17 ++++ lib/suspenders.rb | 1 + lib/suspenders/generators.rb | 30 +++++++ .../accessibility_generator_test.rb | 89 +++++++++++++++++++ test/suspenders/generators_test.rb | 11 +++ test/test_helper.rb | 37 ++++++++ 8 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 lib/generators/suspenders/accessibility_generator.rb create mode 100644 lib/suspenders/generators.rb create mode 100644 test/generators/suspenders/accessibility_generator_test.rb create mode 100644 test/suspenders/generators_test.rb diff --git a/NEWS.md b/NEWS.md index 2dfddb1a0..44770f5bd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ Unreleased * Remove `suspenders` system executable +* Introduce `suspenders:accessibility` generator +* Introduce `Suspenders::Generators::APIAppUnsupported` module and concern 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index fa7abc3cc..27d95fa2d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,14 @@ end ## Generators -TODO +### Accessibility + +Installs [capybara_accessibility_audit] and [capybara_accessible_selectors] + +`./bin/rails g suspenders:accessibility` + + [capybara_accessibility_audit]: https://github.com/thoughtbot/capybara_accessibility_audit + [capybara_accessible_selectors]: https://github.com/citizensadvice/capybara_accessible_selectors ## Contributing diff --git a/lib/generators/suspenders/accessibility_generator.rb b/lib/generators/suspenders/accessibility_generator.rb new file mode 100644 index 000000000..85b91a553 --- /dev/null +++ b/lib/generators/suspenders/accessibility_generator.rb @@ -0,0 +1,17 @@ +module Suspenders + module Generators + class AccessibilityGenerator < Rails::Generators::Base + include Suspenders::Generators::APIAppUnsupported + + desc "Installs capybara_accessibility_audit and capybara_accessible_selectors" + + def add_capybara_gems + gem_group :test do + gem "capybara_accessibility_audit" + gem "capybara_accessible_selectors", github: "citizensadvice/capybara_accessible_selectors" + end + Bundler.with_unbundled_env { run "bundle install" } + end + end + end +end diff --git a/lib/suspenders.rb b/lib/suspenders.rb index 6f8c7b5d2..a9d6ed43f 100644 --- a/lib/suspenders.rb +++ b/lib/suspenders.rb @@ -1,5 +1,6 @@ require "suspenders/version" require "suspenders/railtie" +require "suspenders/generators" module Suspenders # Your code goes here... diff --git a/lib/suspenders/generators.rb b/lib/suspenders/generators.rb new file mode 100644 index 000000000..892433679 --- /dev/null +++ b/lib/suspenders/generators.rb @@ -0,0 +1,30 @@ +require "active_support/concern" + +module Suspenders + module Generators + module APIAppUnsupported + class Error < StandardError + def message + "This generator cannot be used on API only applications." + end + end + + extend ActiveSupport::Concern + + included do + def raise_if_api_only_app + if api_only_app? + raise Suspenders::Generators::APIAppUnsupported::Error + end + end + end + + private + + def api_only_app? + File.read(Rails.root.join("config/application.rb")) + .match?(/^\s*config\.api_only\s*=\s*true/i) + end + end + end +end diff --git a/test/generators/suspenders/accessibility_generator_test.rb b/test/generators/suspenders/accessibility_generator_test.rb new file mode 100644 index 000000000..cff9cc58b --- /dev/null +++ b/test/generators/suspenders/accessibility_generator_test.rb @@ -0,0 +1,89 @@ +require "test_helper" +require "generators/suspenders/accessibility_generator" + +module Suspenders + module Generators + class AccessibilityGeneratorTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::Generators::AccessibilityGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "generator runs without errors" do + assert_nothing_raised do + run_generator + end + end + + test "raises if API only application" do + within_api_only_app do + assert_raises Suspenders::Generators::APIAppUnsupported::Error do + run_generator + end + + assert_file app_root("Gemfile") do |file| + assert_no_match "capybara_accessibility_audit", file + assert_no_match "capybara_accessible_selectors", file + end + end + end + + test "does not raise if API configuration is commented out" do + within_api_only_app do + path = app_root("config/application.rb") + content = File.binread(path).gsub!("config.api_only = true", "# config.api_only = true") + File.binwrite(path, content) + + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match "capybara_accessibility_audit", file + assert_match "capybara_accessible_selectors", file + end + end + end + + test "adds gems to Gemfile" do + expected_output = <<~RUBY + group :test do + gem "capybara_accessibility_audit" + gem "capybara_accessible_selectors", github: "citizensadvice/capybara_accessible_selectors" + end + RUBY + + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match(expected_output, file) + end + end + + test "installs gems with Bundler" do + Bundler.stubs(:with_unbundled_env).yields + generator.expects(:run).with("bundle install").once + + capture(:stdout) do + generator.add_capybara_gems + end + end + + test "generator has a description" do + description = "Installs capybara_accessibility_audit and capybara_accessible_selectors" + + assert_equal description, Suspenders::Generators::AccessibilityGenerator.desc + end + + private + + def prepare_destination + touch "Gemfile" + end + + def restore_destination + remove_file_if_exists "Gemfile" + end + end + end +end diff --git a/test/suspenders/generators_test.rb b/test/suspenders/generators_test.rb new file mode 100644 index 000000000..3d7cccb62 --- /dev/null +++ b/test/suspenders/generators_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +class Suspenders::GeneratorsTest < ActiveSupport::TestCase + class APIAppUnsupportedTest < Suspenders::GeneratorsTest + test "message returns a custom message" do + expected = "This generator cannot be used on API only applications." + + assert_equal expected, Suspenders::Generators::APIAppUnsupported::Error.new.message + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2de18a5cf..060d7dc22 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -42,4 +42,41 @@ def touch(file) FileUtils.touch path end + + def within_api_only_app(&block) + backup_file "config/application.rb" + application_config = <<~RUBY + require_relative "boot" + require "rails/all" + + Bundler.require(*Rails.groups) + + module Dummy + class Application < Rails::Application + config.load_defaults 7.1 + + config.autoload_lib(ignore: %w(assets tasks)) + + config.api_only = true + end + end + RUBY + File.open(app_root("config/application.rb"), "w") { _1.write application_config } + + yield + ensure + restore_file "config/application.rb" + end + + private + + def backup_file(file) + FileUtils.mv app_root(file), app_root("#{file}.bak") + touch file + end + + def restore_file(file) + remove_file_if_exists(file) + FileUtils.mv app_root("#{file}.bak"), app_root(file) + end end