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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +227 -0
- data/Rakefile +8 -0
- data/app/assets/config/snapshot_inspector/manifest.js +2 -0
- data/app/assets/javascripts/snapshot_inspector/application.js +1 -0
- data/app/assets/stylesheets/snapshot_inspector/application.css +33 -0
- data/app/assets/stylesheets/snapshot_inspector/snapshots/mail.css +48 -0
- data/app/assets/stylesheets/snapshot_inspector/snapshots/not_found.css +15 -0
- data/app/assets/stylesheets/snapshot_inspector/snapshots/response.css +9 -0
- data/app/assets/stylesheets/snapshot_inspector/snapshots.css +73 -0
- data/app/controllers/snapshot_inspector/application_controller.rb +14 -0
- data/app/controllers/snapshot_inspector/snapshots/mail_controller.rb +57 -0
- data/app/controllers/snapshot_inspector/snapshots/response_controller.rb +15 -0
- data/app/controllers/snapshot_inspector/snapshots_controller.rb +7 -0
- data/app/helpers/snapshot_inspector/application_helper.rb +15 -0
- data/app/helpers/snapshot_inspector/snapshots_helper.rb +37 -0
- data/app/mailers/snapshot_inspector/application_mailer.rb +6 -0
- data/app/models/snapshot_inspector/snapshot/context.rb +49 -0
- data/app/models/snapshot_inspector/snapshot/mail_type.rb +35 -0
- data/app/models/snapshot_inspector/snapshot/response_type.rb +19 -0
- data/app/models/snapshot_inspector/snapshot/rspec_context.rb +52 -0
- data/app/models/snapshot_inspector/snapshot/test_unit_context.rb +44 -0
- data/app/models/snapshot_inspector/snapshot/type.rb +52 -0
- data/app/models/snapshot_inspector/snapshot.rb +86 -0
- data/app/views/layouts/snapshot_inspector/application.html.erb +18 -0
- data/app/views/snapshot_inspector/snapshots/index.html.erb +29 -0
- data/app/views/snapshot_inspector/snapshots/mail/show.html.erb +107 -0
- data/app/views/snapshot_inspector/snapshots/not_found.html.erb +8 -0
- data/app/views/snapshot_inspector/snapshots/response/raw.html.erb +1 -0
- data/app/views/snapshot_inspector/snapshots/response/show.html.erb +1 -0
- data/config/importmap.rb +12 -0
- data/config/routes.rb +9 -0
- data/lib/minitest/snapshot_inspector_plugin.rb +28 -0
- data/lib/snapshot_inspector/engine.rb +67 -0
- data/lib/snapshot_inspector/storage.rb +60 -0
- data/lib/snapshot_inspector/test/action_mailer_headers.rb +18 -0
- data/lib/snapshot_inspector/test/rspec_helpers.rb +45 -0
- data/lib/snapshot_inspector/test/test_unit_helpers.rb +48 -0
- data/lib/snapshot_inspector/version.rb +3 -0
- data/lib/snapshot_inspector.rb +42 -0
- data/lib/tasks/tmp.rake +10 -0
- 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 @@
|
|
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>
|
data/config/importmap.rb
ADDED
@@ -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
|