ruby_raider 3.12.1 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/lib/commands/utility_commands.rb +16 -0
  4. data/lib/generators/cucumber/templates/partials/selenium_env.tt +65 -1
  5. data/lib/generators/generator.rb +10 -1
  6. data/lib/generators/helper_generator.rb +7 -0
  7. data/lib/generators/templates/common/gemfile.tt +1 -1
  8. data/lib/generators/templates/common/partials/web_config.tt +7 -0
  9. data/lib/generators/templates/common/read_me.tt +45 -0
  10. data/lib/generators/templates/helpers/driver_helper.tt +22 -0
  11. data/lib/generators/templates/helpers/partials/selenium_driver.tt +11 -1
  12. data/lib/generators/templates/helpers/raider_element.tt +4 -4
  13. data/lib/generators/templates/helpers/spec_helper.tt +19 -0
  14. data/lib/generators/templates/helpers/trace_helper.tt +1097 -0
  15. data/lib/generators/templates/helpers/wait_helper.tt +145 -4
  16. data/lib/utilities/utilities.rb +7 -0
  17. data/lib/version +1 -1
  18. data/ruby_raider.gemspec +1 -0
  19. data/sig/commands/utility_commands.rbs +2 -0
  20. data/sig/generators/generator.rbs +4 -0
  21. data/sig/generators/helper_generator.rbs +1 -0
  22. data/sig/utilities/utilities.rbs +1 -0
  23. data/spec/generators/fixtures/video_helper_stubs/selenium-webdriver.rb +11 -2
  24. data/spec/generators/trace_helper_spec.rb +945 -0
  25. data/spec/integration/content/config_content_spec.rb +20 -0
  26. data/spec/integration/content/cucumber_reporter_content_spec.rb +51 -0
  27. data/spec/integration/content/gemfile_content_spec.rb +2 -2
  28. data/spec/integration/content/helper_content_spec.rb +60 -1
  29. data/spec/integration/content/readme_content_spec.rb +46 -0
  30. data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
  31. data/spec/integration/end_to_end_spec.rb +392 -29
  32. data/spec/integration/generators/helpers_generator_spec.rb +26 -0
  33. data/spec/integration/helpers/wait_helper_spec.rb +183 -0
  34. data/spec/integration/trace_format_checker.rb +141 -0
  35. data/spec/integration/trace_page_server.rb +80 -0
  36. data/spec/utilities/trace_command_spec.rb +66 -0
  37. metadata +27 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1312bb43aceb9be9a70d5e3d09e6694d901f6b5dc3ec80f27b6973de6c8d60c
4
- data.tar.gz: ad4e86e9f7dd6bbbacac033fe48f18a93d6397040ab29490d0add8456f6ad2cc
3
+ metadata.gz: 6e4d2e6fae8f8504215b558818a81660fca9652f52886c6b72f3e1d6850d01bd
4
+ data.tar.gz: 3a8e5a4290303aab776bce5e6e9e8c86023e88a036b935de18a2c56ded688c8c
5
5
  SHA512:
