snapshot_inspector 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +227 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/snapshot_inspector/manifest.js +2 -0
  6. data/app/assets/javascripts/snapshot_inspector/application.js +1 -0
  7. data/app/assets/stylesheets/snapshot_inspector/application.css +33 -0
  8. data/app/assets/stylesheets/snapshot_inspector/snapshots/mail.css +48 -0
  9. data/app/assets/stylesheets/snapshot_inspector/snapshots/not_found.css +15 -0
  10. data/app/assets/stylesheets/snapshot_inspector/snapshots/response.css +9 -0
  11. data/app/assets/stylesheets/snapshot_inspector/snapshots.css +73 -0
  12. data/app/controllers/snapshot_inspector/application_controller.rb +14 -0
  13. data/app/controllers/snapshot_inspector/snapshots/mail_controller.rb +57 -0
  14. data/app/controllers/snapshot_inspector/snapshots/response_controller.rb +15 -0
  15. data/app/controllers/snapshot_inspector/snapshots_controller.rb +7 -0
  16. data/app/helpers/snapshot_inspector/application_helper.rb +15 -0
  17. data/app/helpers/snapshot_inspector/snapshots_helper.rb +37 -0
  18. data/app/mailers/snapshot_inspector/application_mailer.rb +6 -0
  19. data/app/models/snapshot_inspector/snapshot/context.rb +49 -0
  20. data/app/models/snapshot_inspector/snapshot/mail_type.rb +35 -0
  21. data/app/models/snapshot_inspector/snapshot/response_type.rb +19 -0
  22. data/app/models/snapshot_inspector/snapshot/rspec_context.rb +52 -0
  23. data/app/models/snapshot_inspector/snapshot/test_unit_context.rb +44 -0
  24. data/app/models/snapshot_inspector/snapshot/type.rb +52 -0
  25. data/app/models/snapshot_inspector/snapshot.rb +86 -0
  26. data/app/views/layouts/snapshot_inspector/application.html.erb +18 -0
  27. data/app/views/snapshot_inspector/snapshots/index.html.erb +29 -0
  28. data/app/views/snapshot_inspector/snapshots/mail/show.html.erb +107 -0
  29. data/app/views/snapshot_inspector/snapshots/not_found.html.erb +8 -0
  30. data/app/views/snapshot_inspector/snapshots/response/raw.html.erb +1 -0
  31. data/app/views/snapshot_inspector/snapshots/response/show.html.erb +1 -0
  32. data/config/importmap.rb +12 -0
  33. data/config/routes.rb +9 -0
  34. data/lib/minitest/snapshot_inspector_plugin.rb +28 -0
  35. data/lib/snapshot_inspector/engine.rb +67 -0
  36. data/lib/snapshot_inspector/storage.rb +60 -0
  37. data/lib/snapshot_inspector/test/action_mailer_headers.rb +18 -0
  38. data/lib/snapshot_inspector/test/rspec_helpers.rb +45 -0
  39. data/lib/snapshot_inspector/test/test_unit_helpers.rb +48 -0
  40. data/lib/snapshot_inspector/version.rb +3 -0
  41. data/lib/snapshot_inspector.rb +42 -0
  42. data/lib/tasks/tmp.rake +10 -0
  43. metadata +159 -0
