snapshot_ui 0.4.1 → 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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +145 -4
  4. data/lib/minitest/snapshot_ui_plugin.rb +9 -0
  5. data/lib/snapshot_ui/colorize.rb +15 -4
  6. data/lib/snapshot_ui/configuration.rb +12 -3
  7. data/lib/snapshot_ui/snapshot/context.rb +4 -1
  8. data/lib/snapshot_ui/snapshot/storage.rb +54 -3
  9. data/lib/snapshot_ui/snapshot.rb +54 -13
  10. data/lib/snapshot_ui/test/minitest_helpers.rb +21 -6
  11. data/lib/snapshot_ui/version.rb +1 -1
  12. data/lib/snapshot_ui/web/application.rb +100 -39
  13. data/lib/snapshot_ui/web/assets/javascripts/application.js +4 -2
  14. data/lib/snapshot_ui/web/assets/javascripts/controllers/refresh_controller.js +76 -12
  15. data/lib/snapshot_ui/web/assets/javascripts/controllers/source_location_controller.js +14 -6
  16. data/lib/snapshot_ui/web/assets/javascripts/vendor/stimulus.js +2564 -0
  17. data/lib/snapshot_ui/web/assets/javascripts/vendor/turbo.js +7257 -0
  18. data/lib/snapshot_ui/web/assets/stylesheets/application.css +120 -0
  19. data/lib/snapshot_ui/web/views/layout.html.erb +4 -9
  20. data/lib/snapshot_ui/web/views/snapshots/index.html.erb +5 -11
  21. data/lib/snapshot_ui/web.rb +27 -10
  22. data/lib/snapshot_ui.rb +4 -2
  23. data/snapshot_ui.gemspec +8 -14
  24. metadata +17 -76
  25. data/bin/snapshot_ui +0 -7
  26. data/lib/snapshot_ui/cli/watcher.ru +0 -91
  27. data/lib/snapshot_ui/cli.rb +0 -29
  28. data/lib/snapshot_ui/web/assets/javascripts/channels/consumer.js +0 -3
  29. data/lib/snapshot_ui/web/assets/javascripts/channels/index.js +0 -1
  30. data/lib/snapshot_ui/web/assets/javascripts/channels/refresh_channel.js +0 -27
@@ -183,3 +183,123 @@ body#not_found p {
183
183
  height: 2px;
184
184
  background-color: lightskyblue;
185
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/stimulus": "https://cdn.jsdelivr.net/npm/@hotwired/stimulus@3.2.2/+esm",
16
- "@hotwired/turbo": "https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/+esm",
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>#take_screenshot</code>. Run the tests with <code>TAKE_SNAPSHOTS=1</code>, and the snapshot will appear here.
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"
@@ -7,21 +7,38 @@ require_relative "web/application"
7
7
 
8
8
  module SnapshotUI
9
9
  class Web
10
- def self.call(env)
11
- new.call(env)
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 call(env)
15
- app =
16
- Rack::Builder.app do
17
- use Rack::Static,
18
- root: "#{File.dirname(__FILE__)}/web/assets",
19
- urls: %w[/stylesheets /javascripts]
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
- run Application.new
22
- end
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.0.0"
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
- spec.files = (`git ls-files | grep -E '^(lib)'`.split("\n") + %w[snapshot_ui.gemspec README.md CHANGELOG.md LICENSE.txt bin/snapshot_ui])
23
- spec.executables = ["snapshot_ui"]
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
- # Uncomment to register a new dependency of your gem
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.1
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: 2024-07-22 00:00:00.000000000 Z
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: '3.0'
20
- type: :runtime
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: '0.26'
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: '0.26'
55
- - !ruby/object:Gem::Dependency
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: '1.3'
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.0.0
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.4.19
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,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
4
-
5
- require "snapshot_ui/cli"
6
-
7
- SnapshotUI::CLI.start(ARGV)
@@ -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
@@ -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,3 +0,0 @@
1
- import { createConsumer } from "@rails/actioncable"
2
-
3
- export default createConsumer()
@@ -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
- });