6
- metadata.gz: b913021e191c5ff9fddc114590bc7b6a4a1b0e340a0fb2257da5477c2e3344dcd73088ea1cbac4eb1b28953b7bc3c250d56c648f15e85977088b8d96d3ddbc2b
7
- data.tar.gz: c1d12da3f4447bbabecc6638895129dc0dd9d23dc08389a8b9d6ee9077c3936855b28746b9a6d9d049b3ced2c2e296462154c88e1845d6357078bb41ab8f75f6
6
+ metadata.gz: f0af4e104fc9cc2b0a99f0ebd4a996994f16500302e5f1cabf635569de8d404c30bf289ce4df145376fad0579b5cf1dc865cee19fd45e757954aa0ebcd4c75bc
7
+ data.tar.gz: 67dd60f0a67b57a2cd09605a7aec9b91a822d198ed95c3be94d67663f6aef5b4db0d4b024ef09bd97e764396381d12c0f0b413552efde25f87fac44d57b48d88
data/README.md CHANGED
@@ -67,6 +67,7 @@ Every generated project comes with:
67
67
  - **Example tests or a skeleton (`examples:false`)** — start from the working login examples, or pass `examples:false` for an empty skeleton with the helpers, config, CI and README (see [Getting Started](#getting-started))
68
68
  - **Login reuse** — generated web projects (Selenium or Watir) log in once and restore the saved session for examples tagged `:logged_in` (RSpec) or `@logged_in` (Cucumber); `RAIDER_LOGIN_STATE=off|refresh` and `login_state: max_age:` control it, and the state stays in git-ignored `.raider/auth/`
69
69
  - **Failure video replay** — Every test is recorded and the video is kept only for failing tests, attached to the report as `video` (WebM from page screenshots for Selenium and Watir, Appium's own MP4 for mobile, which needs `ffmpeg` on the Appium server host for iOS and for Android recordings over 180 s). Turn it off per run with `VIDEO=off`, or per project with `raider u video off`; ffmpeg is downloaded once, SHA-256 checked and cached in `~/.cache/raider`. The RaiderHQ `ffmpeg-7.1` build that the download uses is not published yet, so for now set `RAIDER_FFMPEG_PATH` to an ffmpeg with libvpx to get web videos
70
+ - **Failure trace replay** (Selenium with RSpec or Cucumber) — Failing tests keep a step-by-step trace: each action with its locator and wait time, a screenshot and the page HTML after every step that changes the page, plus network requests and console messages on Chrome, Edge and Firefox. It is saved as `raider-results/attachments/<n>-trace.zip`, attached to the report as `trace` and opened in Raider Desktop or on Hideout. `TRACE=on` keeps passing tests too, `TRACE=off` captures nothing, and `raider u trace on|off` sets it for the project. Typed values in password fields and `RAIDER_SECRET_*` values are hidden; other typed values, page text and URLs are kept, so treat a trace like the video. Request headers, cookies and bodies are never captured
70
71
  - **GitHub Actions CI** — Ready-to-use test pipeline that caches ffmpeg for failure videos
71
72
  - **Rake tasks** — Default, smoke, and regression tasks with tag-based filtering
72
73
  - **Retry support** — RSpec retry with configurable retry count via `RETRY_COUNT` env var
@@ -204,6 +205,7 @@ raider u platform [PLATFORM] # Set platform for cross-platform tests
204
205
  raider u start_appium # Start Appium server
205
206
  raider u desktop # Download Raider Desktop
206
207
  raider u video [ACTION] # Failure videos: install (ffmpeg), status, on, off
208
+ raider u trace [on/off] # Failure traces: turn on or off (Selenium)
207
209
  ```
208
210
 
209
211
  ## Development
@@ -123,7 +123,23 @@ class UtilityCommands < Thor
123
123
  end
124
124
  end
125
125
 
126
+ desc 'trace [on|off]', 'Turns trace capture on or off (writes trace.enabled in config/config.yml)'
127
+
128
+ def trace(action)
129
+ case action
130
+ when 'on', 'off' then trace_toggle(action == 'on')
131
+ else
132
+ say_error "Unknown trace command '#{action}'. Use on|off", :red
133
+ exit 1
134
+ end
135
+ end
136
+
126
137
  no_commands do
138
+ def trace_toggle(enabled)
139
+ Utilities.trace_enabled = enabled
140
+ say "Trace capture #{enabled ? 'enabled' : 'disabled'}", :green
141
+ end
142
+
127
143
  def video_install
128
144
  require_relative '../ruby_raider/ffmpeg'
129
145
  path = RubyRaider::Ffmpeg.ensure!
@@ -8,6 +8,9 @@ require 'raider_reporter/cucumber'
8
8
  require 'tmpdir'
9
9
  require 'yaml'
10
10
  require_relative '../../helpers/video_helper'
11
+ <%- if trace_helper? -%>
12
+ require_relative '../../helpers/trace_helper'
13
+ <%- end -%>
11
14
  require_relative '../../helpers/session_state'
12
15
  <%- if examples? -%>
13
16
  require_relative '../../models/user_factory'
@@ -15,7 +18,47 @@ require_relative '../../page_objects/pages/account'
15
18
  require_relative '../../page_objects/pages/login'
16
19
  <%- end -%>
17
20
 
18
- Before do
21
+ <%- if trace_helper? -%>
22
+ # Attempts per scenario across --retry, keyed on the scenario's location, for the trace of each attempt.
23
+ TRACE_ATTEMPTS = Hash.new(0) unless defined?(TRACE_ATTEMPTS)
24
+
25
+ # Each Gherkin step becomes a group in the trace, named by its keyword and text; hook steps are not steps.
26
+ InstallPlugin do |config, _registry|
27
+ if defined?(TraceHelper)
28
+ begin
29
+ ast_lookup = Cucumber::Formatter::AstLookup.new(config)
30
+ groups = {}
31
+ config.on_event(:test_step_started) do |event|
32
+ next if event.test_step.hook?
33
+
34
+ step = event.test_step
35
+ keyword = begin
36
+ ast_lookup.step_source(step).step.keyword.strip
37
+ rescue StandardError
38
+ nil
39
+ end
40
+ location = { file: step.location.file, line: step.location.lines.min }
41
+ groups[step.id] = TraceHelper.active_recorder&.step_started(name: [keyword, step.text].compact.join(' '), source: location)
42
+ rescue StandardError => e
43
+ TraceHelper.disable!("step groups failed: #{e.message}") if defined?(TraceHelper)
44
+ end
45
+ config.on_event(:test_step_finished) do |event|
46
+ next if event.test_step.hook?
47
+
48
+ status = %i[passed failed].include?(event.result.to_sym) ? event.result.to_sym : :skipped
49
+ error = event.result.failed? ? event.result.exception : nil
50
+ TraceHelper.active_recorder&.step_finished(groups.delete(event.test_step.id), status: status, error: error)
51
+ rescue StandardError => e
52
+ TraceHelper.disable!("step groups failed: #{e.message}") if defined?(TraceHelper)
53
+ end
54
+ rescue StandardError => e
55
+ TraceHelper.warn_once("Gherkin step groups unavailable: #{e.message}")
56
+ end
57
+ end
58
+ end
59
+
60
+ <%- end -%>
61
+ Before do<%= trace_helper? ? ' |scenario|' : '' %>
19
62
  viewport = YAML.load_file('config/config.yml')['viewport']
20
63
  if viewport
21
64
  driver.manage.window.resize_to(viewport['width'], viewport['height'])
@@ -24,6 +67,14 @@ Before do
24
67
  end
25
68
  @video_recorder = VideoHelper.recorder_for(driver)
26
69
  @video_recorder.start
70
+ <%- if trace_helper? -%>
71
+ if defined?(TraceHelper)
72
+ @trace_attempt = (TRACE_ATTEMPTS[scenario.location.to_s] += 1)
73
+ @trace_test = { identifier: scenario.location.to_s, full_name: scenario.name, attempt: @trace_attempt }
74
+ @trace_recorder = TraceHelper.recorder_for(driver)
75
+ @trace_recorder.start
76
+ end
77
+ <%- end -%>
27
78
  end
28
79
 
29
80
  <%- if examples? -%>
@@ -41,8 +92,18 @@ end
41
92
 
42
93
  <%- end -%>
43
94
  After do |scenario|
95
+ <%- if trace_helper? -%>
96
+ if defined?(TraceHelper)
97
+ keep_trace = TraceHelper.keep_all? || scenario.failed?
98
+ trace_test = @trace_test&.merge(status: scenario.failed? ? 'failed' : 'passed', error: scenario.exception)
99
+ trace_path = @trace_recorder&.stop(keep: keep_trace, test: trace_test)
100
+ end
101
+ <%- end -%>
44
102
  video_path = @video_recorder&.stop(keep: scenario.failed?)
45
103
  RaiderReporter.attach('video', video_path, type: :webm) if video_path
104
+ <%- if trace_helper? -%>
105
+ RaiderReporter.attach('trace', trace_path, type: :trace, attempt: @trace_attempt) if trace_path
106
+ <%- end -%>
46
107
  if scenario.failed?
47
108
  Dir.mktmpdir do |temp_folder|
48
109
  screenshot_path = File.join(temp_folder, 'screenshot.png')
@@ -51,6 +112,9 @@ After do |scenario|
51
112
  end
52
113
  end
53
114
  ensure
115
+ <%- if trace_helper? -%>
116
+ @trace_recorder&.cleanup
117
+ <%- end -%>
54
118
  @video_recorder&.cleanup
55
119
  driver.quit
56
120
  end
@@ -73,6 +73,11 @@ class Generator < Thor::Group
73
73
  !watir?
74
74
  end
75
75
 
76
+ # Selenium web projects record a trace per test; it needs WaitHelper's actions, and mobile has none yet
77
+ def trace_helper?
78
+ wait_helper? && !mobile?
79
+ end
80
+
76
81
  # False for `raider new -p examples:false`: an empty skeleton without the example pages, tests and wiring
77
82
  def examples?
78
83
  !options_after_name.include?('no_examples')
@@ -90,8 +95,12 @@ class Generator < Thor::Group
90
95
  selenium?
91
96
  end
92
97
 
98
+ def ruby_raider_version
99
+ File.read(File.expand_path('../version', __dir__)).strip
100
+ end
101
+
93
102
  def ruby_raider_version_requirement
94
- major, minor = File.read(File.expand_path('../version', __dir__)).strip.split('.')
103
+ major, minor = ruby_raider_version.split('.')
95
104
  "~> #{major}.#{minor}"
96
105
  end
97
106
 
@@ -11,6 +11,7 @@ class HelpersGenerator < Generator
11
11
  generate_video_helper
12
12
  generate_session_state
13
13
  generate_wait_helper
14
+ generate_trace_helper
14
15
  end
15
16
 
16
17
  private
@@ -50,6 +51,12 @@ class HelpersGenerator < Generator
50
51
  template('helpers/raider_element.tt', "#{name}/helpers/raider_element.rb")
51
52
  end
52
53
 
54
+ def generate_trace_helper
55
+ return unless trace_helper?
56
+
57
+ template('helpers/trace_helper.tt', "#{name}/helpers/trace_helper.rb")
58
+ end
59
+
53
60
  def generate_appium_helper
54
61
  return unless cross_platform?
55
62
 
@@ -14,7 +14,7 @@ gem 'cucumber'
14
14
  <%- end -%>
15
15
  gem 'parallel_split_test'
16
16
  gem 'parallel_tests'
17
- gem 'raider_reporter', '~> 0.10'
17
+ gem 'raider_reporter', '~> 0.12'
18
18
  gem 'rake'
19
19
  gem 'reek'
20
20
  gem 'rspec'
@@ -19,6 +19,13 @@ video:
19
19
  enabled: true
20
20
  fps: 5
21
21
  max_width: 1280
22
+ <%- if trace_helper? -%>
23
+
24
+ trace:
25
+ enabled: true # capture traces; failing tests keep theirs
26
+ max_steps: 200 # newest steps are kept; older ones lose screenshots and snapshots first
27
+ max_mb: 20 # the zip stays under this; see Cost
28
+ <%- end -%>
22
29
 
23
30
  login_state:
24
31
  max_age: 3600 # seconds a saved login is reused before logging in again
@@ -224,6 +224,9 @@ timeout: 15
224
224
  - `headless`: not in the file by default. Add `headless: true`, or set `HEADLESS=true` for one run, to run the
225
225
  browser without a window.
226
226
  - `video`: failure videos, see [Video replay](#video-replay).
227
+ <%- if trace_helper? -%>
228
+ - `trace`: failing-test traces, see [Trace replay](#trace-replay).
229
+ <%- end -%>
227
230
  - `login_state`: how long a saved login is reused, see [Reusing the login](#reusing-the-login).
228
231
  <%- end -%>
229
232
 
@@ -530,6 +533,10 @@ The wait helper is ordinary code in your project, so you can take it out. To do
530
533
  <%- end -%>
531
534
  5. Scaffolding (`raider g page` and `raider g component`) then writes plain `driver.find_element` examples on its
532
535
  own, because it checks for `helpers/wait_helper.rb`.
536
+ <%- if trace_helper? -%>
537
+
538
+ Note that without WaitHelper a trace records only navigation and the failure (see [Trace replay](#trace-replay)).
539
+ <%- end -%>
533
540
 
534
541
  <%- end -%>
535
542
  <%- if web? -%>
@@ -674,6 +681,44 @@ The video is a `video.webm` built from page screenshots with ffmpeg.
674
681
  no video.
675
682
  <%- end -%>
676
683
 
684
+ <%- if trace_helper? -%>
685
+ ### Trace replay
686
+
687
+ Every test records a trace, and the trace is kept only for failing tests<%= rspec? ? ' (on the last retry attempt)' : '' %>. A trace is a step-by-step
688
+ record of the test: each action and page load, with the locator, how long it waited, a screenshot and the page HTML
689
+ after it, the network requests, and the browser console. It is saved as `raider-results/attachments/<n>-trace.zip`,
690
+ next to the failure screenshot and video, and attached to the report as `trace`.
691
+
692
+ - Keep the trace of passing tests too with `TRACE=on bundle exec <%= rspec? ? 'rspec' : 'cucumber' %>`. Capture nothing for one run with
693
+ `TRACE=off bundle exec <%= rspec? ? 'rspec' : 'cucumber' %>`, or for the project with `raider u trace off` (`raider u trace on` turns it back on).
694
+ `raider u trace on` and `raider u trace off` write `enabled:` under `trace:` in `config/config.yml`.
695
+ - Open a trace in Raider Desktop, or from the test's page on Hideout when the run is uploaded.
696
+ - Cost: a trace takes one screenshot and one page HTML for every step that changes the page (a click, typing,
697
+ a page load), including in passing tests, whose traces are then thrown away. If that slows your suite down,
698
+ switch tracing off as above.
699
+ - Caps, under `trace:` in `config/config.yml`: `max_steps` (200) keeps the newest steps, and `max_mb` (20) keeps the
700
+ zip under that size. When a cap is reached, the HTML of the oldest steps is dropped first, then their
701
+ screenshots, then the oldest network and console entries. The last 10 steps always keep theirs.
702
+ - Network requests and console messages are captured only on Chrome, Edge or Firefox. Other browsers, such as
703
+ Safari, get a trace with steps, screenshots and HTML only. Capture is passive: the trace listens to the browser and
704
+ never changes a request.
705
+ - The trace helper never fails a test. When it has to leave something out, such as network on a browser without
706
+ BiDi or a screenshot it could not take, you get at most one `[trace]` notice on stderr for the run. If capturing
707
+ breaks, you get one more `[trace]` line ending in "tracing disabled for this run", and no more traces for that run.
708
+
709
+ **Treat a trace like the video: it can contain secrets.** What is hidden: values typed into password fields
710
+ (`type=password`, or an `autocomplete` that contains "password"), and any typed value equal to a `RAIDER_SECRET_*`
711
+ environment variable. Everything else is kept: other typed values, page text, URLs with their query strings, and
712
+ console text. So a secret typed into an ordinary field, or shown on the page, ends up in the trace. Failure messages
713
+ can contain typed text too, because an assertion message is kept as it is. Request headers, cookies and request or
714
+ response bodies are never captured. Do not share or upload traces from runs that handle real credentials.
715
+
716
+ **Removing the trace helper.** The trace helper is ordinary code in your project. Delete `helpers/trace_helper.rb` and
717
+ the `require_relative` lines that load `trace_helper`: in <%= rspec? ? '`helpers/driver_helper.rb`' : '`helpers/driver_helper.rb` and `features/support/env.rb`' %>. The other lines that use it
718
+ (in <%= rspec? ? '`helpers/spec_helper.rb`' : '`features/support/env.rb`' %> and the driver helper's listeners) only run when `TraceHelper` is defined,
719
+ so they can stay. Remove the `trace:` block from `config/config.yml` if you like.
720
+
721
+ <%- end -%>
677
722
  <%- unless azure? && mobile? -%>
678
723
  ### Quarantine
679
724
 
@@ -5,12 +5,34 @@ require 'yaml'
5
5
  require 'active_support/inflector'
6
6
  require 'selenium-webdriver'
7
7
  require_relative 'video_helper'
8
+ <%- if trace_helper? -%>
9
+ require_relative 'trace_helper'
10
+ <%- end -%>
8
11
  <%- else -%>
9
12
  require 'appium_lib'
10
13
  <%- end -%>
11
14
 
12
15
  module DriverHelper
13
16
  <%- if selenium_based? -%>
17
+ # Selenium takes one listener: this one hands every event on to the video and trace listeners.
18
+ class FanOutListener < Selenium::WebDriver::Support::AbstractEventListener
19
+ def self.for(listeners)
20
+ listeners.empty? ? nil : new(listeners)
21
+ end
22
+
23
+ def initialize(listeners)
24
+ super()
25
+ @listeners = listeners
26
+ end
27
+
28
+ Selenium::WebDriver::Support::AbstractEventListener.instance_methods(false).each do |event|
29
+ define_method(event) do |*args|
30
+ @listeners.each { |listener| listener.public_send(event, *args) if listener.respond_to?(event) }
31
+ nil
32
+ end
33
+ end
34
+ end
35
+
14
36
  def driver(*opts)
15
37
  @driver ||= create_driver(*opts)
16
38
  end
@@ -2,11 +2,19 @@
2
2
  @config = YAML.load_file('config/config.yml')
3
3
  browser = @config['browser']
4
4
  arguments = { options: create_webdriver_options(*opts) }
5
- listener = VideoHelper.listener
5
+ listener = FanOutListener.for(listeners)
6
6
  arguments[:listener] = listener if listener
7
7
  Selenium::WebDriver.for(browser.to_sym, **arguments)
8
8
  end
9
9
 
10
+ # The helpers' Selenium listeners. Deleting video_helper.rb or trace_helper.rb needs only its require.
11
+ def listeners
12
+ listeners = []
13
+ listeners << VideoHelper.listener if defined?(VideoHelper)
14
+ listeners << TraceHelper.listener if defined?(TraceHelper)
15
+ listeners.compact
16
+ end
17
+
10
18
  def browser_arguments(*opts)
11
19
  args = opts.empty? ? @config['browser_arguments'][@config['browser']] : opts
12
20
  args += ['--headless'] if (ENV['HEADLESS'] || @config['headless']) && !args.include?('--headless')
@@ -18,5 +26,7 @@
18
26
  browser = load_browser == 'ie' ? load_browser.upcase : load_browser.capitalize
19
27
  options = "Selenium::WebDriver::#{browser}::Options".constantize.new
20
28
  browser_arguments(*opts).each { |arg| options.add_argument(arg) }
29
+ # WebDriver BiDi carries the trace's network and console (chrome, edge and firefox).
30
+ options.web_socket_url = true if defined?(TraceHelper) && TraceHelper.bidi?(load_browser)
21
31
  options
22
32
  end
@@ -65,13 +65,13 @@ class RaiderElement
65
65
  end
66
66
 
67
67
  def send_keys(*keys, timeout: nil)
68
- perform(:send_keys, timeout) { |element| element.send_keys(*keys) }
68
+ perform(:send_keys, timeout, args: keys) { |element| element.send_keys(*keys) }
69
69
  self
70
70
  end
71
71
 
72
72
  <%- unless mobile? -%>
73
73
  def select_by(how, what, timeout: nil)
74
- perform(:select_by, timeout) { |element| Selenium::WebDriver::Support::Select.new(element).select_by(how, what) }
74
+ perform(:select_by, timeout, args: [how, what]) { |element| Selenium::WebDriver::Support::Select.new(element).select_by(how, what) }
75
75
  self
76
76
  end
77
77
 
@@ -143,8 +143,8 @@ class RaiderElement
143
143
  WaitHelper::Lookup.for(@driver, locator, by, scope: @lookup)
144
144
  end
145
145
 
146
- def perform(action, timeout, &block)
147
- waiter(timeout).perform_action(action, @lookup, WaitHelper::Waiter::CHECKS.fetch(action), &block)
146
+ def perform(action, timeout, args: nil, &block)
147
+ waiter(timeout).perform_action(action, @lookup, WaitHelper::Waiter::CHECKS.fetch(action), args: args, &block)
148
148
  end
149
149
 
150
150
  def method_missing(name, *args, **options, &block)
@@ -62,6 +62,10 @@ module SpecHelper
62
62
  <%- end -%>
63
63
  @video_recorder = VideoHelper.recorder_for(<%= watir? ? 'browser' : 'driver' %>)
64
64
  @video_recorder.start
65
+ <%- if trace_helper? -%>
66
+ @trace_recorder = TraceHelper.recorder_for(driver) if defined?(TraceHelper)
67
+ @trace_recorder&.start
68
+ <%- end -%>
65
69
  end
66
70
  <%- if web? && examples? -%>
67
71
 
@@ -80,6 +84,15 @@ module SpecHelper
80
84
 
81
85
  config.after(:each) do |example|
82
86
  example_name = example.description
87
+ <%- if trace_helper? -%>
88
+ if defined?(TraceHelper)
89
+ attempt = example.metadata[:retry_attempts].to_i + 1
90
+ keep_trace = TraceHelper.keep_all? || (example.exception && (defined?(VideoHelper) ? VideoHelper.final_attempt?(example) : true))
91
+ trace_test = { identifier: example.id.delete_prefix('./'), full_name: example.full_description,
92
+ status: example.exception ? 'failed' : 'passed', attempt: attempt, error: example.exception }
93
+ trace_path = @trace_recorder&.stop(keep: keep_trace, test: trace_test)
94
+ end
95
+ <%- end -%>
83
96
  keep_video = example.exception ? VideoHelper.final_attempt?(example) : false
84
97
  video_path = @video_recorder&.stop(keep: keep_video)
85
98
  if example.exception
@@ -91,7 +104,13 @@ module SpecHelper
91
104
  end
92
105
  end
93
106
  RaiderReporter.attach('video', video_path, type: <%= mobile? ? ':mp4' : ':webm' %>) if video_path
107
+ <%- if trace_helper? -%>
108
+ RaiderReporter.attach('trace', trace_path, type: :trace, attempt: attempt) if trace_path
109
+ <%- end -%>
94
110
  ensure
111
+ <%- if trace_helper? -%>
112
+ @trace_recorder&.cleanup
113
+ <%- end -%>
95
114
  @video_recorder&.cleanup
96
115
  <%= partial('quit_driver', strip: true) %>
97
116
  end