snapshot_ui 0.3.0 → 0.4.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/lib/snapshot_ui/cli/watcher.ru +11 -9
- data/lib/snapshot_ui/cli.rb +11 -8
- data/lib/snapshot_ui/configuration.rb +3 -2
- data/lib/snapshot_ui/version.rb +1 -1
- data/lib/snapshot_ui/web/application.rb +27 -0
- data/lib/snapshot_ui/web/assets/javascripts/application.js +2 -0
- data/lib/snapshot_ui/web/assets/javascripts/controllers/source_location_controller.js +17 -0
- data/lib/snapshot_ui/web/assets/stylesheets/application.css +78 -2
- data/lib/snapshot_ui/web/views/layout.html.erb +2 -1
- data/lib/snapshot_ui/web/views/snapshots/index.html.erb +11 -5
- data/lib/snapshot_ui.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d083a6167cbd33d814565a34c6da82c32f920bf1fef6faa8171e1cf4bae1f86
|
4
|
+
data.tar.gz: 15f7d8346e5f192e4b8901760b64a1d91362507bb7adf91cbaed37e30a257613
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07c26236a62e772945eb5165e2552d2a278493acb7b65d4429f445e98b2401b5ee39acfe842b2a5c9787220b5444ac3f0a1ecab1f7e8cb0a7f87e2f01726b2b2
|
7
|
+
data.tar.gz: 5fa30fbda712efff8c596acca55ec3811fee661a6684fa7d34aee7a30b2032255e14930c8288f90e5e5fe15d82424cef66848a4860d560241d9631e57837b94a
|
@@ -6,18 +6,19 @@ require "async/websocket/client"
|
|
6
6
|
require "async/websocket/adapters/rack"
|
7
7
|
require "listen"
|
8
8
|
require "set"
|
9
|
+
require "uri"
|
9
10
|
|
10
|
-
|
11
|
-
WEBSOCKET_ENDPOINT = "http://localhost:7070/cable"
|
11
|
+
load ENV.fetch("SNAPSHOT_UI_INITIALIZER_FILE")
|
12
12
|
|
13
13
|
REFRESH_MESSAGE = {identifier: "{\"channel\":\"RefreshChannel\"}", message: "refresh"}.to_json
|
14
14
|
CONFIRM_SUBSCRIPTION_MESSAGE = {identifier: "{\"channel\":\"RefreshChannel\"}", type: "confirm_subscription"}.to_json
|
15
|
-
PING_MESSAGE = {type: "ping", message: Time.now.to_i.to_s}.to_json
|
16
15
|
|
17
16
|
@connections = Set.new
|
18
17
|
|
18
|
+
live_websocket_uri = URI.parse(SnapshotUI.configuration.live_websocket_url)
|
19
|
+
|
19
20
|
run lambda { |env|
|
20
|
-
if env["REQUEST_PATH"] ==
|
21
|
+
if env["REQUEST_PATH"] == live_websocket_uri.path
|
21
22
|
Async::WebSocket::Adapters::Rack.open(env, protocols: ["actioncable-v1-json"]) do |connection|
|
22
23
|
@connections << connection
|
23
24
|
|
@@ -27,7 +28,7 @@ run lambda { |env|
|
|
27
28
|
|
28
29
|
Async do |ping_task|
|
29
30
|
loop do
|
30
|
-
connection.write(
|
31
|
+
connection.write({type: "ping", message: Time.now.to_i.to_s}.to_json)
|
31
32
|
connection.flush
|
32
33
|
sleep(2)
|
33
34
|
end
|
@@ -50,7 +51,7 @@ run lambda { |env|
|
|
50
51
|
}
|
51
52
|
|
52
53
|
Async do |client_task|
|
53
|
-
endpoint = Async::HTTP::Endpoint.parse(
|
54
|
+
endpoint = Async::HTTP::Endpoint.parse(SnapshotUI.configuration.live_websocket_url)
|
54
55
|
|
55
56
|
Async::WebSocket::Client.connect(endpoint) do |connection|
|
56
57
|
Async do |listener_task|
|
@@ -68,15 +69,16 @@ Async do |client_task|
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def detect_snapshots_update(task)
|
71
|
-
Pathname.new(
|
72
|
-
listener = Listen.to(
|
72
|
+
Pathname.new(SnapshotUI.configuration.storage_directory).mkpath
|
73
|
+
listener = Listen.to(SnapshotUI.configuration.storage_directory) { |_modified, _added, _removed| broadcast_update }
|
73
74
|
|
74
75
|
task.async do
|
75
76
|
listener.start
|
76
77
|
task.sleep
|
77
78
|
end
|
78
79
|
|
79
|
-
Console.info("Watching for snapshots updates in #{
|
80
|
+
Console.info("Watching for snapshots updates in #{SnapshotUI.configuration.storage_directory}")
|
81
|
+
Console.info("Review snapshots on #{SnapshotUI.configuration.web_url}")
|
80
82
|
end
|
81
83
|
|
82
84
|
def broadcast_update
|
data/lib/snapshot_ui/cli.rb
CHANGED
@@ -1,22 +1,25 @@
|
|
1
1
|
require "bundler/setup"
|
2
2
|
require "thor"
|
3
3
|
require "pathname"
|
4
|
+
require "uri"
|
4
5
|
|
5
6
|
module SnapshotUI
|
6
7
|
class CLI < Thor
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
unless File.exist?(snapshot_directory)
|
12
|
-
puts "The provided directory `#{snapshot_directory}` doesn't exist. Please double check the path."
|
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."
|
13
12
|
exit 1
|
14
13
|
end
|
15
14
|
|
16
|
-
|
15
|
+
load initializer_file
|
16
|
+
|
17
|
+
websocket_host_uri = URI.parse(SnapshotUI.configuration.live_websocket_url)
|
18
|
+
websocket_host_uri.path = ""
|
19
|
+
|
17
20
|
config_path = Pathname.new(__dir__).join("cli", "watcher.ru").cleanpath.to_s
|
18
21
|
|
19
|
-
exec "
|
22
|
+
exec "SNAPSHOT_UI_INITIALIZER_FILE=#{initializer_file} bundle exec falcon serve --bind #{websocket_host_uri} --count 1 --config #{config_path}"
|
20
23
|
end
|
21
24
|
|
22
25
|
def self.exit_on_failure?
|
@@ -5,12 +5,13 @@ require_relative "colorize"
|
|
5
5
|
module SnapshotUI
|
6
6
|
class Configuration
|
7
7
|
attr_writer :storage_directory, :project_root_directory
|
8
|
-
attr_accessor :web_url
|
8
|
+
attr_accessor :web_url, :live_websocket_url
|
9
9
|
|
10
|
-
def initialize(project_root_directory:, storage_directory:, web_url:)
|
10
|
+
def initialize(project_root_directory:, storage_directory:, web_url:, live_websocket_url:)
|
11
11
|
@project_root_directory = project_root_directory
|
12
12
|
@storage_directory = storage_directory
|
13
13
|
@web_url = web_url
|
14
|
+
@live_websocket_url = live_websocket_url
|
14
15
|
end
|
15
16
|
|
16
17
|
def storage_directory
|
data/lib/snapshot_ui/version.rb
CHANGED
@@ -119,6 +119,33 @@ module SnapshotUI
|
|
119
119
|
def refresh_controller
|
120
120
|
'data-controller="refresh" data-action="refresh-connected@window->refresh#connected refresh-disconnected@window->refresh#disconnected turbo:before-render@window->refresh#display_status"'
|
121
121
|
end
|
122
|
+
|
123
|
+
def snapshot_title(snapshot)
|
124
|
+
title = snapshot.context.name.sub("test_", "").gsub(/^\d{4}\s*/, "").tr("_", " ")
|
125
|
+
suffix =
|
126
|
+
if snapshot.context.take_snapshot_index > 0
|
127
|
+
" (##{snapshot.context.take_snapshot_index + 1} in the same test)"
|
128
|
+
end
|
129
|
+
|
130
|
+
"#{title}#{suffix}"
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_group_title(test_group)
|
134
|
+
parts = test_group.split("::")
|
135
|
+
last_part = "<span class='last'>#{parts.last}</span>"
|
136
|
+
all = parts[0..-2] << last_part
|
137
|
+
|
138
|
+
all.join(" <span class='divider'>/</span> ")
|
139
|
+
end
|
140
|
+
|
141
|
+
def copy_icon_svg
|
142
|
+
<<~HTML
|
143
|
+
<svg class="copy icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
144
|
+
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
|
145
|
+
<path fill="currentColor" d="M384 336l-192 0c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l140.1 0L400 115.9 400 320c0 8.8-7.2 16-16 16zM192 384l192 0c35.3 0 64-28.7 64-64l0-204.1c0-12.7-5.1-24.9-14.1-33.9L366.1 14.1c-9-9-21.2-14.1-33.9-14.1L192 0c-35.3 0-64 28.7-64 64l0 256c0 35.3 28.7 64 64 64zM64 128c-35.3 0-64 28.7-64 64L0 448c0 35.3 28.7 64 64 64l192 0c35.3 0 64-28.7 64-64l0-32-48 0 0 32c0 8.8-7.2 16-16 16L64 464c-8.8 0-16-7.2-16-16l0-256c0-8.8 7.2-16 16-16l32 0 0-48-32 0z"/>
|
146
|
+
</svg>
|
147
|
+
HTML
|
148
|
+
end
|
122
149
|
end
|
123
150
|
end
|
124
151
|
end
|
@@ -3,6 +3,8 @@ import "@hotwired/turbo"
|
|
3
3
|
import "channels"
|
4
4
|
|
5
5
|
import RefreshController from "controllers/refresh_controller"
|
6
|
+
import SourceLocationController from "controllers/source_location_controller"
|
6
7
|
|
7
8
|
window.Stimulus = Application.start()
|
8
9
|
Stimulus.register("refresh", RefreshController)
|
10
|
+
Stimulus.register("source-location", SourceLocationController)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
copy(event) {
|
5
|
+
event.preventDefault()
|
6
|
+
|
7
|
+
let currentTarget = event.currentTarget
|
8
|
+
|
9
|
+
navigator.clipboard.writeText(currentTarget.dataset.sourceLocation)
|
10
|
+
|
11
|
+
currentTarget.classList.toggle('animate');
|
12
|
+
setTimeout(()=> {
|
13
|
+
currentTarget.classList.toggle('animate')
|
14
|
+
},300)
|
15
|
+
|
16
|
+
}
|
17
|
+
}
|
@@ -28,6 +28,15 @@ body#snapshots_index h1 {
|
|
28
28
|
body#snapshots_index h2 {
|
29
29
|
margin: 30px 0 0;
|
30
30
|
font-size: 24px;
|
31
|
+
color: #aaa;
|
32
|
+
}
|
33
|
+
|
34
|
+
body#snapshots_index h2 .last {
|
35
|
+
color: #666666;
|
36
|
+
}
|
37
|
+
|
38
|
+
body#snapshots_index h2 .divider {
|
39
|
+
color: #ccc;
|
31
40
|
}
|
32
41
|
|
33
42
|
body#snapshots_index p {
|
@@ -44,8 +53,14 @@ body#snapshots_index h1, h2, h3, h4, h5, h6 {
|
|
44
53
|
color: #666666;
|
45
54
|
}
|
46
55
|
|
47
|
-
body#snapshots_index a {
|
48
|
-
|
56
|
+
body#snapshots_index a {
|
57
|
+
color: #000;
|
58
|
+
text-decoration: none;
|
59
|
+
}
|
60
|
+
|
61
|
+
body#snapshots_index a:hover {
|
62
|
+
text-decoration: underline;
|
63
|
+
}
|
49
64
|
|
50
65
|
body#snapshots_index code {
|
51
66
|
background: rgba(175, 184, 193, 0.2);
|
@@ -101,6 +116,67 @@ body#not_found p {
|
|
101
116
|
color: #666666;
|
102
117
|
}
|
103
118
|
|
119
|
+
.copy_source_location {
|
120
|
+
all: unset;
|
121
|
+
cursor: pointer;
|
122
|
+
position: relative;
|
123
|
+
}
|
124
|
+
|
125
|
+
.copy_source_location .icon {
|
126
|
+
width: 0.9em;
|
127
|
+
height: 0.9em;
|
128
|
+
color: #aaa;
|
129
|
+
}
|
130
|
+
|
131
|
+
.copy_source_location:hover .icon {
|
132
|
+
color: #000;
|
133
|
+
}
|
134
|
+
|
135
|
+
.copy_source_location.animate .icon {
|
136
|
+
animation: enlarge-and-back-to-normal 0.3s;
|
137
|
+
}
|
138
|
+
|
139
|
+
@keyframes enlarge-and-back-to-normal {
|
140
|
+
0%,
|
141
|
+
100% {
|
142
|
+
transform: scale(1, 1);
|
143
|
+
}
|
144
|
+
50% {
|
145
|
+
transform: scale(1.3, 1.3);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
.copy_source_location:before {
|
150
|
+
content: attr(data-source-location);
|
151
|
+
position:absolute;
|
152
|
+
font-size: 90%;
|
153
|
+
top:-95%;
|
154
|
+
transform:translateX(-50%);
|
155
|
+
left:100%;
|
156
|
+
margin-top:-15px;
|
157
|
+
padding:3px 15px 1px;
|
158
|
+
border-radius:10px;
|
159
|
+
background:#eee;
|
160
|
+
color: #666;
|
161
|
+
text-align:center;
|
162
|
+
display:none;
|
163
|
+
}
|
164
|
+
|
165
|
+
.copy_source_location:after {
|
166
|
+
content: "";
|
167
|
+
position:absolute;
|
168
|
+
top:-11px;
|
169
|
+
transform:translateX(-50%);
|
170
|
+
margin-left:10px;
|
171
|
+
border:10px solid #eee;
|
172
|
+
border-color: #eee transparent transparent transparent;
|
173
|
+
display:none;
|
174
|
+
}
|
175
|
+
|
176
|
+
.copy_source_location:hover:before, .copy_source_location:hover:after {
|
177
|
+
display:block;
|
178
|
+
}
|
179
|
+
|
104
180
|
/* Hotwired Turbo related */
|
105
181
|
.turbo-progress-bar {
|
106
182
|
height: 2px;
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<meta name="turbo-refresh-method" content="morph">
|
8
8
|
<meta name="turbo-refresh-scroll" content="preserve">
|
9
9
|
<link rel="stylesheet" href="<%= stylesheet_path("application.css") %>">
|
10
|
-
<meta name="action-cable-url" content="
|
10
|
+
<meta name="action-cable-url" content="<%= SnapshotUI.configuration.live_websocket_url %>" />
|
11
11
|
<script type="importmap" data-turbo-track="reload">
|
12
12
|
{
|
13
13
|
"imports": {
|
@@ -16,6 +16,7 @@
|
|
16
16
|
"@hotwired/turbo": "https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/+esm",
|
17
17
|
"@rails/actioncable": "https://cdn.jsdelivr.net/npm/@rails/actioncable@7.1.3-4/+esm",
|
18
18
|
"controllers/refresh_controller": "<%= javascript_path("controllers/refresh_controller.js") %>",
|
19
|
+
"controllers/source_location_controller": "<%= javascript_path("controllers/source_location_controller.js") %>",
|
19
20
|
"channels/consumer": "<%= javascript_path("channels/consumer.js") %>",
|
20
21
|
"channels": "<%= javascript_path("channels/index.js") %>",
|
21
22
|
"channels/refresh_channel": "<%= javascript_path("channels/refresh_channel.js") %>"
|
@@ -44,15 +44,21 @@ bundle exec snapshot_ui watch SNAPSHOT_DIRECTORY</pre>
|
|
44
44
|
<% end %>
|
45
45
|
|
46
46
|
<% @grouped_by_test_class.each do |test_group, snapshots| %>
|
47
|
-
|
47
|
+
|
48
|
+
<h2><%= test_group_title(test_group) %></h2>
|
48
49
|
|
49
50
|
<ul>
|
50
51
|
<% snapshots.each do |snapshot| %>
|
51
52
|
<li>
|
52
|
-
<a href="<%= snapshot_path(snapshot.slug) %>">
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
<a href="<%= snapshot_path(snapshot.slug) %>"><%= snapshot_title(snapshot) %></a>
|
54
|
+
<button
|
55
|
+
class="copy_source_location"
|
56
|
+
data-controller="source-location"
|
57
|
+
data-action="click->source-location#copy"
|
58
|
+
data-source-location="<%= snapshot.context.source_location.join(":") %>"
|
59
|
+
>
|
60
|
+
<%= copy_icon_svg %>
|
61
|
+
</button>
|
56
62
|
</li>
|
57
63
|
<% end %>
|
58
64
|
</ul>
|
data/lib/snapshot_ui.rb
CHANGED
@@ -7,7 +7,8 @@ module SnapshotUI
|
|
7
7
|
DEFAULT_CONFIGURATION = {
|
8
8
|
project_root_directory: nil,
|
9
9
|
storage_directory: nil,
|
10
|
-
web_url: "http://localhost:3000/ui/snapshots"
|
10
|
+
web_url: "http://localhost:3000/ui/snapshots",
|
11
|
+
live_websocket_url: "http://localhost:49152/live"
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
def self.configure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snapshot_ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomaz Zlender
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- lib/snapshot_ui/web/assets/javascripts/channels/index.js
|
111
111
|
- lib/snapshot_ui/web/assets/javascripts/channels/refresh_channel.js
|
112
112
|
- lib/snapshot_ui/web/assets/javascripts/controllers/refresh_controller.js
|
113
|
+
- lib/snapshot_ui/web/assets/javascripts/controllers/source_location_controller.js
|
113
114
|
- lib/snapshot_ui/web/assets/stylesheets/application.css
|
114
115
|
- lib/snapshot_ui/web/views/layout.html.erb
|
115
116
|
- lib/snapshot_ui/web/views/snapshots/index.html.erb
|