@@ -0,0 +1,35 @@
1
+ require "mail"
2
+
3
+ module SnapshotInspector
4
+ class Snapshot
5
+ class MailType < Type
6
+ snapshotee ActionMailer::MessageDelivery
7
+
8
+ # @private
9
+ def extract(snapshotee)
10
+ @message = snapshotee.to_s
11
+ @bcc = snapshotee.bcc
12
+ end
13
+
14
+ # @private
15
+ def from_hash(hash)
16
+ @message = hash[:message]
17
+ @bcc = hash[:bcc]
18
+ end
19
+
20
+ def message
21
+ message = Mail::Message.new(@message)
22
+ message.bcc = @bcc
23
+ message
24
+ end
25
+
26
+ def mailer_name
27
+ message.header["X-SnapshotInspector-Mailer-Name"].value
28
+ end
29
+
30
+ def action_name
31
+ message.header["X-SnapshotInspector-Action-Name"].value
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ module SnapshotInspector
2
+ class Snapshot
3
+ class ResponseType < Type
4
+ snapshotee ActionDispatch::TestResponse
5
+
6
+ attr_reader :body
7
+
8
+ # @private
9
+ def extract(snapshotee)
10
+ @body = snapshotee.parsed_body
11
+ end
12
+
13
+ # @private
14
+ def from_hash(hash)
15
+ @body = hash[:body]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module SnapshotInspector
2
+ class Snapshot
3
+ class RspecContext < Context
4
+ test_framework :rspec
5
+
6
+ attr_reader :test_framework, :example, :take_snapshot_index
7
+
8
+ # @private
9
+ def extract(context)
10
+ @test_framework = context[:test_framework]
11
+ @example = context[:example]
12
+ @take_snapshot_index = context[:take_snapshot_index]
13
+ end
14
+
15
+ # @private
16
+ def from_hash(hash)
17
+ @test_framework = hash[:test_framework].to_sym
18
+ @example = hash[:example]
19
+ @take_snapshot_index = hash[:take_snapshot_index]
20
+ end
21
+
22
+ def to_slug
23
+ spec_path_without_extension = @example[:file_path].delete_suffix(File.extname(@example[:file_path])).delete_prefix("./")
24
+ [spec_path_without_extension, @example[:line_number], @take_snapshot_index].join("_")
25
+ end
26
+
27
+ def name
28
+ @example[:full_description].gsub(test_group, "").strip
29
+ end
30
+
31
+ def test_group
32
+ root_example_group_description(@example)
33
+ end
34
+
35
+ def order_index
36
+ [@example[:file_path], @example[:line_number], @take_snapshot_index]
37
+ end
38
+
39
+ private
40
+
41
+ def root_example_group_description(hash)
42
+ if hash[:example_group].present?
43
+ root_example_group_description(hash[:example_group])
44
+ elsif hash[:parent_example_group].present?
45
+ root_example_group_description(hash[:parent_example_group])
46
+ else
47
+ hash[:description]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ module SnapshotInspector
2
+ class Snapshot
3
+ class TestUnitContext < Context
4
+ test_framework :test_unit
5
+
6
+ attr_reader :test_framework, :test_case_name, :method_name, :source_location, :take_snapshot_index
7
+
8
+ # @private
9
+ def extract(context)
10
+ @test_framework = context[:test_framework]
11
+ @test_case_name = context[:test_case_name]
12
+ @method_name = context[:method_name]
13
+ @source_location = context[:source_location]
14
+ @take_snapshot_index = context[:take_snapshot_index]
15
+ end
16
+
17
+ # @private
18
+ def from_hash(hash)
19
+ @test_framework = hash[:test_framework].to_sym
20
+ @test_case_name = hash[:test_case_name]
21
+ @method_name = hash[:method_name]
22
+ @source_location = hash[:source_location]
23
+ @take_snapshot_index = hash[:take_snapshot_index]
24
+ end
25
+
26
+ def to_slug
27
+ spec_path_without_extension = source_location[0].delete_suffix(File.extname(source_location[0])).delete_prefix(Rails.root.to_s + "/")
28
+ [spec_path_without_extension, source_location[1], take_snapshot_index].join("_")
29
+ end
30
+
31
+ def name
32
+ method_name.gsub(/^test_/, "").humanize(capitalize: false)
33
+ end
34
+
35
+ def test_group
36
+ test_case_name
37
+ end
38
+
39
+ def order_index
40
+ source_location.dup << take_snapshot_index
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,52 @@
1
+ module SnapshotInspector
2
+ class Snapshot
3
+ class Type
4
+ class UnknownSnapshotee < StandardError; end
5
+
6
+ class_attribute :registry, default: {}, instance_writer: false, instance_predicate: false
7
+
8
+ def self.snapshotee(class_name)
9
+ registry[class_name] = self
10
+ end
11
+
12
+ def self.extract(snapshotee)
13
+ record = type_class(snapshotee.class).new
14
+ record.extract(snapshotee)
15
+ record
16
+ end
17
+
18
+ def self.from_hash(hash)
19
+ record = type_class(hash[:snapshotee_class].constantize).new
20
+ record.from_hash(hash)
21
+ record
22
+ end
23
+
24
+ private_class_method def self.type_class(snapshotee_class)
25
+ registry[snapshotee_class] || raise(UnknownSnapshotee.new(unknown_snapshotee_class_message(snapshotee_class)))
26
+ end
27
+
28
+ private_class_method def self.unknown_snapshotee_class_message(snapshotee_class)
29
+ list_of_known_classes = registry.keys.map(&:to_s).sort.map { |class_name| "`#{class_name}`" }.join(" or ")
30
+ "#take_snapshot only accepts an argument of kind #{list_of_known_classes}. You provided `#{snapshotee_class}`."
31
+ end
32
+
33
+ # @private
34
+ def extract(_snapshotee)
35
+ raise "Implement in a child class."
36
+ end
37
+
38
+ # @private
39
+ def from_hash(_hash)
40
+ raise "Implement in a child class."
41
+ end
42
+
43
+ def type
44
+ self.class.to_s.underscore.split("/").last.gsub("_type", "")
45
+ end
46
+
47
+ def as_json(data = {})
48
+ {snapshotee_class: registry.key(self.class)}.merge(super)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,86 @@
1
+ require "snapshot_inspector/snapshot/response_type"
2
+ require "snapshot_inspector/snapshot/mail_type"
3
+ require "snapshot_inspector/snapshot/test_unit_context"
4
+ require "snapshot_inspector/snapshot/rspec_context"
5
+
6
+ module SnapshotInspector
7
+ class Snapshot
8
+ class NotFound < StandardError; end
9
+
10
+ attr_reader :context, :slug, :created_at
11
+ delegate_missing_to :@type_data
12
+
13
+ def self.persist(snapshotee:, context:)
14
+ new.extract(snapshotee: snapshotee, context: context).persist
15
+ end
16
+
17
+ def self.find(slug)
18
+ hash = JSON.parse(Storage.read(slug), symbolize_names: true)
19
+ new.from_hash(hash)
20
+ rescue Errno::ENOENT
21
+ raise NotFound.new("Snapshot with a slug `#{slug}` can't be found.")
22
+ end
23
+
24
+ def self.grouped_by_test_case
25
+ all.group_by do |snapshot|
26
+ snapshot.context.test_group
27
+ end
28
+ end
29
+
30
+ private_class_method def self.all
31
+ snapshots = Storage.list.map { |slug| find(slug) }
32
+
33
+ order_by_line_number(snapshots)
34
+ end
35
+
36
+ private_class_method def self.order_by_line_number(snapshots)
37
+ snapshots.sort_by do |snapshot|
38
+ snapshot.context.order_index
39
+ end
40
+ end
41
+
42
+ # @private
43
+ def extract(snapshotee:, context:)
44
+ extract_type_specific_data(snapshotee)
45
+ extract_context(context)
46
+
47
+ @slug = @context.to_slug
48
+ @created_at = Time.current
49
+ self
50
+ end
51
+
52
+ # @private
53
+ def persist
54
+ Storage.write(slug, JSON.pretty_generate(as_json))
55
+ self
56
+ end
57
+
58
+ # @private
59
+ def from_hash(hash)
60
+ from_hash_type_specific_data(hash)
61
+ from_hash_context(hash)
62
+
63
+ @slug = hash[:slug]
64
+ @created_at = Time.zone.parse(hash[:created_at])
65
+ self
66
+ end
67
+
68
+ private
69
+
70
+ def extract_type_specific_data(snapshotee)
71
+ @type_data = Type.extract(snapshotee)
72
+ end
73
+
74
+ def extract_context(context)
75
+ @context = Context.extract(context)
76
+ end
77
+
78
+ def from_hash_type_specific_data(hash)
79
+ @type_data = Type.from_hash(hash[:type_data])
80
+ end
81
+
82
+ def from_hash_context(hash)
83
+ @context = Context.from_hash(hash[:context])
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Snapshot Inspector</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "snapshot_inspector/application", media: "all" %>
9
+ <%= snapshot_inspector_importmap_tags %>
10
+ <%= javascript_import_module_tag "snapshot_inspector/application" %>
11
+ <% if defined?(Hotwire::Livereload::DISABLE_FILE) && !File.exist?(Rails.root.join(Hotwire::Livereload::DISABLE_FILE)) %>
12
+ <%= hotwire_livereload_tags %>
13
+ <% end %>
14
+ </head>
15
+ <body id="<%= "#{controller_name}_#{action_name}" %>">
16
+ <%= yield %>
17
+ </body>
18
+ </html>
@@ -0,0 +1,29 @@
1
+ <main>
2
+ <h1>Snapshots</h1>
3
+
4
+ <% if @grouped_by_test_class.blank? %>
5
+ <p>Place `take_screenshot response` in your integration tests after a `response` object is populated and run the tests. Snapshots will appear below.</p>
6
+ <% end %>
7
+
8
+ <%= form_tag "", method: :get, class: "enable_javascript" do %>
9
+ <%= label_tag :enable_javascript do %>
10
+ <%= check_box_tag :enable_javascript, "true", params[:enable_javascript] == "true", onclick: "submit()" %>
11
+ <span>Open snapshots with JavaScript enabled (by default all JavaScript tags are removed)</span>
12
+ <% end %>
13
+ <% end %>
14
+
15
+ <% @grouped_by_test_class.each do |test_group, snapshots| %>
16
+ <h2><%= test_group %></h2>
17
+
18
+ <ul>
19
+ <% snapshots.each do |snapshot| %>
20
+ <li>
21
+ <%= link_to SnapshotInspector::SnapshotsHelper.snapshot_path(snapshot, enable_javascript: params[:enable_javascript]) do %>
22
+ <%= snapshot.context.name %>
23
+ <%= if snapshot.context.take_snapshot_index > 0 then "(#{(snapshot.context.take_snapshot_index + 1).ordinalize} in the same test)" end %>
24
+ <% end %>
25
+ </li>
26
+ <% end %>
27
+ </ul>
28
+ <% end %>
29
+ </main>
@@ -0,0 +1,107 @@
1
+ <header>
2
+ <dl>
3
+ <% if @email.respond_to?(:smtp_envelope_from) && Array(@email.from) != Array(@email.smtp_envelope_from) %>
4
+ <dt>SMTP-From:</dt>
5
+ <dd id="smtp_from"><%= @email.smtp_envelope_from %></dd>
6
+ <% end %>
7
+
8
+ <% if @email.respond_to?(:smtp_envelope_to) && @email.to != @email.smtp_envelope_to %>
9
+ <dt>SMTP-To:</dt>
10
+ <dd id="smtp_to"><%= @email.smtp_envelope_to %></dd>
11
+ <% end %>
12
+
13
+ <dt>From:</dt>
14
+ <dd id="from"><%= @email.header['from'] %></dd>
15
+
16
+ <% if @email.reply_to %>
17
+ <dt>Reply-To:</dt>
18
+ <dd id="reply_to"><%= @email.header['reply-to'] %></dd>
19
+ <% end %>
20
+
21
+ <dt>To:</dt>
22
+ <dd id="to"><%= @email.header['to'] %></dd>
23
+
24
+ <% if @email.cc %>
25
+ <dt>CC:</dt>
26
+ <dd id="cc"><%= @email.header['cc'] %></dd>
27
+ <% end %>
28
+
29
+ <% if @email.bcc %>
30
+ <dt>BCC:</dt>
31
+ <dd id="bcc"><%= @email.header['bcc'] %></dd>
32
+ <% end %>
33
+
34
+ <dt>Date:</dt>
35
+ <dd id="date"><%= Time.current.rfc2822 %></dd>
36
+
37
+ <dt>Subject:</dt>
38
+ <dd><strong id="subject"><%= @email.subject %></strong></dd>
39
+
40
+ <% unless @email.attachments.nil? || @email.attachments.empty? %>
41
+ <dt>Attachments:</dt>
42
+ <dd>
43
+ <% @email.attachments.each do |a| %>
44
+ <% filename = a.respond_to?(:original_filename) ? a.original_filename : a.filename %>
45
+ <%= link_to filename, "data:application/octet-stream;charset=utf-8;base64,#{Base64.encode64(a.body.to_s)}", download: filename %>
46
+ <% end %>
47
+ </dd>
48
+ <% end %>
49
+
50
+ <dt>Format:</dt>
51
+ <% if @email.html_part && @email.text_part %>
52
+ <dd>
53
+ <select id="part" onchange="refreshBody();">
54
+ <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="<%= part_query('text/html') %>">View as HTML email</option>
55
+ <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="<%= part_query('text/plain') %>">View as plain-text email</option>
56
+ </select>
57
+ </dd>
58
+ <% elsif @part %>
59
+ <dd id="mime_type" data-mime-type="<%= part_query(@part.mime_type) %>"><%= @part.mime_type == 'text/html' ? 'HTML email' : 'plain-text email' %></dd>
60
+ <% else %>
61
+ <dd id="mime_type" data-mime-type=""></dd>
62
+ <% end %>
63
+
64
+ <% unless @email.header_fields.blank? %>
65
+ <dt>Headers:</dt>
66
+ <dd>
67
+ <details>
68
+ <summary>Show all headers</summary>
69
+ <table>
70
+ <% @email.header_fields.each do |field| %>
71
+ <tr>
72
+ <td align="right" style="color: #7f7f7f"><%= field.name %>:</td>
73
+ <td><%= field.value %></td>
74
+ </tr>
75
+ <% end %>
76
+ </table>
77
+ </details>
78
+ </dd>
79
+ <% end %>
80
+
81
+ <dt>EML File:</dt>
82
+ <dd><%= link_to "Download", format: :eml %></dd>
83
+ </dl>
84
+ </header>
85
+
86
+ <% if @part && @part.mime_type %>
87
+ <iframe name="messageBody" src="<%= raw_mail_snapshot_path(slug: @snapshot.slug, part: @part.mime_type) %>"></iframe>
88
+ <% else %>
89
+ <p>
90
+ You are trying to preview an email that does not have any content.
91
+ This is probably because the <em>mail</em> method has not been called in <em><%= @preview.preview_name %>#<%= @email_action %></em>.
92
+ </p>
93
+ <% end %>
94
+
95
+ <script>
96
+ function refreshBody() {
97
+ const part_select = document.querySelector('select#part');
98
+ const part_param = part_select ?
99
+ part_select.options[part_select.selectedIndex].value :
100
+ document.querySelector('#mime_type').dataset.mimeType;
101
+
102
+ const url = location.pathname.replace(/\.(txt|html)$/, '');
103
+ const format = /html/.test(part_param) ? '.html' : '.txt';
104
+
105
+ location.href = url + format;
106
+ }
107
+ </script>
@@ -0,0 +1,8 @@
1
+ <main id="not_found">
2
+ <h1>Not Found</h1>
3
+
4
+ <p>
5
+ <%= @error %>
6
+ Go back to <%= link_to "a list of snapshots", root_path, style: "text-decoration: underline; color: #666" %>.
7
+ </p>
8
+ </main>
@@ -0,0 +1 @@
1
+ <%= prepare_for_render(@snapshot.body, enable_javascript: params[:enable_javascript]) %>
@@ -0,0 +1 @@
1
+ <iframe id="body" src="<%= raw_response_snapshot_path(@snapshot.slug, enable_javascript: params[:enable_javascript]) %>"></iframe>
@@ -0,0 +1,12 @@
1
+ require "snapshot_inspector"
2
+
3
+ SnapshotInspector.configuration.importmap.draw do
4
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
5
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
6
+ pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js"
7
+ pin "hotkeys-js", to: "https://ga.jspm.io/npm:hotkeys-js@3.10.1/dist/hotkeys.esm.js"
8
+
9
+ pin "application", to: "snapshot_inspector/application.js", preload: true
10
+
11
+ pin_all_from SnapshotInspector::Engine.root.join("app/assets/javascripts/snapshot_inspector/controllers"), under: "controllers", to: "snapshot_inspector/controllers"
12
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ SnapshotInspector::Engine.routes.draw do
2
+ root to: "snapshots#index"
3
+
4
+ get "mail/raw/*slug", to: "snapshots/mail#raw", as: :raw_mail_snapshot
5
+ get "mail/*slug", to: "snapshots/mail#show", as: :mail_snapshot
6
+
7
+ get "response/raw/*slug", to: "snapshots/response#raw", as: :raw_response_snapshot
8
+ get "response/*slug", to: "snapshots/response#show", as: :response_snapshot
9
+ end
@@ -0,0 +1,28 @@
1
+ require "snapshot_inspector"
2
+ require "snapshot_inspector/storage"
3
+ require "minitest"
4
+
5
+ module Minitest
6
+ class SnapshotInspectorReporter < Reporter
7
+ def report
8
+ SnapshotInspector::Storage.move_files_from_processing_directory_to_snapshots_directory if SnapshotInspector::Storage.processing_directory.exist?
9
+
10
+ io.print "\n\nInspect snapshots on #{SnapshotInspector.configuration.host + SnapshotInspector.configuration.route_path}"
11
+ end
12
+ end
13
+
14
+ class << self
15
+ def plugin_snapshot_inspector_options(opts, _options)
16
+ opts.on "--take-snapshots", "Take snapshots of responses for inspecting at #{SnapshotInspector.configuration.host + SnapshotInspector.configuration.route_path}" do
17
+ SnapshotInspector.configuration.snapshot_taking_enabled = true
18
+ end
19
+ end
20
+
21
+ def plugin_snapshot_inspector_init(_options)
22
+ return unless SnapshotInspector.configuration.snapshot_taking_enabled
23
+
24
+ reporter << SnapshotInspectorReporter.new
25
+ SnapshotInspector::Storage.clear(:processing)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,67 @@
1
+ require "importmap-rails"
2
+ require "snapshot_inspector/test/action_mailer_headers"
3
+
4
+ module SnapshotInspector
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace SnapshotInspector
7
+
8
+ config.before_configuration do |_app|
9
+ SnapshotInspector.configuration.storage_directory = Rails.root.join(SnapshotInspector::STORAGE_DIRECTORY)
10
+ end
11
+
12
+ unless Rails.env.test?
13
+ rake_tasks do
14
+ load "tasks/tmp.rake"
15
+ end
16
+ end
17
+
18
+ initializer "snapshot_inspector.importmap", before: "importmap" do |app|
19
+ app.config.importmap.paths << root.join("config/importmap.rb")
20
+ app.config.importmap.cache_sweepers << root.join("app/assets/javascripts")
21
+ end
22
+
23
+ initializer "snapshot_inspector.assets.precompile" do |app|
24
+ app.config.assets.precompile += %w[snapshot_inspector/manifest]
25
+ end
26
+
27
+ initializer "snapshot_inspector.include_test_helpers" do |_app|
28
+ if defined?(RSpec)
29
+ RSpec.configure do |config|
30
+ config.include SnapshotInspector::Test::RSpecHelpers
31
+ config.after :suite do
32
+ SnapshotInspector::Storage.move_files_from_processing_directory_to_snapshots_directory if SnapshotInspector::Storage.processing_directory.exist?
33
+ end
34
+ end
35
+ else
36
+ ActiveSupport.on_load(:active_support_test_case) do
37
+ include SnapshotInspector::Test::TestUnitHelpers
38
+ end
39
+ end
40
+
41
+ ActiveSupport.on_load(:action_mailer) do
42
+ include SnapshotInspector::Test::ActionMailerHeaders
43
+ end
44
+ end
45
+
46
+ initializer "snapshot_inspector.configure_default_url_options" do |_app|
47
+ url_options =
48
+ if Rails.application.routes.default_url_options.present?
49
+ Rails.application.routes.default_url_options
50
+ elsif Rails.application.config.action_controller.default_url_options.present?
51
+ Rails.application.config.action_controller.default_url_options
52
+ end
53
+
54
+ SnapshotInspector.configuration.host = url_options ? [url_options[:host], url_options[:port]].join(":") : SnapshotInspector.configuration.host
55
+ end
56
+
57
+ initializer "snapshot_inspector.register_eml_mime_type" do |_app|
58
+ Mime::Type.register "application/octet-stream", :eml
59
+ end
60
+
61
+ config.after_initialize do |app|
62
+ app.routes.prepend do
63
+ mount SnapshotInspector::Engine, at: SnapshotInspector.configuration.route_path
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,60 @@
1
+ module SnapshotInspector
2
+ class Storage
3
+ class << self
4
+ def snapshots_directory
5
+ SnapshotInspector.configuration.storage_directory.join("snapshots")
6
+ end
7
+
8
+ def processing_directory
9
+ SnapshotInspector.configuration.storage_directory.join("processing")
10
+ end
11
+
12
+ def write(key, value)
13
+ file_path = to_file_path_for_writing(key)
14
+ file_path.dirname.mkpath
15
+ file_path.write(value)
16
+ end
17
+
18
+ def read(key)
19
+ File.read(to_file_path_for_reading(key))
20
+ end
21
+
22
+ def list
23
+ Dir
24
+ .glob("#{snapshots_directory}/**/*.{json}")
25
+ .map { |file_path| to_key(file_path) }
26
+ end
27
+
28
+ def clear(directory = nil)
29
+ case directory
30
+ when :snapshots
31
+ snapshots_directory.rmtree
32
+ when :processing
33
+ processing_directory.rmtree
34
+ else
35
+ snapshots_directory.rmtree
36
+ processing_directory.rmtree
37
+ end
38
+ end
39
+
40
+ def move_files_from_processing_directory_to_snapshots_directory
41
+ clear(:snapshots)
42
+ processing_directory.rename(snapshots_directory)
43
+ end
44
+
45
+ private
46
+
47
+ def to_key(file_path)
48
+ file_path.gsub(snapshots_directory.to_s + "/", "").gsub(".json", "")
49
+ end
50
+
51
+ def to_file_path_for_reading(key)
52
+ snapshots_directory.join("#{key}.json")
53
+ end
54
+
55
+ def to_file_path_for_writing(key)
56
+ processing_directory.join("#{key}.json")
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,18 @@
1
+ module SnapshotInspector
2
+ module Test
3
+ module ActionMailerHeaders
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :snapshot_inspector_headers, if: -> { Rails.env.test? }
8
+ end
9
+
10
+ private
11
+
12
+ def snapshot_inspector_headers
13
+ headers["X-SnapshotInspector-Mailer-Name"] = mailer_name
14
+ headers["X-SnapshotInspector-Action-Name"] = action_name
15
+ end
16
+ end
17
+ end
18
+ end