snapshot_ui 0.4.0 → 0.5.0
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 +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +145 -4
- data/lib/minitest/snapshot_ui_plugin.rb +9 -0
- data/lib/snapshot_ui/colorize.rb +15 -4
- data/lib/snapshot_ui/configuration.rb +12 -3
- data/lib/snapshot_ui/snapshot/context.rb +4 -1
- data/lib/snapshot_ui/snapshot/storage.rb +54 -3
- data/lib/snapshot_ui/snapshot.rb +54 -13
- data/lib/snapshot_ui/test/minitest_helpers.rb +21 -6
- data/lib/snapshot_ui/version.rb +1 -1
- data/lib/snapshot_ui/web/application.rb +100 -39
- data/lib/snapshot_ui/web/assets/javascripts/application.js +4 -2
- data/lib/snapshot_ui/web/assets/javascripts/controllers/refresh_controller.js +76 -12
- data/lib/snapshot_ui/web/assets/javascripts/controllers/source_location_controller.js +14 -6
- data/lib/snapshot_ui/web/assets/javascripts/vendor/stimulus.js +2564 -0
- data/lib/snapshot_ui/web/assets/javascripts/vendor/turbo.js +7257 -0
- data/lib/snapshot_ui/web/assets/stylesheets/application.css +121 -0
- data/lib/snapshot_ui/web/views/layout.html.erb +4 -9
- data/lib/snapshot_ui/web/views/snapshots/index.html.erb +5 -11
- data/lib/snapshot_ui/web.rb +27 -10
- data/lib/snapshot_ui.rb +4 -2
- data/snapshot_ui.gemspec +8 -14
- metadata +17 -76
- data/bin/snapshot_ui +0 -7
- data/lib/snapshot_ui/cli/watcher.ru +0 -91
- data/lib/snapshot_ui/cli.rb +0 -29
- data/lib/snapshot_ui/web/assets/javascripts/channels/consumer.js +0 -3
- data/lib/snapshot_ui/web/assets/javascripts/channels/index.js +0 -1
- data/lib/snapshot_ui/web/assets/javascripts/channels/refresh_channel.js +0 -27
|
@@ -159,6 +159,7 @@ body#not_found p {
|
|
|
159
159
|
background:#eee;
|
|
160
160
|
color: #666;
|
|
161
161
|
text-align:center;
|
|
162
|
+
width: max-content;
|
|
162
163
|
display:none;
|
|
163
164
|
}
|
|
164
165
|
|
|
@@ -182,3 +183,123 @@ body#not_found p {
|
|
|
182
183
|
height: 2px;
|
|
183
184
|
background-color: lightskyblue;
|
|
184
185
|
}
|
|
186
|
+
|
|
187
|
+
.type_badge {
|
|
188
|
+
display: inline-block;
|
|
189
|
+
margin-left: 8px;
|
|
190
|
+
padding: 1px 8px;
|
|
191
|
+
font-size: 55%;
|
|
192
|
+
font-weight: bold;
|
|
193
|
+
text-transform: uppercase;
|
|
194
|
+
letter-spacing: 0.05em;
|
|
195
|
+
vertical-align: middle;
|
|
196
|
+
border-radius: 10px;
|
|
197
|
+
background: #eef2ff;
|
|
198
|
+
color: #4f46e5;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Mail snapshots */
|
|
202
|
+
body#snapshots_mail {
|
|
203
|
+
background: #f6f7f9;
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: column;
|
|
206
|
+
height: 100%;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
body#snapshots_mail .mail {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-direction: column;
|
|
212
|
+
height: 100%;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
body#snapshots_mail .mail_header {
|
|
216
|
+
background: #fff;
|
|
217
|
+
border-bottom: 1px solid #e5e7eb;
|
|
218
|
+
padding: 20px 28px;
|
|
219
|
+
font-size: 15px;
|
|
220
|
+
line-height: 1.5;
|
|
221
|
+
flex: 0 0 auto;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
body#snapshots_mail .mail_header .back {
|
|
225
|
+
color: #6b7280;
|
|
226
|
+
text-decoration: none;
|
|
227
|
+
font-size: 13px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
body#snapshots_mail .mail_header .back:hover { text-decoration: underline; }
|
|
231
|
+
|
|
232
|
+
body#snapshots_mail .mail_header h1 {
|
|
233
|
+
margin: 8px 0 2px;
|
|
234
|
+
font-size: 22px;
|
|
235
|
+
font-weight: 600;
|
|
236
|
+
color: #111827;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
body#snapshots_mail .mail_header .mailer {
|
|
240
|
+
margin: 0 0 12px;
|
|
241
|
+
color: #9ca3af;
|
|
242
|
+
font-family: Menlo, Courier, monospace;
|
|
243
|
+
font-size: 12px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
body#snapshots_mail dl.fields {
|
|
247
|
+
display: grid;
|
|
248
|
+
grid-template-columns: max-content 1fr;
|
|
249
|
+
gap: 2px 14px;
|
|
250
|
+
margin: 0 0 14px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
body#snapshots_mail dl.fields dt { color: #9ca3af; font-size: 13px; }
|
|
254
|
+
body#snapshots_mail dl.fields dd { margin: 0; color: #374151; font-size: 13px; }
|
|
255
|
+
|
|
256
|
+
body#snapshots_mail .mail_toolbar {
|
|
257
|
+
display: flex;
|
|
258
|
+
align-items: center;
|
|
259
|
+
gap: 16px;
|
|
260
|
+
margin: 4px 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
body#snapshots_mail .parts { display: inline-flex; border: 1px solid #d1d5db; border-radius: 8px; overflow: hidden; }
|
|
264
|
+
body#snapshots_mail .part_tab {
|
|
265
|
+
padding: 5px 14px;
|
|
266
|
+
font-size: 13px;
|
|
267
|
+
color: #374151;
|
|
268
|
+
text-decoration: none;
|
|
269
|
+
background: #fff;
|
|
270
|
+
}
|
|
271
|
+
body#snapshots_mail .part_tab + .part_tab { border-left: 1px solid #d1d5db; }
|
|
272
|
+
body#snapshots_mail .part_tab.is_active { background: #4f46e5; color: #fff; }
|
|
273
|
+
body#snapshots_mail .part_single { font-size: 13px; color: #6b7280; }
|
|
274
|
+
|
|
275
|
+
body#snapshots_mail .eml { font-size: 13px; color: #4f46e5; text-decoration: none; }
|
|
276
|
+
body#snapshots_mail .eml:hover { text-decoration: underline; }
|
|
277
|
+
|
|
278
|
+
body#snapshots_mail .attachments { margin-top: 12px; font-size: 13px; }
|
|
279
|
+
body#snapshots_mail .attachments_label { color: #9ca3af; margin-right: 6px; }
|
|
280
|
+
body#snapshots_mail .attachment {
|
|
281
|
+
display: inline-block;
|
|
282
|
+
margin: 2px 6px 2px 0;
|
|
283
|
+
padding: 3px 10px;
|
|
284
|
+
background: #eef2ff;
|
|
285
|
+
color: #4f46e5;
|
|
286
|
+
border-radius: 6px;
|
|
287
|
+
text-decoration: none;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
body#snapshots_mail details.all_headers { margin-top: 12px; }
|
|
291
|
+
body#snapshots_mail details.all_headers summary { cursor: pointer; color: #6b7280; font-size: 13px; }
|
|
292
|
+
body#snapshots_mail details.all_headers table { margin-top: 8px; border-collapse: collapse; font-size: 12px; }
|
|
293
|
+
body#snapshots_mail details.all_headers td { padding: 2px 10px 2px 0; vertical-align: top; }
|
|
294
|
+
body#snapshots_mail details.all_headers td.name { color: #9ca3af; text-align: right; white-space: nowrap; }
|
|
295
|
+
|
|
296
|
+
body#snapshots_mail iframe.mail_body {
|
|
297
|
+
flex: 1 1 auto;
|
|
298
|
+
width: 100%;
|
|
299
|
+
border: 0;
|
|
300
|
+
background: #fff;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
body#snapshots_mail iframe.mail_body.is_text { background: #fff; }
|
|
304
|
+
|
|
305
|
+
body#snapshots_mail .empty { padding: 40px 28px; color: #6b7280; }
|
|
@@ -6,20 +6,15 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
7
|
<meta name="turbo-refresh-method" content="morph">
|
|
8
8
|
<meta name="turbo-refresh-scroll" content="preserve">
|
|
9
|
-
<link rel="stylesheet" href="<%= stylesheet_path("application.css") %>">
|
|
10
|
-
<meta name="action-cable-url" content="<%= SnapshotUI.configuration.live_websocket_url %>" />
|
|
9
|
+
<link rel="stylesheet" href="<%= stylesheet_path("application.css") %>" data-turbo-track="reload">
|
|
11
10
|
<script type="importmap" data-turbo-track="reload">
|
|
12
11
|
{
|
|
13
12
|
"imports": {
|
|
14
13
|
"application": "<%= javascript_path("application.js") %>",
|
|
15
|
-
"@hotwired/
|
|
16
|
-
"@hotwired/
|
|
17
|
-
"@rails/actioncable": "https://cdn.jsdelivr.net/npm/@rails/actioncable@7.1.3-4/+esm",
|
|
14
|
+
"@hotwired/turbo": "<%= javascript_path("vendor/turbo.js") %>",
|
|
15
|
+
"@hotwired/stimulus": "<%= javascript_path("vendor/stimulus.js") %>",
|
|
18
16
|
"controllers/refresh_controller": "<%= javascript_path("controllers/refresh_controller.js") %>",
|
|
19
|
-
"controllers/source_location_controller": "<%= javascript_path("controllers/source_location_controller.js") %>"
|
|
20
|
-
"channels/consumer": "<%= javascript_path("channels/consumer.js") %>",
|
|
21
|
-
"channels": "<%= javascript_path("channels/index.js") %>",
|
|
22
|
-
"channels/refresh_channel": "<%= javascript_path("channels/refresh_channel.js") %>"
|
|
17
|
+
"controllers/source_location_controller": "<%= javascript_path("controllers/source_location_controller.js") %>"
|
|
23
18
|
}
|
|
24
19
|
}
|
|
25
20
|
</script>
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
<h1>Snapshots</h1>
|
|
3
3
|
|
|
4
4
|
<% if @grouped_by_test_class.empty? %>
|
|
5
|
+
<p>A list of snapshots will appear here once you've taken them.</p>
|
|
6
|
+
|
|
5
7
|
<p>
|
|
6
8
|
In your integration tests, once a request has been made,
|
|
7
|
-
add <code>#
|
|
9
|
+
add <code>#take_snapshot</code>. Run the tests with <code>TAKE_SNAPSHOTS=1</code>, and the snapshot will appear here.
|
|
8
10
|
</p>
|
|
9
11
|
|
|
10
12
|
<p>Look below at an example integration test.</p>
|
|
@@ -32,15 +34,7 @@ class ApplicationTest < Minitest::Spec
|
|
|
32
34
|
end
|
|
33
35
|
</pre>
|
|
34
36
|
|
|
35
|
-
<p>
|
|
36
|
-
To activate live updates for this page, execute the following command in your terminal.<br>
|
|
37
|
-
</p>
|
|
38
|
-
<pre>
|
|
39
|
-
bundle exec snapshot_ui watch SNAPSHOT_DIRECTORY</pre>
|
|
40
|
-
<p>
|
|
41
|
-
where <code>SNAPSHOT_DIRECTORY</code> is a path to a directory that contains
|
|
42
|
-
snapshot UI data, e.g. <code>tmp/snapshot_ui</code>.
|
|
43
|
-
</p>
|
|
37
|
+
<p>This page refreshes automatically whenever snapshots are updated.</p>
|
|
44
38
|
<% end %>
|
|
45
39
|
|
|
46
40
|
<% @grouped_by_test_class.each do |test_group, snapshots| %>
|
|
@@ -50,7 +44,7 @@ bundle exec snapshot_ui watch SNAPSHOT_DIRECTORY</pre>
|
|
|
50
44
|
<ul>
|
|
51
45
|
<% snapshots.each do |snapshot| %>
|
|
52
46
|
<li>
|
|
53
|
-
<a href="<%= snapshot_path(snapshot.slug) %>"><%= snapshot_title(snapshot) %></a
|
|
47
|
+
<a href="<%= snapshot_path(snapshot.slug) %>"><%= snapshot_title(snapshot) %></a><%= snapshot_type_badge(snapshot) %>
|
|
54
48
|
<button
|
|
55
49
|
class="copy_source_location"
|
|
56
50
|
data-controller="source-location"
|
data/lib/snapshot_ui/web.rb
CHANGED
|
@@ -7,21 +7,38 @@ require_relative "web/application"
|
|
|
7
7
|
|
|
8
8
|
module SnapshotUI
|
|
9
9
|
class Web
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
@renderers = {}
|
|
11
|
+
|
|
12
|
+
# Registers a renderer for a snapshot type. A renderer responds to
|
|
13
|
+
# +show(snapshot, view)+ and +raw(snapshot, view)+, each returning a Rack
|
|
14
|
+
# response triple; +view+ is the Application instance, which exposes request
|
|
15
|
+
# and URL helpers. snapshot_ui-rails registers one for the "mail" type.
|
|
16
|
+
def self.register_renderer(type, renderer)
|
|
17
|
+
@renderers[type.to_s] = renderer
|
|
12
18
|
end
|
|
13
19
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
def self.renderer(type)
|
|
21
|
+
@renderers[type.to_s]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The Rack application serving the UI: the static assets in front of
|
|
25
|
+
# Application, which handles a request per instance. Built once.
|
|
26
|
+
def self.app
|
|
27
|
+
@app ||= Rack::Builder.app do
|
|
28
|
+
use Rack::Static,
|
|
29
|
+
root: "#{File.dirname(__FILE__)}/web/assets",
|
|
30
|
+
urls: %w[/stylesheets /javascripts]
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
run ->(env) { Application.new.call(env) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
23
35
|
|
|
36
|
+
def self.call(env)
|
|
24
37
|
app.call(env)
|
|
25
38
|
end
|
|
39
|
+
|
|
40
|
+
def call(env)
|
|
41
|
+
self.class.call(env)
|
|
42
|
+
end
|
|
26
43
|
end
|
|
27
44
|
end
|
data/lib/snapshot_ui.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require_relative "snapshot_ui/version"
|
|
3
6
|
require_relative "snapshot_ui/snapshot"
|
|
4
7
|
require_relative "snapshot_ui/configuration"
|
|
5
8
|
|
|
@@ -7,8 +10,7 @@ module SnapshotUI
|
|
|
7
10
|
DEFAULT_CONFIGURATION = {
|
|
8
11
|
project_root_directory: nil,
|
|
9
12
|
storage_directory: nil,
|
|
10
|
-
web_url: "http://localhost:3000/ui/snapshots"
|
|
11
|
-
live_websocket_url: "http://localhost:49152/live"
|
|
13
|
+
web_url: "http://localhost:3000/ui/snapshots"
|
|
12
14
|
}.freeze
|
|
13
15
|
|
|
14
16
|
def self.configure
|
data/snapshot_ui.gemspec
CHANGED
|
@@ -11,25 +11,19 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = "Take snapshots of UI during testing for inspection in a browser."
|
|
12
12
|
spec.homepage = "https://github.com/tomazzlender/snapshot_ui"
|
|
13
13
|
spec.license = "MIT"
|
|
14
|
-
spec.required_ruby_version = ">= 3.
|
|
14
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
15
15
|
|
|
16
16
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
17
17
|
|
|
18
18
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
20
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
19
|
+
spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/main/gems/snapshot_ui"
|
|
20
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/gems/snapshot_ui/CHANGELOG.md"
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
spec.
|
|
22
|
+
gem_root = File.expand_path(__dir__)
|
|
23
|
+
spec.files = Dir.chdir(gem_root) do
|
|
24
|
+
Dir.glob(%w[lib/**/* snapshot_ui.gemspec README.md CHANGELOG.md LICENSE.txt]).select { |path| File.file?(path) }
|
|
25
|
+
end
|
|
24
26
|
spec.require_paths = ["lib"]
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
spec.add_dependency "rack", "~> 3.0"
|
|
28
|
-
spec.add_dependency "listen", "~> 3.9"
|
|
29
|
-
spec.add_dependency "async-websocket", "~> 0.26"
|
|
30
|
-
spec.add_dependency "falcon", "~> 0.47"
|
|
31
|
-
spec.add_dependency "thor", "~> 1.3"
|
|
32
|
-
|
|
33
|
-
# For more information and examples about making a new gem, check out our
|
|
34
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
|
28
|
+
spec.add_dependency "rack", ">= 2.2", "< 4"
|
|
35
29
|
end
|
metadata
CHANGED
|
@@ -1,101 +1,45 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: snapshot_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tomaz Zlender
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rack
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
20
|
-
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '3.0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: listen
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '3.9'
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '3.9'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: async-websocket
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
18
|
+
version: '2.2'
|
|
19
|
+
- - "<"
|
|
46
20
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
21
|
+
version: '4'
|
|
48
22
|
type: :runtime
|
|
49
23
|
prerelease: false
|
|
50
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
25
|
requirements:
|
|
52
|
-
- - "
|
|
26
|
+
- - ">="
|
|
53
27
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
55
|
-
-
|
|
56
|
-
name: falcon
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0.47'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0.47'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: thor
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '1.3'
|
|
76
|
-
type: :runtime
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
28
|
+
version: '2.2'
|
|
29
|
+
- - "<"
|
|
81
30
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
83
|
-
description:
|
|
31
|
+
version: '4'
|
|
84
32
|
email:
|
|
85
33
|
- tomaz@zlender.se
|
|
86
|
-
executables:
|
|
87
|
-
- snapshot_ui
|
|
34
|
+
executables: []
|
|
88
35
|
extensions: []
|
|
89
36
|
extra_rdoc_files: []
|
|
90
37
|
files:
|
|
91
38
|
- CHANGELOG.md
|
|
92
39
|
- LICENSE.txt
|
|
93
40
|
- README.md
|
|
94
|
-
- bin/snapshot_ui
|
|
95
41
|
- lib/minitest/snapshot_ui_plugin.rb
|
|
96
42
|
- lib/snapshot_ui.rb
|
|
97
|
-
- lib/snapshot_ui/cli.rb
|
|
98
|
-
- lib/snapshot_ui/cli/watcher.ru
|
|
99
43
|
- lib/snapshot_ui/colorize.rb
|
|
100
44
|
- lib/snapshot_ui/configuration.rb
|
|
101
45
|
- lib/snapshot_ui/snapshot.rb
|
|
@@ -106,11 +50,10 @@ files:
|
|
|
106
50
|
- lib/snapshot_ui/web.rb
|
|
107
51
|
- lib/snapshot_ui/web/application.rb
|
|
108
52
|
- lib/snapshot_ui/web/assets/javascripts/application.js
|
|
109
|
-
- lib/snapshot_ui/web/assets/javascripts/channels/consumer.js
|
|
110
|
-
- lib/snapshot_ui/web/assets/javascripts/channels/index.js
|
|
111
|
-
- lib/snapshot_ui/web/assets/javascripts/channels/refresh_channel.js
|
|
112
53
|
- lib/snapshot_ui/web/assets/javascripts/controllers/refresh_controller.js
|
|
113
54
|
- lib/snapshot_ui/web/assets/javascripts/controllers/source_location_controller.js
|
|
55
|
+
- lib/snapshot_ui/web/assets/javascripts/vendor/stimulus.js
|
|
56
|
+
- lib/snapshot_ui/web/assets/javascripts/vendor/turbo.js
|
|
114
57
|
- lib/snapshot_ui/web/assets/stylesheets/application.css
|
|
115
58
|
- lib/snapshot_ui/web/views/layout.html.erb
|
|
116
59
|
- lib/snapshot_ui/web/views/snapshots/index.html.erb
|
|
@@ -123,9 +66,8 @@ licenses:
|
|
|
123
66
|
metadata:
|
|
124
67
|
allowed_push_host: https://rubygems.org
|
|
125
68
|
homepage_uri: https://github.com/tomazzlender/snapshot_ui
|
|
126
|
-
source_code_uri: https://github.com/tomazzlender/snapshot_ui
|
|
127
|
-
changelog_uri: https://github.com/tomazzlender/snapshot_ui/blob/main/CHANGELOG.md
|
|
128
|
-
post_install_message:
|
|
69
|
+
source_code_uri: https://github.com/tomazzlender/snapshot_ui/tree/main/gems/snapshot_ui
|
|
70
|
+
changelog_uri: https://github.com/tomazzlender/snapshot_ui/blob/main/gems/snapshot_ui/CHANGELOG.md
|
|
129
71
|
rdoc_options: []
|
|
130
72
|
require_paths:
|
|
131
73
|
- lib
|
|
@@ -133,15 +75,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
133
75
|
requirements:
|
|
134
76
|
- - ">="
|
|
135
77
|
- !ruby/object:Gem::Version
|
|
136
|
-
version: 3.
|
|
78
|
+
version: 3.3.0
|
|
137
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
80
|
requirements:
|
|
139
81
|
- - ">="
|
|
140
82
|
- !ruby/object:Gem::Version
|
|
141
83
|
version: '0'
|
|
142
84
|
requirements: []
|
|
143
|
-
rubygems_version: 3.
|
|
144
|
-
signing_key:
|
|
85
|
+
rubygems_version: 3.6.9
|
|
145
86
|
specification_version: 4
|
|
146
87
|
summary: Take snapshots of UI during testing for inspection in a browser.
|
|
147
88
|
test_files: []
|
data/bin/snapshot_ui
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "async"
|
|
4
|
-
require "async/http/endpoint"
|
|
5
|
-
require "async/websocket/client"
|
|
6
|
-
require "async/websocket/adapters/rack"
|
|
7
|
-
require "listen"
|
|
8
|
-
require "set"
|
|
9
|
-
require "uri"
|
|
10
|
-
|
|
11
|
-
load ENV.fetch("SNAPSHOT_UI_INITIALIZER_FILE")
|
|
12
|
-
|
|
13
|
-
REFRESH_MESSAGE = {identifier: "{\"channel\":\"RefreshChannel\"}", message: "refresh"}.to_json
|
|
14
|
-
CONFIRM_SUBSCRIPTION_MESSAGE = {identifier: "{\"channel\":\"RefreshChannel\"}", type: "confirm_subscription"}.to_json
|
|
15
|
-
|
|
16
|
-
@connections = Set.new
|
|
17
|
-
|
|
18
|
-
live_websocket_uri = URI.parse(SnapshotUI.configuration.live_websocket_url)
|
|
19
|
-
|
|
20
|
-
run lambda { |env|
|
|
21
|
-
if env["REQUEST_PATH"] == live_websocket_uri.path
|
|
22
|
-
Async::WebSocket::Adapters::Rack.open(env, protocols: ["actioncable-v1-json"]) do |connection|
|
|
23
|
-
@connections << connection
|
|
24
|
-
|
|
25
|
-
Console.info "Client connected."
|
|
26
|
-
connection.write(CONFIRM_SUBSCRIPTION_MESSAGE)
|
|
27
|
-
connection.flush
|
|
28
|
-
|
|
29
|
-
Async do |ping_task|
|
|
30
|
-
loop do
|
|
31
|
-
connection.write({type: "ping", message: Time.now.to_i.to_s}.to_json)
|
|
32
|
-
connection.flush
|
|
33
|
-
sleep(2)
|
|
34
|
-
end
|
|
35
|
-
ensure
|
|
36
|
-
ping_task&.stop
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
while connection.read
|
|
40
|
-
end
|
|
41
|
-
rescue Protocol::WebSocket::ClosedError
|
|
42
|
-
Console.info "Client disconnected."
|
|
43
|
-
@connections.delete(connection)
|
|
44
|
-
ensure
|
|
45
|
-
Console.info "Client disconnected."
|
|
46
|
-
@connections.delete(connection)
|
|
47
|
-
end
|
|
48
|
-
else
|
|
49
|
-
[404, {}, ["404 Not Found"]]
|
|
50
|
-
end
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
Async do |client_task|
|
|
54
|
-
endpoint = Async::HTTP::Endpoint.parse(SnapshotUI.configuration.live_websocket_url)
|
|
55
|
-
|
|
56
|
-
Async::WebSocket::Client.connect(endpoint) do |connection|
|
|
57
|
-
Async do |listener_task|
|
|
58
|
-
detect_snapshots_update(listener_task)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
while (message = connection.read)
|
|
62
|
-
if message == REFRESH_MESSAGE
|
|
63
|
-
Console.info "Snapshots updated."
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
ensure
|
|
67
|
-
client_task&.stop
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def detect_snapshots_update(task)
|
|
72
|
-
Pathname.new(SnapshotUI.configuration.storage_directory).mkpath
|
|
73
|
-
listener = Listen.to(SnapshotUI.configuration.storage_directory) { |_modified, _added, _removed| broadcast_update }
|
|
74
|
-
|
|
75
|
-
task.async do
|
|
76
|
-
listener.start
|
|
77
|
-
task.sleep
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
Console.info("Watching for snapshots updates in #{SnapshotUI.configuration.storage_directory}")
|
|
81
|
-
Console.info("Review snapshots on #{SnapshotUI.configuration.web_url}")
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def broadcast_update
|
|
85
|
-
@connections.each do |connection|
|
|
86
|
-
connection.write(REFRESH_MESSAGE)
|
|
87
|
-
connection.flush
|
|
88
|
-
rescue => e
|
|
89
|
-
Console.error "Failed to send message to client: #{e.message}"
|
|
90
|
-
end
|
|
91
|
-
end
|
data/lib/snapshot_ui/cli.rb
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
require "bundler/setup"
|
|
2
|
-
require "thor"
|
|
3
|
-
require "pathname"
|
|
4
|
-
require "uri"
|
|
5
|
-
|
|
6
|
-
module SnapshotUI
|
|
7
|
-
class CLI < Thor
|
|
8
|
-
desc "live SNAPSHOT_UI_INITIALIZER_FILE", "Run the command to enable live refreshing of UI snapshots."
|
|
9
|
-
def live(initializer_file)
|
|
10
|
-
unless File.exist?(initializer_file)
|
|
11
|
-
puts "The provided initializer file `#{initializer_file}` doesn't exist. Please double check the path."
|
|
12
|
-
exit 1
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
load initializer_file
|
|
16
|
-
|
|
17
|
-
websocket_host_uri = URI.parse(SnapshotUI.configuration.live_websocket_url)
|
|
18
|
-
websocket_host_uri.path = ""
|
|
19
|
-
|
|
20
|
-
config_path = Pathname.new(__dir__).join("cli", "watcher.ru").cleanpath.to_s
|
|
21
|
-
|
|
22
|
-
exec "SNAPSHOT_UI_INITIALIZER_FILE=#{initializer_file} bundle exec falcon serve --bind #{websocket_host_uri} --count 1 --config #{config_path}"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def self.exit_on_failure?
|
|
26
|
-
true
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import "channels/refresh_channel"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import consumer from "channels/consumer"
|
|
2
|
-
import * as Turbo from "@hotwired/turbo"
|
|
3
|
-
|
|
4
|
-
consumer.subscriptions.create("RefreshChannel", {
|
|
5
|
-
connected() {
|
|
6
|
-
console.log("Connected. Waiting for snapshots updates...")
|
|
7
|
-
|
|
8
|
-
const connected_event = new CustomEvent("refresh-connected");
|
|
9
|
-
window.dispatchEvent(connected_event);
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
disconnected() {
|
|
13
|
-
console.log("Unsubscribed from snapshots updates. Trying to reconnect...")
|
|
14
|
-
|
|
15
|
-
const disconnected_event = new CustomEvent("refresh-disconnected");
|
|
16
|
-
window.dispatchEvent(disconnected_event);
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
received(_data) {
|
|
20
|
-
console.log("Snapshots updated.")
|
|
21
|
-
Turbo.visit(location, { action: "replace" })
|
|
22
|
-
|
|
23
|
-
if (document.querySelector("iframe#raw")) {
|
|
24
|
-
document.querySelector("iframe#raw").contentWindow.location.reload()
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
});
|