cyperful 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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}
|