cyperful 0.1.3 → 0.1.4
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/cyperful/driver.rb +49 -21
- data/lib/cyperful/framework_injections.rb +15 -0
- data/lib/cyperful/test_parser.rb +78 -24
- data/lib/cyperful.rb +1 -16
- data/public/assets/{index-fb0f529b.css → index-25bd736c.css} +1 -1
- data/public/assets/{index-722e2568.js → index-c11ede11.js} +5 -5
- data/public/index.html +2 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 61dce63e0bff592ded11835cf202b241d717acaaf99331db8f447d3d7dc2aba9
|
|
4
|
+
data.tar.gz: a2ec8532880c523e33ce15d377a1fc28b6832354306c784fa3dea7a38200302b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0cd35d0e7f3ee879367682ef2780fd6904d879a2d0a6c95a936191c3054b9cbfe60f682d4b911804565050b4da8a75744f5f8f58ef41d8fe82c02e1de84278de
|
|
7
|
+
data.tar.gz: 5755db30366159e25279fde63c4e2ccc2f94c934dee4538b237b8e3aee05774ff0182ee8e006d21aaf525ed0b8bdb75675a2fdbeb2879d0ab8fe66d92ecbd4ff
|
data/lib/cyperful/driver.rb
CHANGED
|
@@ -74,6 +74,7 @@ class Cyperful::Driver
|
|
|
74
74
|
)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
# NOTE: there can be multiple steps per line, this takes the last instance
|
|
77
78
|
@step_per_line = @steps.index_by { |step| step[:line] }
|
|
78
79
|
|
|
79
80
|
@current_step = nil
|
|
@@ -110,21 +111,22 @@ class Cyperful::Driver
|
|
|
110
111
|
@tracepoint.enable
|
|
111
112
|
end
|
|
112
113
|
|
|
113
|
-
# Every time a file changes the `test/` directory,
|
|
114
|
-
#
|
|
115
|
-
# TODO: add an option to auto-run
|
|
114
|
+
# Every time a file changes the `test/` directory, reset this test
|
|
115
|
+
# TODO: add an option to auto-run on reload
|
|
116
116
|
def setup_file_listener
|
|
117
|
-
|
|
117
|
+
# TODO: we need to somehow reload the source files
|
|
118
118
|
|
|
119
|
-
@
|
|
120
|
-
@file_listener =
|
|
121
|
-
Listen.to(test_dir) do |_modified, _added, _removed|
|
|
122
|
-
puts "Test files changed, resetting test..."
|
|
119
|
+
# test_dir = @source_filepath.match(%r{^/.+/(?:test|spec)\b})[0]
|
|
123
120
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
# @file_listener&.stop
|
|
122
|
+
# @file_listener =
|
|
123
|
+
# Listen.to(test_dir) do |_modified, _added, _removed|
|
|
124
|
+
# puts "Test files changed, resetting test..."
|
|
125
|
+
|
|
126
|
+
# @pause_at_step = true
|
|
127
|
+
# @step_pausing_queue.enq(:reset)
|
|
128
|
+
# end
|
|
129
|
+
# @file_listener.start
|
|
128
130
|
end
|
|
129
131
|
|
|
130
132
|
def print_steps
|
|
@@ -149,7 +151,7 @@ class Cyperful::Driver
|
|
|
149
151
|
"running"
|
|
150
152
|
end
|
|
151
153
|
|
|
152
|
-
def test_duration_ms
|
|
154
|
+
private def test_duration_ms
|
|
153
155
|
start_at = @steps.first&.[](:start_at)
|
|
154
156
|
return nil unless start_at
|
|
155
157
|
last_ended_step_i = @steps.rindex { |step| step[:end_at] }
|
|
@@ -234,12 +236,27 @@ class Cyperful::Driver
|
|
|
234
236
|
def drive_iframe
|
|
235
237
|
puts "Driving iframe..."
|
|
236
238
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
# make sure a `within` block doesn't affect these commands
|
|
240
|
+
without_finder_scopes do
|
|
241
|
+
@session.switch_to_frame(
|
|
242
|
+
# `find` waits for the iframe to load
|
|
243
|
+
@session.find(:css, "iframe#scenario-frame"),
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
|
|
240
247
|
@driving_iframe = true
|
|
241
248
|
end
|
|
242
249
|
|
|
250
|
+
private def without_finder_scopes(&block)
|
|
251
|
+
scopes = @session.send(:scopes)
|
|
252
|
+
before_scopes = scopes.dup
|
|
253
|
+
scopes.reject! { |el| el.is_a?(Capybara::Node::Element) }
|
|
254
|
+
block.call
|
|
255
|
+
ensure
|
|
256
|
+
scopes.clear
|
|
257
|
+
scopes.push(*before_scopes)
|
|
258
|
+
end
|
|
259
|
+
|
|
243
260
|
# forked from: https://github.com/teamcapybara/capybara/blob/master/lib/capybara/session.rb#L264
|
|
244
261
|
private def make_absolute_url(visit_uri)
|
|
245
262
|
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
|
|
@@ -268,8 +285,17 @@ class Cyperful::Driver
|
|
|
268
285
|
|
|
269
286
|
WATCHER_JS = File.read(File.join(Cyperful::ROOT_DIR, "watcher.js"))
|
|
270
287
|
|
|
288
|
+
private def skip_multi_sessions
|
|
289
|
+
unless Capybara.current_session == @session
|
|
290
|
+
warn "Skipped Cyperful setup in non-default session: #{Capybara.session_name}"
|
|
291
|
+
return true
|
|
292
|
+
end
|
|
293
|
+
false
|
|
294
|
+
end
|
|
295
|
+
|
|
271
296
|
def internal_visit(url)
|
|
272
297
|
return false unless @driving_iframe
|
|
298
|
+
return false if skip_multi_sessions
|
|
273
299
|
|
|
274
300
|
abs_url, display_url = make_absolute_url(url)
|
|
275
301
|
|
|
@@ -291,6 +317,7 @@ class Cyperful::Driver
|
|
|
291
317
|
|
|
292
318
|
def internal_current_url
|
|
293
319
|
return nil unless @driving_iframe
|
|
320
|
+
return nil if skip_multi_sessions
|
|
294
321
|
|
|
295
322
|
@session.evaluate_script("window.location.href")
|
|
296
323
|
end
|
|
@@ -335,9 +362,6 @@ class Cyperful::Driver
|
|
|
335
362
|
@tracepoint&.disable
|
|
336
363
|
@tracepoint = nil
|
|
337
364
|
|
|
338
|
-
@file_listener&.stop
|
|
339
|
-
@file_listener = nil
|
|
340
|
-
|
|
341
365
|
if error&.is_a?(Cyperful::ResetCommand)
|
|
342
366
|
puts "\nPlease ignore the error, we're just resetting the test ;)"
|
|
343
367
|
|
|
@@ -355,9 +379,10 @@ class Cyperful::Driver
|
|
|
355
379
|
backtrace = []
|
|
356
380
|
error.backtrace.each do |s|
|
|
357
381
|
i ||= 0 if s.include?(@source_filepath)
|
|
358
|
-
|
|
359
|
-
|
|
382
|
+
next unless i
|
|
383
|
+
i += 1
|
|
360
384
|
backtrace << s
|
|
385
|
+
break if i >= 6
|
|
361
386
|
end
|
|
362
387
|
|
|
363
388
|
warn "\n\nTest failed with error:\n#{error.message}\n#{backtrace.join("\n")}"
|
|
@@ -372,5 +397,8 @@ class Cyperful::Driver
|
|
|
372
397
|
puts "Cyperful teardown complete. Waiting for command..."
|
|
373
398
|
command = @step_pausing_queue.deq
|
|
374
399
|
queue_reset if command == :reset
|
|
400
|
+
ensure
|
|
401
|
+
@file_listener&.stop
|
|
402
|
+
@file_listener = nil
|
|
375
403
|
end
|
|
376
404
|
end
|
|
@@ -17,9 +17,24 @@ module PrependCapybaraSession
|
|
|
17
17
|
return if Cyperful.current&.internal_visit(current_url)
|
|
18
18
|
super
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
def go_back
|
|
22
|
+
super
|
|
23
|
+
Cyperful.current&.drive_iframe
|
|
24
|
+
end
|
|
20
25
|
end
|
|
21
26
|
Capybara::Session.prepend(PrependCapybaraSession)
|
|
22
27
|
|
|
28
|
+
module PrependCapybaraWindow
|
|
29
|
+
# this solves a bug in Capybara where it doesn't
|
|
30
|
+
# return to driving the iframe after a call to `Window#close`
|
|
31
|
+
def close
|
|
32
|
+
super
|
|
33
|
+
Cyperful.current&.drive_iframe
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
Capybara::Window.prepend(PrependCapybaraWindow)
|
|
37
|
+
|
|
23
38
|
# The Minitest test helper.
|
|
24
39
|
# TODO: support other test frameworks like RSpec
|
|
25
40
|
module Cyperful::SystemTestHelper
|
data/lib/cyperful/test_parser.rb
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
require "parser/current"
|
|
2
|
+
require "capybara/minitest"
|
|
2
3
|
|
|
3
4
|
class Cyperful::TestParser
|
|
5
|
+
# see docs for methods: https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Session
|
|
6
|
+
@step_at_methods =
|
|
7
|
+
Capybara::Session::DSL_METHODS.to_set +
|
|
8
|
+
Capybara::Minitest::Assertions.instance_methods(false) -
|
|
9
|
+
# exclude methods that don't have side-effects i.e. don't modify the page:
|
|
10
|
+
%i[
|
|
11
|
+
body
|
|
12
|
+
html
|
|
13
|
+
source
|
|
14
|
+
current_url
|
|
15
|
+
current_host
|
|
16
|
+
current_path
|
|
17
|
+
current_scope
|
|
18
|
+
status_code
|
|
19
|
+
response_headers
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
def self.step_at_methods
|
|
23
|
+
@step_at_methods
|
|
24
|
+
end
|
|
25
|
+
def self.add_step_at_methods(*mods_or_methods)
|
|
26
|
+
mods_or_methods.each do |mod_or_method|
|
|
27
|
+
case mod_or_method
|
|
28
|
+
when Module
|
|
29
|
+
@step_at_methods +=
|
|
30
|
+
mod_or_method.methods(false) + mod_or_method.instance_methods(false)
|
|
31
|
+
when String, Symbol
|
|
32
|
+
@step_at_methods << mod_or_method.to_sym
|
|
33
|
+
else
|
|
34
|
+
raise "Expected Module or Array of strings/symbols, got #{mod_or_method.class}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
4
39
|
def initialize(test_class)
|
|
5
40
|
@test_class = test_class
|
|
6
41
|
@source_filepath = Object.const_source_location(test_class.name).first
|
|
@@ -24,14 +59,14 @@ class Cyperful::TestParser
|
|
|
24
59
|
end
|
|
25
60
|
|
|
26
61
|
(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
62
|
+
# the children of the `class` node are either:
|
|
63
|
+
# - a `begin` node if there's more than 1 child node
|
|
64
|
+
# - or the one 0 or 1 child node
|
|
65
|
+
system_test_class
|
|
66
|
+
.children
|
|
67
|
+
.find { |node| node.type == :begin }
|
|
68
|
+
&.children || [system_test_class.children[2]].compact
|
|
69
|
+
)
|
|
35
70
|
.map do |node|
|
|
36
71
|
# e.g. `test "my test" do ... end`
|
|
37
72
|
if node.type == :block && node.children[0].type == :send &&
|
|
@@ -50,29 +85,48 @@ class Cyperful::TestParser
|
|
|
50
85
|
end
|
|
51
86
|
.compact
|
|
52
87
|
.to_h do |test_method, block_node|
|
|
53
|
-
[
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.uniq { |step| step[:line] },
|
|
58
|
-
]
|
|
88
|
+
out = []
|
|
89
|
+
block_node.children.each { |child| find_test_steps(child, out) }
|
|
90
|
+
|
|
91
|
+
[test_method, out]
|
|
59
92
|
end
|
|
60
93
|
end
|
|
61
94
|
|
|
62
|
-
private def find_test_steps(ast, out = [])
|
|
95
|
+
private def find_test_steps(ast, out = [], depth = 0)
|
|
63
96
|
return out unless ast&.is_a?(Parser::AST::Node)
|
|
64
97
|
|
|
65
|
-
if ast.type == :send
|
|
66
|
-
out
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
as_string: ast.loc.expression.source,
|
|
71
|
-
}
|
|
72
|
-
end
|
|
98
|
+
if ast.type == :send
|
|
99
|
+
add_node(ast, out, depth)
|
|
100
|
+
ast.children.each { |child| find_test_steps(child, out, depth) }
|
|
101
|
+
elsif ast.type == :block
|
|
102
|
+
method, _args, child = ast.children
|
|
73
103
|
|
|
74
|
-
|
|
104
|
+
children = child.type == :begin ? child.children : [child]
|
|
105
|
+
|
|
106
|
+
if method.type == :send
|
|
107
|
+
depth += 1 if add_node(method, out, depth)
|
|
108
|
+
method.children.each { |child| find_test_steps(child, out, depth) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
children.each { |child| find_test_steps(child, out, depth) }
|
|
112
|
+
end
|
|
75
113
|
|
|
76
114
|
out
|
|
77
115
|
end
|
|
116
|
+
|
|
117
|
+
private def add_node(node, out, depth)
|
|
118
|
+
unless Cyperful::TestParser.step_at_methods.include?(node.children[1])
|
|
119
|
+
return false
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
out << {
|
|
123
|
+
method: node.children[1],
|
|
124
|
+
line: node.loc.line,
|
|
125
|
+
column: node.loc.column,
|
|
126
|
+
as_string: node.loc.expression.source,
|
|
127
|
+
block_depth: depth,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
true
|
|
131
|
+
end
|
|
78
132
|
end
|
data/lib/cyperful.rb
CHANGED
|
@@ -29,23 +29,8 @@ module Cyperful
|
|
|
29
29
|
@current&.teardown(error)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
# more potential methods: https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Session
|
|
33
|
-
@step_at_methods = [*Capybara::Session::NODE_METHODS, :visit, :refresh]
|
|
34
|
-
def self.step_at_methods
|
|
35
|
-
@step_at_methods
|
|
36
|
-
end
|
|
37
32
|
def self.add_step_at_methods(*mods_or_methods)
|
|
38
|
-
mods_or_methods
|
|
39
|
-
case mod_or_method
|
|
40
|
-
when Module
|
|
41
|
-
@step_at_methods +=
|
|
42
|
-
mod_or_method.methods(false) + mod_or_method.instance_methods(false)
|
|
43
|
-
when String, Symbol
|
|
44
|
-
@step_at_methods << mod_or_method.to_sym
|
|
45
|
-
else
|
|
46
|
-
raise "Expected Module or Array of strings/symbols, got #{mod_or_method.class}"
|
|
47
|
-
end
|
|
48
|
-
end
|
|
33
|
+
Cyperful::TestParser.add_step_at_methods(*mods_or_methods)
|
|
49
34
|
end
|
|
50
35
|
end
|
|
51
36
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.pointer-events-none{pointer-events:none}.static{position:static}.absolute{position:absolute}.relative{position:relative}.left-0{left:0px}.right-0{right:0px}.right-4{right:1rem}.top-0{top:0px}.top-1{top:.25rem}.m-4{margin:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1{margin-left:.25rem}.mr-2{margin-right:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:4rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.basis-96{flex-basis:24rem}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-md{border-radius:.375rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.bg-\[\#121b2e\]{--tw-bg-opacity: 1;background-color:rgb(18 27 46 / var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.bg-orange-400{--tw-bg-opacity: 1;background-color:rgb(251 146 60 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-4{padding:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.pt-0{padding-top:0}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[1\.25em\]{font-size:1.25em}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.Logo g{transform-origin:50% 50%;animation:rotate 5s linear infinite;animation-play-state:paused}.Logo.Logo--animating g{animation-play-state:running}.Logo g:nth-child(1){animation-duration:2s}.Logo g:nth-child(2){animation-duration:2.5s}.Logo g:nth-child(3){animation-duration:3s}.Logo g:nth-child(4){animation-duration:3.5s}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity))}.hover\:bg-orange-500:hover{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.hover\:bg-yellow-600:hover{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity))}.hover\:underline:hover{text-decoration-line:underline}.group:hover .group-hover\:block{display:block}
|
|
1
|
+
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.pointer-events-none{pointer-events:none}.static{position:static}.absolute{position:absolute}.relative{position:relative}.left-0{left:0px}.right-0{right:0px}.right-4{right:1rem}.top-0{top:0px}.top-1{top:.25rem}.m-4{margin:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-1{margin-left:.25rem}.mr-2{margin-right:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:4rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.basis-96{flex-basis:24rem}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-md{border-radius:.375rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.bg-\[\#121b2e\]{--tw-bg-opacity: 1;background-color:rgb(18 27 46 / var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.bg-orange-400{--tw-bg-opacity: 1;background-color:rgb(251 146 60 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-4{padding:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.pt-0{padding-top:0}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[1\.25em\]{font-size:1.25em}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@keyframes rotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.Logo g{transform-origin:50% 50%;animation:rotate 5s linear infinite;animation-play-state:paused}.Logo.Logo--animating g{animation-play-state:running}.Logo g:nth-child(1){animation-duration:1s}.Logo g:nth-child(2){animation-duration:1.5s}.Logo g:nth-child(3){animation-duration:3s}.Logo g:nth-child(4){animation-duration:4s}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity))}.hover\:bg-orange-500:hover{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.hover\:bg-yellow-600:hover{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity))}.hover\:underline:hover{text-decoration-line:underline}.group:hover .group-hover\:block{display:block}
|