snapshot_inspector 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|