cyperful 0.1.10 → 0.3.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/cyperful/driver.rb +71 -33
- data/lib/cyperful/framework_helper.rb +7 -0
- data/lib/cyperful/framework_injections.rb +22 -28
- data/lib/cyperful/logger.rb +14 -0
- data/lib/cyperful/minitest.rb +30 -0
- data/lib/cyperful/rspec.rb +24 -0
- data/lib/cyperful/test_parser.rb +78 -14
- data/lib/cyperful/ui_server.rb +0 -9
- data/lib/cyperful.rb +28 -1
- data/public/assets/index-DQa6Blo_.js +51 -0
- data/public/assets/index-DqywWqA7.css +1 -0
- data/public/assets/syntax-highlighter-worker-BezFv5DT.js +12 -0
- data/public/frame-agent.js +169 -104
- data/public/index.html +2 -2
- metadata +26 -8
- data/public/assets/index-CrBQcYdq.js +0 -42
- data/public/assets/index-Uj6YFMhM.css +0 -1
- data/public/assets/syntax-highlighter-worker-Cumko8SL.js +0 -2798
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 928af50672505bb877c08f2ef62533dee16149822b591eb0d854b28235fcd207
|
4
|
+
data.tar.gz: 8b3974789ec4d679fd6413fd5a8ac09b770e038487741871249da41815faeb1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08d44698ee7d2e39c0b6cc36395d359983348e24378fa0f71a13c029d26cdeded6283828054498e524db52f39564214a33bb8f860ad857accaa4d22edd8c6a02'
|
7
|
+
data.tar.gz: a3aa8baf3c5d3a12747e316aeff713b834cc0ef8ac1a784ab24ffc2c405fb4f60908f8109b2b0a24a504e8d66a3d71e620ad6cd02c11b53c9734b04e005f8b12
|
data/lib/cyperful/driver.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
class Cyperful::Driver
|
2
2
|
attr_reader :steps, :pausing
|
3
3
|
|
4
|
+
# delegate
|
5
|
+
private def logger
|
6
|
+
Cyperful.logger
|
7
|
+
end
|
8
|
+
private def config
|
9
|
+
Cyperful.config
|
10
|
+
end
|
11
|
+
|
4
12
|
SCREENSHOTS_DIR = File.join(Cyperful::ROOT_DIR, "public/screenshots")
|
5
13
|
|
6
14
|
def initialize
|
@@ -8,6 +16,12 @@ class Cyperful::Driver
|
|
8
16
|
|
9
17
|
@session = Capybara.current_session
|
10
18
|
raise "Could not find Capybara session" unless @session
|
19
|
+
unless @session.driver.is_a?(Capybara::Selenium::Driver)
|
20
|
+
raise "Cyperful only supports Selenium, got: #{@session.driver}"
|
21
|
+
end
|
22
|
+
unless @session.driver.browser.browser == :chrome
|
23
|
+
raise "Cyperful only supports Chrome, got: #{@session.driver.browser.browser}"
|
24
|
+
end
|
11
25
|
|
12
26
|
setup_api_server
|
13
27
|
end
|
@@ -17,8 +31,11 @@ class Cyperful::Driver
|
|
17
31
|
@test_name = test_name.to_sym
|
18
32
|
|
19
33
|
@source_filepath =
|
20
|
-
|
21
|
-
|
34
|
+
if Cyperful.rspec?
|
35
|
+
test_class.metadata.fetch(:absolute_file_path)
|
36
|
+
else
|
37
|
+
Object.const_source_location(test_class.name).first
|
38
|
+
end || raise("Could not find source file for #{test_class.name}")
|
22
39
|
|
23
40
|
reset_steps
|
24
41
|
|
@@ -61,7 +78,9 @@ class Cyperful::Driver
|
|
61
78
|
@steps =
|
62
79
|
Cyperful::TestParser.new(@test_class).steps_per_test.fetch(@test_name)
|
63
80
|
|
64
|
-
|
81
|
+
raise "No steps found in #{@test_class}:#{@test_name}" if @steps.blank?
|
82
|
+
|
83
|
+
editor_scheme = config.editor_scheme
|
65
84
|
|
66
85
|
@steps.each_with_index do |step, i|
|
67
86
|
step.merge!(
|
@@ -115,19 +134,29 @@ class Cyperful::Driver
|
|
115
134
|
Object.const_get(class_name)
|
116
135
|
end
|
117
136
|
|
118
|
-
def
|
137
|
+
def enqueue_reset
|
119
138
|
at_exit do
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
139
|
+
case Cyperful.test_framework
|
140
|
+
when :rspec
|
141
|
+
RSpec.configuration.reset # private API. this resets the test reporter
|
142
|
+
RSpec.configuration.start_time = RSpec::Core::Time.now # this needs to be reset
|
143
|
+
RSpec.world.reset # private API. this unloads constants and clears examples
|
144
|
+
RSpec::Core::Runner.invoke # this reloads and starts the test suite
|
145
|
+
when :minitest
|
146
|
+
# reload test-suite code on reset (for `setup_file_listener`)
|
147
|
+
# TODO: also reload dependent files
|
148
|
+
# NOTE: run_on_method will fail if test_name also changed
|
149
|
+
@test_class = reload_const(@test_class.name, @source_filepath)
|
150
|
+
|
151
|
+
# TODO
|
152
|
+
# if Cyperful.config.reload_source_files && defined?(Rails)
|
153
|
+
# Rails.application.reloader.reload!
|
154
|
+
# end
|
155
|
+
|
156
|
+
Minitest.run_one_method(@test_class, @test_name)
|
157
|
+
else
|
158
|
+
raise "Unsupported test framework: #{Cyperful.test_framework}"
|
159
|
+
end
|
131
160
|
end
|
132
161
|
end
|
133
162
|
|
@@ -161,11 +190,11 @@ class Cyperful::Driver
|
|
161
190
|
# @source_file_listener = Listen.to(rails_directory) ...
|
162
191
|
# end
|
163
192
|
|
164
|
-
if
|
193
|
+
if config.reload_test_files
|
165
194
|
@file_listener&.stop
|
166
195
|
@file_listener =
|
167
196
|
Listen.to(test_directory) do |_modified, _added, _removed|
|
168
|
-
puts "Test files changed, resetting test..."
|
197
|
+
logger.puts "Test files changed, resetting test..."
|
169
198
|
|
170
199
|
# keep the same pause state after the reload
|
171
200
|
self.class.next_run_options = { pause_at_step: @pause_at_step }
|
@@ -178,13 +207,15 @@ class Cyperful::Driver
|
|
178
207
|
end
|
179
208
|
|
180
209
|
def print_steps
|
181
|
-
puts
|
210
|
+
logger.puts("found #{@steps.length} steps:")
|
182
211
|
@steps.each_with_index do |step, i|
|
183
|
-
|
184
|
-
|
185
|
-
|
212
|
+
logger.plain(
|
213
|
+
" #{
|
214
|
+
(i + 1).to_s.rjust(2)
|
215
|
+
}. #{step[:method]}: #{step[:line]}:#{step[:column]}",
|
216
|
+
)
|
186
217
|
end
|
187
|
-
|
218
|
+
logger.plain
|
188
219
|
end
|
189
220
|
|
190
221
|
# pending (i.e. test hasn't started), paused, running, passed, failed
|
@@ -267,7 +298,7 @@ class Cyperful::Driver
|
|
267
298
|
@current_step[:end_at] = (Time.now.to_f * 1000.0).to_i
|
268
299
|
@current_step[:status] = !error ? "passed" : "failed"
|
269
300
|
|
270
|
-
|
301
|
+
logger.plain(
|
271
302
|
" (#{@current_step[:end_at] - @current_step[:start_at]}ms)#{
|
272
303
|
error ? " FAILED" : ""
|
273
304
|
}",
|
@@ -293,7 +324,7 @@ class Cyperful::Driver
|
|
293
324
|
end
|
294
325
|
|
295
326
|
def drive_iframe
|
296
|
-
puts "Driving iframe..."
|
327
|
+
logger.puts "Driving iframe..."
|
297
328
|
|
298
329
|
# make sure a `within` block doesn't affect these commands
|
299
330
|
without_finder_scopes do
|
@@ -353,8 +384,13 @@ class Cyperful::Driver
|
|
353
384
|
end
|
354
385
|
|
355
386
|
private def skip_multi_sessions
|
356
|
-
|
357
|
-
warn "Skipped
|
387
|
+
if Capybara.current_session != @session
|
388
|
+
logger.warn "Skipped setup in non-default session"
|
389
|
+
# for debugging: {
|
390
|
+
# "current_session.mode": Capybara.current_session.mode,
|
391
|
+
# "session.mode": @session.mode,
|
392
|
+
# current_driver: Capybara.current_driver,
|
393
|
+
# }.to_json
|
358
394
|
return true
|
359
395
|
end
|
360
396
|
false
|
@@ -390,7 +426,7 @@ class Cyperful::Driver
|
|
390
426
|
end
|
391
427
|
|
392
428
|
def setup_api_server
|
393
|
-
@ui_server = Cyperful::UiServer.new(port:
|
429
|
+
@ui_server = Cyperful::UiServer.new(port: config.port)
|
394
430
|
|
395
431
|
@cyperful_origin = @ui_server.url_origin
|
396
432
|
|
@@ -422,7 +458,7 @@ class Cyperful::Driver
|
|
422
458
|
# The server appears to always stop on it's own,
|
423
459
|
# so we don't need to stop it within an `at_exit` or `Minitest.after_run`
|
424
460
|
|
425
|
-
puts "
|
461
|
+
logger.puts "server started: #{@cyperful_origin}"
|
426
462
|
end
|
427
463
|
|
428
464
|
def teardown(error = nil)
|
@@ -430,11 +466,11 @@ class Cyperful::Driver
|
|
430
466
|
@tracepoint = nil
|
431
467
|
|
432
468
|
if error&.is_a?(Cyperful::ResetCommand)
|
433
|
-
puts "
|
469
|
+
logger.puts "Resetting test (ignore any error logs)..."
|
434
470
|
|
435
471
|
@ui_server.notify(nil) # `break` out of the `loop` (see `UiServer#socket_open`)
|
436
472
|
|
437
|
-
|
473
|
+
enqueue_reset
|
438
474
|
return
|
439
475
|
end
|
440
476
|
|
@@ -461,10 +497,12 @@ class Cyperful::Driver
|
|
461
497
|
|
462
498
|
@ui_server.notify(nil) # `break` out of the `loop` (see `UiServer#socket_open`)
|
463
499
|
|
464
|
-
puts "
|
465
|
-
|
500
|
+
logger.puts "teardown complete. Waiting for command..."
|
501
|
+
|
502
|
+
# NOTE: MiniTest will raise an `Interrupt` if the user Ctrl+C's here
|
503
|
+
# TODO: can't seem to capture Rspec Ctrl+C's :(
|
466
504
|
command = @step_pausing_queue.deq
|
467
|
-
|
505
|
+
enqueue_reset if command == :reset
|
468
506
|
ensure
|
469
507
|
@file_listener&.stop
|
470
508
|
@file_listener = nil
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module Cyperful::FrameworkHelper
|
2
|
+
# Disable default screenshot on failure b/c we handle them ourselves.
|
3
|
+
# https://github.com/rails/rails/blob/v7.0.1/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb#L156
|
4
|
+
def take_failed_screenshot
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
end
|
@@ -1,29 +1,5 @@
|
|
1
1
|
require "action_dispatch/system_testing/driver"
|
2
2
|
|
3
|
-
# The Minitest test helper.
|
4
|
-
# TODO: support other test frameworks like RSpec
|
5
|
-
module Cyperful::SystemTestHelper
|
6
|
-
def setup
|
7
|
-
Cyperful.setup(self.class, self.method_name)
|
8
|
-
super
|
9
|
-
end
|
10
|
-
|
11
|
-
def teardown
|
12
|
-
error = passed? ? nil : failure
|
13
|
-
|
14
|
-
error = error.error if error.is_a?(Minitest::UnexpectedError)
|
15
|
-
|
16
|
-
Cyperful.teardown(error)
|
17
|
-
super
|
18
|
-
end
|
19
|
-
|
20
|
-
# Disable default screenshot on failure b/c we handle them ourselves.
|
21
|
-
# https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb#L156
|
22
|
-
def take_failed_screenshot
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
3
|
# we need to override the some Capybara::Session methods because they
|
28
4
|
# control the top-level browser window, but we want them
|
29
5
|
# to control the iframe instead
|
@@ -78,10 +54,32 @@ module PrependSystemTestingDriver
|
|
78
54
|
|
79
55
|
# this assumes Selenium and Chrome:
|
80
56
|
|
57
|
+
# all chrome flags: https://peter.sh/experiments/chromium-command-line-switches
|
58
|
+
# chrome flags for tooling: https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md
|
59
|
+
|
60
|
+
# set the min flags to make cyperful functional:
|
61
|
+
|
81
62
|
# so user isn't prompted when we start recording video w/ MediaStream
|
82
63
|
driver_opts.add_argument("--auto-accept-this-tab-capture")
|
83
64
|
driver_opts.add_argument("--use-fake-ui-for-media-stream")
|
84
65
|
|
66
|
+
if driver_opts.args.none? { |arg| arg.include?("--disable-features=") }
|
67
|
+
# credit: https://github.com/krausest/js-framework-benchmark/discussions/1682
|
68
|
+
disabled_features = %w[
|
69
|
+
InterestFeedContentSuggestions
|
70
|
+
IPH_DemoMode
|
71
|
+
BackForwardCache
|
72
|
+
OptimizationHints
|
73
|
+
OptimizationHintsFetching
|
74
|
+
OptimizationTargetPrediction
|
75
|
+
PrivacySandboxSettings4
|
76
|
+
Translate
|
77
|
+
]
|
78
|
+
driver_opts.add_argument(
|
79
|
+
"--flag-switches-begin --disable-features=#{disabled_features.join(",")} --flag-switches-end",
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
85
83
|
# make sure we're not in headless mode
|
86
84
|
driver_opts.args.delete("--headless")
|
87
85
|
driver_opts.args.delete("--headless=new")
|
@@ -94,9 +92,5 @@ module PrependSystemTestingDriver
|
|
94
92
|
end
|
95
93
|
ActionDispatch::SystemTesting::Driver.prepend(PrependSystemTestingDriver)
|
96
94
|
|
97
|
-
# if defined?(Minitest::Test)
|
98
|
-
# Minitest::Test::PASSTHROUGH_EXCEPTIONS << Cyperful::AbstractCommand
|
99
|
-
# end
|
100
|
-
|
101
95
|
# fix for: Set-Cookie (SameSite=Lax) doesn't work when within an iframe with host 127.0.0.1
|
102
96
|
Capybara.server_host = "localhost"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Cyperful::Logger
|
2
|
+
def puts(message = nil)
|
3
|
+
# call puts method from Kernel module
|
4
|
+
Kernel.puts("[cyperful] #{message}")
|
5
|
+
end
|
6
|
+
|
7
|
+
def warn(message = nil)
|
8
|
+
Kernel.warn("[cyperful] #{message}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def plain(message = nil)
|
12
|
+
Kernel.puts(message)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "cyperful"
|
2
|
+
require "capybara/minitest"
|
3
|
+
|
4
|
+
module Cyperful::Minitest # rubocop:disable Style/ClassAndModuleChildren
|
5
|
+
module SystemTestHelper
|
6
|
+
include Cyperful::FrameworkHelper
|
7
|
+
|
8
|
+
def setup
|
9
|
+
Cyperful.setup(self.class, self.method_name)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
error = passed? ? nil : failure
|
15
|
+
|
16
|
+
error = error.error if error.is_a?(Minitest::UnexpectedError)
|
17
|
+
|
18
|
+
Cyperful.teardown(error)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Cyperful.add_step_at_methods(
|
25
|
+
Capybara::Minitest::Assertions.instance_methods(false),
|
26
|
+
)
|
27
|
+
|
28
|
+
# if defined?(Minitest::Test)
|
29
|
+
# Minitest::Test::PASSTHROUGH_EXCEPTIONS << Cyperful::AbstractCommand
|
30
|
+
# end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "cyperful"
|
2
|
+
|
3
|
+
RSpec.configure do |rspec_conf|
|
4
|
+
rspec_conf.include(Cyperful::FrameworkHelper)
|
5
|
+
|
6
|
+
rspec_conf.before(:example, type: :system) do
|
7
|
+
# e.g. class = `RSpec::ExampleGroups::MyTest`
|
8
|
+
# e.g. full_description = "my_test can visit root"
|
9
|
+
example = RSpec.current_example
|
10
|
+
Cyperful.setup(self.class, example.full_description)
|
11
|
+
end
|
12
|
+
|
13
|
+
rspec_conf.after(:example, type: :system) do
|
14
|
+
example = RSpec.current_example
|
15
|
+
error = example.exception # RSpec::Expectations::ExpectationNotMetError | nil
|
16
|
+
|
17
|
+
Cyperful.teardown(error)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Cyperful.add_step_at_methods(
|
22
|
+
Capybara::RSpecMatchers.instance_methods(false),
|
23
|
+
# Capybara::RSpecMatcherProxies.instance_methods(false),
|
24
|
+
)
|
data/lib/cyperful/test_parser.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
require "parser/current"
|
2
|
-
require "capybara/minitest"
|
1
|
+
require "parser/current" # TODO: switch to Prism?
|
3
2
|
|
4
3
|
class Cyperful::TestParser
|
5
4
|
# see docs for methods: https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Session
|
6
5
|
@step_at_methods =
|
7
|
-
Capybara::Session::DSL_METHODS
|
8
|
-
Capybara::Minitest::Assertions.instance_methods(false) -
|
6
|
+
Capybara::Session::DSL_METHODS -
|
9
7
|
# exclude methods that don't have side-effects i.e. don't modify the page:
|
10
8
|
%i[
|
11
9
|
body
|
@@ -25,7 +23,7 @@ class Cyperful::TestParser
|
|
25
23
|
@step_at_methods_set ||= @step_at_methods.to_set
|
26
24
|
end
|
27
25
|
def self.add_step_at_methods(*mods_or_methods)
|
28
|
-
mods_or_methods.each do |mod_or_method|
|
26
|
+
mods_or_methods.flatten.each do |mod_or_method|
|
29
27
|
case mod_or_method
|
30
28
|
when Module
|
31
29
|
@step_at_methods +=
|
@@ -33,17 +31,81 @@ class Cyperful::TestParser
|
|
33
31
|
when String, Symbol
|
34
32
|
@step_at_methods << mod_or_method.to_sym
|
35
33
|
else
|
36
|
-
raise "Expected Module or
|
34
|
+
raise "Expected Module or string/symbol, got: #{mod_or_method.class.name}"
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
41
39
|
def initialize(test_class)
|
42
40
|
@test_class = test_class
|
43
|
-
|
41
|
+
|
42
|
+
@source_filepath =
|
43
|
+
if Cyperful.rspec?
|
44
|
+
test_class.metadata.fetch(:absolute_file_path)
|
45
|
+
else
|
46
|
+
Object.const_source_location(test_class.name).first
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
def steps_per_test
|
51
|
+
case Cyperful.test_framework
|
52
|
+
when :rspec
|
53
|
+
rspec_steps_per_test
|
54
|
+
when :minitest
|
55
|
+
minitest_steps_per_test
|
56
|
+
else
|
57
|
+
raise "Unsupported test framework: #{Cyperful.test_framework}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private def rspec_steps_per_test
|
62
|
+
ast = Parser::CurrentRuby.parse(File.read(@source_filepath))
|
63
|
+
|
64
|
+
example_per_line =
|
65
|
+
@test_class.examples.to_h do |example|
|
66
|
+
# file_path = example.metadata.fetch(:absolute_file_path)
|
67
|
+
[example.metadata.fetch(:line_number) || -1, example]
|
68
|
+
end
|
69
|
+
|
70
|
+
example_asts =
|
71
|
+
search_nodes(ast) do |node|
|
72
|
+
next nil unless node.type == :block
|
73
|
+
|
74
|
+
# assumption: the block is on the same line as the example, and there's at most one example per line
|
75
|
+
example = example_per_line[node.loc.begin.line]
|
76
|
+
next nil unless example
|
77
|
+
|
78
|
+
# "#{@test_class.name} #{example.description} #{}"
|
79
|
+
|
80
|
+
[example, node]
|
81
|
+
end
|
82
|
+
|
83
|
+
example_asts.to_h do |(example, block_node)|
|
84
|
+
out = []
|
85
|
+
block_node.children.each { |child| find_test_steps(child, out) }
|
86
|
+
|
87
|
+
# de-duplicate steps by line number
|
88
|
+
# TODO: support multiple steps on the same line. `step_per_line = ...` needs to be refactored
|
89
|
+
out = out.reverse.uniq { |step| step[:line] }.reverse
|
90
|
+
|
91
|
+
[example.full_description.to_sym, out]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private def search_nodes(parent, out = [], &block)
|
96
|
+
parent.children.each do |node|
|
97
|
+
next unless node.is_a?(Parser::AST::Node)
|
98
|
+
if (ret = block.call(node))
|
99
|
+
#
|
100
|
+
out << ret
|
101
|
+
else
|
102
|
+
search_nodes(node, out, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
out
|
106
|
+
end
|
107
|
+
|
108
|
+
private def minitest_steps_per_test
|
47
109
|
ast = Parser::CurrentRuby.parse(File.read(@source_filepath))
|
48
110
|
|
49
111
|
test_class_name = @test_class.name.to_sym
|
@@ -57,7 +119,7 @@ class Cyperful::TestParser
|
|
57
119
|
end
|
58
120
|
end
|
59
121
|
unless system_test_class
|
60
|
-
raise "Could not find class #{test_class.name} in #{@source_filepath}"
|
122
|
+
raise "Could not find class #{@test_class.name} in #{@source_filepath}"
|
61
123
|
end
|
62
124
|
|
63
125
|
(
|
@@ -76,21 +138,21 @@ class Cyperful::TestParser
|
|
76
138
|
test_string = node.children[0].children[2].children[0]
|
77
139
|
|
78
140
|
# https://github.com/rails/rails/blob/66676ce499a32e4c62220bd05f8ee2cdf0e15f0c/activesupport/lib/active_support/testing/declarative.rb#L14C23-L14C61
|
79
|
-
|
141
|
+
test_name = :"test_#{test_string.gsub(/\s+/, "_")}"
|
80
142
|
|
81
143
|
block_node = node.children[2]
|
82
|
-
[
|
144
|
+
[test_name, block_node]
|
83
145
|
|
84
146
|
# e.g. `def test_my_test; ... end`
|
85
147
|
elsif node.type == :def && node.children[0].to_s.start_with?("test_")
|
86
|
-
|
148
|
+
test_name = node.children[0]
|
87
149
|
|
88
150
|
block_node = node.children[2]
|
89
|
-
[
|
151
|
+
[test_name, block_node]
|
90
152
|
end
|
91
153
|
end
|
92
154
|
.compact
|
93
|
-
.to_h do |
|
155
|
+
.to_h do |test_name, block_node|
|
94
156
|
out = []
|
95
157
|
block_node.children.each { |child| find_test_steps(child, out) }
|
96
158
|
|
@@ -98,7 +160,7 @@ class Cyperful::TestParser
|
|
98
160
|
# TODO: support multiple steps on the same line. `step_per_line = ...` needs to be refactored
|
99
161
|
out = out.reverse.uniq { |step| step[:line] }.reverse
|
100
162
|
|
101
|
-
[
|
163
|
+
[test_name, out]
|
102
164
|
end
|
103
165
|
end
|
104
166
|
|
@@ -122,6 +184,8 @@ class Cyperful::TestParser
|
|
122
184
|
end
|
123
185
|
|
124
186
|
children.each { |child| find_test_steps(child, out, depth) }
|
187
|
+
elsif ast.type == :begin || ast.type == :kwbegin || ast.type == :ensure
|
188
|
+
ast.children.each { |child| find_test_steps(child, out, depth) }
|
125
189
|
end
|
126
190
|
|
127
191
|
out
|
data/lib/cyperful/ui_server.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
1
|
require "webrick/websocket"
|
2
2
|
require "webrick/httpproxy"
|
3
3
|
|
4
|
-
# fix for: webrick-websocket incorrectly assumes `Upgrade` header is always present
|
5
|
-
module FixWebrickWebsocketServer
|
6
|
-
def service(req, res)
|
7
|
-
req.header["upgrade"] = [""] if req["upgrade"].nil?
|
8
|
-
super
|
9
|
-
end
|
10
|
-
end
|
11
|
-
WEBrick::Websocket::HTTPServer.prepend(FixWebrickWebsocketServer)
|
12
|
-
|
13
4
|
class Cyperful::UiServer
|
14
5
|
def initialize(port:)
|
15
6
|
@port = port
|
data/lib/cyperful.rb
CHANGED
@@ -33,11 +33,36 @@ module Cyperful
|
|
33
33
|
@config ||= Config.new
|
34
34
|
end
|
35
35
|
|
36
|
+
def self.logger
|
37
|
+
@logger ||= Cyperful::Logger.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.test_framework
|
41
|
+
@test_framework || raise("Cyperful not set up yet")
|
42
|
+
end
|
43
|
+
def self.rspec?
|
44
|
+
@test_framework == :rspec
|
45
|
+
end
|
46
|
+
def self.minitest?
|
47
|
+
@test_framework == :minitest
|
48
|
+
end
|
49
|
+
|
36
50
|
def self.current
|
37
51
|
@current
|
38
52
|
end
|
39
53
|
def self.setup(test_class, test_name)
|
40
|
-
|
54
|
+
@test_framework =
|
55
|
+
if defined?(RSpec::Core::ExampleGroup) &&
|
56
|
+
test_class < RSpec::Core::ExampleGroup
|
57
|
+
:rspec
|
58
|
+
elsif defined?(ActiveSupport::TestCase) &&
|
59
|
+
test_class < ActiveSupport::TestCase
|
60
|
+
:minitest
|
61
|
+
else
|
62
|
+
raise "Unsupported test framework for class: #{test_class.name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
logger.puts "init test: \"#{rspec? ? test_name : "#{test_class}##{test_name}"}\""
|
41
66
|
|
42
67
|
# must set `Cyperful.current` before calling `async_setup`
|
43
68
|
@current ||= Cyperful::Driver.new
|
@@ -62,8 +87,10 @@ module Cyperful
|
|
62
87
|
end
|
63
88
|
|
64
89
|
require "cyperful/commands"
|
90
|
+
require "cyperful/logger"
|
65
91
|
require "cyperful/test_parser"
|
66
92
|
require "cyperful/ui_server"
|
67
93
|
require "cyperful/driver"
|
94
|
+
require "cyperful/framework_helper"
|
68
95
|
require "cyperful/framework_injections"
|
69
96
|
require "cyperful/railtie" if defined?(Rails::Railtie)
|