Module: Glib::MailerTester
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/glib/mailer_tester.rb
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
-
.compute_missing_previews(mailer_actions:, previewed_actions:, except: []) ⇒ Object
Pure (no Rails reflection) so it is unit-testable.
-
.load_mailer_files ⇒ Object
Loads only ‘app/mailers` (so every mailer subclass is defined) rather than eager-loading the whole app — faster, and avoids unrelated load failures.
-
.load_preview_files ⇒ Object
Loads every preview file (idempotent — ‘require` no-ops if already loaded).
-
.missing_previews(except: []) ⇒ Object
Sorted Array<String> of “Mailer#action” entries that have no preview.
- .missing_previews_message(missing) ⇒ Object
-
.reflect_mailer_actions ⇒ Object
Reflection glue (needs a loaded Rails app).
- .reflect_preview_methods(mailer_name) ⇒ Object
Instance Method Summary collapse
-
#assert_mail_unchanged(mail) ⇒ Object
TODO: Remove test result files that are not relevant anymore.
- #assert_mails_unchanged ⇒ Object
- #last_mail ⇒ Object
-
#log_dir ⇒ Object
Overridable.
- #log_root_dir ⇒ Object
- #mails ⇒ Object
- #reset_mails ⇒ Object
Class Method Details
.compute_missing_previews(mailer_actions:, previewed_actions:, except: []) ⇒ Object
Pure (no Rails reflection) so it is unit-testable.
mailer_actions: { "FooMailer" => ["welcome", "goodbye"] }
previewed_actions: ->(mailer_name) { ["welcome"] }
except: ["FooMailer#goodbye"]
80 81 82 83 84 85 86 87 88 |
# File 'lib/glib/mailer_tester.rb', line 80 def self.compute_missing_previews(mailer_actions:, previewed_actions:, except: []) excluded = except.map(&:to_s).to_set mailer_actions.flat_map do |mailer, actions| previewed = Array(previewed_actions.call(mailer)).map(&:to_s).to_set actions.map(&:to_s) .reject { |action| previewed.include?(action) } .map { |action| "#{mailer}##{action}" } end.reject { |id| excluded.include?(id) }.sort end |
.load_mailer_files ⇒ Object
Loads only ‘app/mailers` (so every mailer subclass is defined) rather than eager-loading the whole app — faster, and avoids unrelated load failures.
118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/glib/mailer_tester.rb', line 118 def self.load_mailer_files dir = Rails.root.join('app/mailers') return unless dir.exist? loader = Rails.respond_to?(:autoloaders) ? Rails.autoloaders.main : nil if loader.respond_to?(:eager_load_dir) loader.eager_load_dir(dir.to_s) else Rails.application.eager_load! end end |
.load_preview_files ⇒ Object
Loads every preview file (idempotent — ‘require` no-ops if already loaded).
62 63 64 |
# File 'lib/glib/mailer_tester.rb', line 62 def self.load_preview_files Dir.glob(Rails.root + 'test/mailers/previews/*').each { |file| require file } end |
.missing_previews(except: []) ⇒ Object
Sorted Array<String> of “Mailer#action” entries that have no preview.
67 68 69 70 71 72 73 74 |
# File 'lib/glib/mailer_tester.rb', line 67 def self.missing_previews(except: []) load_preview_files compute_missing_previews( mailer_actions: reflect_mailer_actions, previewed_actions: ->(mailer) { reflect_preview_methods(mailer) }, except: except ) end |
.missing_previews_message(missing) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/glib/mailer_tester.rb', line 90 def self.(missing) <<~MSG Every mailer action must have a preview so `generate_preview_tests` snapshot-tests it. These actions have none: #{missing.join("\n ")} Add a method named after the action to the matching `<Mailer>Preview` class under test/mailers/previews/, returning `<Mailer>.<action>(...)`. If an action genuinely cannot be previewed, allowlist it with a reason: assert_all_mailer_actions_have_previews(except: ['FooMailer#some_action']) # why MSG end |
.reflect_mailer_actions ⇒ Object
Reflection glue (needs a loaded Rails app). Scopes to app mailers under ‘ApplicationMailer` so third-party mailers (Devise, etc.) are not flagged.
107 108 109 110 111 112 113 114 |
# File 'lib/glib/mailer_tester.rb', line 107 def self.reflect_mailer_actions load_mailer_files base = '::ApplicationMailer'.safe_constantize || ActionMailer::Base base.descendants.each_with_object({}) do |mailer, acc| actions = mailer.action_methods.to_a acc[mailer.name] = actions unless actions.empty? end end |
.reflect_preview_methods(mailer_name) ⇒ Object
130 131 132 133 |
# File 'lib/glib/mailer_tester.rb', line 130 def self.reflect_preview_methods(mailer_name) preview = "#{mailer_name}Preview".safe_constantize preview ? preview.instance_methods(false).map(&:to_s) : [] end |
Instance Method Details
#assert_mail_unchanged(mail) ⇒ Object
TODO: Remove test result files that are not relevant anymore
165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/glib/mailer_tester.rb', line 165 def assert_mail_unchanged(mail) # `method_name` refers to the name of the current test method. dir = File.join(log_dir, method_name) unless File.directory?(dir) FileUtils.mkdir_p(dir) end mail.to.each do |recipient| _assert_mail_body_unchanged("#{method_name}/#{recipient}", mail) end end |
#assert_mails_unchanged ⇒ Object
158 159 160 161 162 |
# File 'lib/glib/mailer_tester.rb', line 158 def assert_mails_unchanged mails.each do |mail| assert_mail_unchanged(mail) end end |
#last_mail ⇒ Object
152 153 154 155 156 |
# File 'lib/glib/mailer_tester.rb', line 152 def last_mail raise 'Last mail does not exist' unless (mail = mails.last) mail end |
#log_dir ⇒ Object
Overridable
140 141 142 |
# File 'lib/glib/mailer_tester.rb', line 140 def log_dir File.(File.join(log_root_dir, "results/#{class_name.underscore}/")) end |
#log_root_dir ⇒ Object
135 136 137 |
# File 'lib/glib/mailer_tester.rb', line 135 def log_root_dir raise 'Implementation needed' end |
#mails ⇒ Object
148 149 150 |
# File 'lib/glib/mailer_tester.rb', line 148 def mails ActionMailer::Base.deliveries end |
#reset_mails ⇒ Object
144 145 146 |
# File 'lib/glib/mailer_tester.rb', line 144 def reset_mails ActionMailer::Base.deliveries = [] end |