ruby_raider 3.12.0 → 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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +2 -0
- data/lib/commands/utility_commands.rb +16 -0
- data/lib/generators/automation/templates/page.tt +8 -3
- data/lib/generators/automation/templates/partials/initialize_selector.tt +3 -3
- data/lib/generators/automation/templates/partials/url_methods.tt +3 -2
- data/lib/generators/automation/templates/partials/visit_method.tt +5 -5
- data/lib/generators/cucumber/cucumber_generator.rb +1 -1
- data/lib/generators/cucumber/templates/env.tt +3 -3
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +65 -1
- data/lib/generators/cucumber/templates/steps.tt +2 -2
- data/lib/generators/cucumber/templates/user_factory.tt +2 -0
- data/lib/generators/cucumber/templates/world.tt +5 -3
- data/lib/generators/generator.rb +25 -12
- data/lib/generators/helper_generator.rb +7 -0
- data/lib/generators/rspec/templates/accessibility_spec.tt +10 -10
- data/lib/generators/rspec/templates/spec.tt +2 -1
- data/lib/generators/templates/common/gemfile.tt +9 -9
- data/lib/generators/templates/common/partials/web_config.tt +7 -0
- data/lib/generators/templates/common/read_me.tt +45 -0
- data/lib/generators/templates/helpers/appium_helper.tt +17 -15
- data/lib/generators/templates/helpers/browser_helper.tt +1 -2
- data/lib/generators/templates/helpers/driver_helper.tt +22 -0
- data/lib/generators/templates/helpers/partials/appium_driver.tt +1 -1
- data/lib/generators/templates/helpers/partials/selenium_driver.tt +11 -1
- data/lib/generators/templates/helpers/raider_element.tt +4 -4
- data/lib/generators/templates/helpers/session_state.tt +1 -1
- data/lib/generators/templates/helpers/spec_helper.tt +20 -1
- data/lib/generators/templates/helpers/trace_helper.tt +1097 -0
- data/lib/generators/templates/helpers/wait_helper.tt +145 -4
- data/lib/ruby_raider.rb +9 -2
- data/lib/utilities/utilities.rb +7 -0
- data/lib/version +1 -1
- data/ruby_raider.gemspec +1 -0
- data/sig/commands/utility_commands.rbs +2 -0
- data/sig/generators/generator.rbs +6 -0
- data/sig/generators/helper_generator.rbs +1 -0
- data/sig/ruby_raider.rbs +1 -0
- data/sig/utilities/utilities.rbs +1 -0
- data/spec/commands/raider_commands_spec.rb +25 -1
- data/spec/fixtures/watir_master/cucumber_watir/features/step_definitions/login_steps.rb.snapshot +1 -1
- data/spec/fixtures/watir_master/cucumber_watir/page_objects/abstract/page.rb.snapshot +2 -4
- data/spec/fixtures/watir_master/rspec_watir/page_objects/abstract/page.rb.snapshot +2 -4
- data/spec/generators/fixtures/video_helper_stubs/selenium-webdriver.rb +11 -2
- data/spec/generators/trace_helper_spec.rb +945 -0
- data/spec/integration/content/config_content_spec.rb +20 -0
- data/spec/integration/content/content_helper.rb +1 -5
- data/spec/integration/content/cucumber_reporter_content_spec.rb +51 -0
- data/spec/integration/content/gemfile_content_spec.rb +2 -2
- data/spec/integration/content/helper_content_spec.rb +60 -1
- data/spec/integration/content/readme_content_spec.rb +46 -0
- data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
- data/spec/integration/content/rubocop_content_spec.rb +87 -0
- data/spec/integration/content/syntax_validation_spec.rb +31 -51
- data/spec/integration/content/test_content_spec.rb +5 -2
- data/spec/integration/end_to_end_spec.rb +415 -29
- data/spec/integration/frozen_string_literal_matcher.rb +13 -0
- data/spec/integration/generators/helpers_generator_spec.rb +26 -0
- data/spec/integration/generators/invoke_generators_spec.rb +60 -0
- data/spec/integration/helpers/wait_helper_spec.rb +183 -0
- data/spec/integration/scaffolding_e2e_spec.rb +1 -5
- data/spec/integration/trace_format_checker.rb +141 -0
- data/spec/integration/trace_page_server.rb +80 -0
- data/spec/utilities/trace_command_spec.rb +66 -0
- metadata +29 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e4d2e6fae8f8504215b558818a81660fca9652f52886c6b72f3e1d6850d01bd
|
|
4
|
+
data.tar.gz: 3a8e5a4290303aab776bce5e6e9e8c86023e88a036b935de18a2c56ded688c8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0af4e104fc9cc2b0a99f0ebd4a996994f16500302e5f1cabf635569de8d404c30bf289ce4df145376fad0579b5cf1dc865cee19fd45e757954aa0ebcd4c75bc
|
|
7
|
+
data.tar.gz: 67dd60f0a67b57a2cd09605a7aec9b91a822d198ed95c3be94d67663f6aef5b4db0d4b024ef09bd97e764396381d12c0f0b413552efde25f87fac44d57b48d88
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.4.
|
|
1
|
+
3.4.12
|
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!
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
<%- if web? && examples? -%>
|
|
2
4
|
require_relative '../components/header'
|
|
3
5
|
<%- end -%>
|
|
@@ -12,9 +14,12 @@ class Page
|
|
|
12
14
|
<%- if wait_helper? -%>
|
|
13
15
|
include WaitHelper
|
|
14
16
|
<%- end -%>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
<%- if cross_platform? || wait_helper? -%>
|
|
18
|
+
|
|
19
|
+
<%- end -%>
|
|
20
|
+
<%= partial('initialize_selector') -%>
|
|
21
|
+
<%= partial('visit_method') -%>
|
|
22
|
+
<%= partial('url_methods') -%>
|
|
18
23
|
|
|
19
24
|
def to_s
|
|
20
25
|
self.class.to_s.sub('Page', ' Page')
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<%- if watir? -%>
|
|
2
2
|
attr_reader :browser
|
|
3
3
|
|
|
4
4
|
def initialize(browser)
|
|
5
5
|
@browser = browser
|
|
6
6
|
end
|
|
7
|
-
|
|
7
|
+
<%- else -%>
|
|
8
8
|
attr_reader :driver
|
|
9
9
|
|
|
10
10
|
def initialize(driver)
|
|
11
11
|
@driver = driver
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
<%- end -%>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<%- if selenium_based? -%>
|
|
2
|
+
|
|
3
3
|
def visit(*page)
|
|
4
4
|
@driver.navigate.to full_url(page.first)
|
|
5
5
|
end
|
|
6
|
-
|
|
6
|
+
<%- elsif watir? -%>
|
|
7
|
+
|
|
7
8
|
def visit(*page)
|
|
8
9
|
@browser.goto full_url(page.first)
|
|
9
10
|
end
|
|
10
|
-
|
|
11
|
-
<% end %>
|
|
11
|
+
<%- end -%>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%- if selenium_based? -%>
|
|
2
|
-
|
|
2
|
+
<%= partial('selenium_env') -%>
|
|
3
3
|
<%- elsif mobile? -%>
|
|
4
|
-
|
|
4
|
+
<%= partial('appium_env') -%>
|
|
5
5
|
<%- else -%>
|
|
6
|
-
|
|
6
|
+
<%= partial('watir_env') -%>
|
|
7
7
|
<%- end -%>
|
|
@@ -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
|
-
|
|
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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
<%- if watir? -%>
|
|
2
|
-
|
|
4
|
+
<%= partial('watir_world', strip: true) %>
|
|
3
5
|
<%- else -%>
|
|
4
|
-
|
|
5
|
-
<%- end -%>
|
|
6
|
+
<%= partial('driver_world', strip: true) %>
|
|
7
|
+
<%- end -%>
|
data/lib/generators/generator.rb
CHANGED
|
@@ -23,37 +23,36 @@ class Generator < Thor::Group
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def cucumber?
|
|
26
|
-
|
|
26
|
+
framework == 'cucumber'
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# The framework is cross platform when it supports Android and iOS
|
|
30
30
|
def cross_platform?
|
|
31
|
-
|
|
31
|
+
automation == 'cross_platform'
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def mobile?
|
|
35
|
-
|
|
35
|
+
%w[android ios cross_platform].include?(automation)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def single_platform?
|
|
39
|
-
|
|
39
|
+
%w[android ios].include?(automation)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def ios?
|
|
43
|
-
|
|
43
|
+
automation == 'ios'
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def android?
|
|
47
|
-
|
|
47
|
+
automation == 'android'
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def rspec?
|
|
51
|
-
|
|
51
|
+
framework == 'rspec'
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# Only the options after automation, framework and name, so a project named "azure" is not Azure CI
|
|
55
54
|
def azure?
|
|
56
|
-
|
|
55
|
+
options_after_name.include?('azure')
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
def github_ci?
|
|
@@ -74,9 +73,14 @@ class Generator < Thor::Group
|
|
|
74
73
|
!watir?
|
|
75
74
|
end
|
|
76
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
|
+
|
|
77
81
|
# False for `raider new -p examples:false`: an empty skeleton without the example pages, tests and wiring
|
|
78
82
|
def examples?
|
|
79
|
-
!
|
|
83
|
+
!options_after_name.include?('no_examples')
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
def web?
|
|
@@ -84,20 +88,29 @@ class Generator < Thor::Group
|
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
def axe_addon?
|
|
87
|
-
|
|
91
|
+
options_after_name.include?('axe_addon')
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
def selenium_based?
|
|
91
95
|
selenium?
|
|
92
96
|
end
|
|
93
97
|
|
|
98
|
+
def ruby_raider_version
|
|
99
|
+
File.read(File.expand_path('../version', __dir__)).strip
|
|
100
|
+
end
|
|
101
|
+
|
|
94
102
|
def ruby_raider_version_requirement
|
|
95
|
-
major, minor =
|
|
103
|
+
major, minor = ruby_raider_version.split('.')
|
|
96
104
|
"~> #{major}.#{minor}"
|
|
97
105
|
end
|
|
98
106
|
|
|
99
107
|
private
|
|
100
108
|
|
|
109
|
+
# Option flags only: everything after automation, framework and name
|
|
110
|
+
def options_after_name
|
|
111
|
+
args.drop(3)
|
|
112
|
+
end
|
|
113
|
+
|
|
101
114
|
def _initializer
|
|
102
115
|
@_initializer ||= super
|
|
103
116
|
end
|
|
@@ -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
|
|
|
@@ -4,18 +4,18 @@ require_relative '../helpers/spec_helper'
|
|
|
4
4
|
|
|
5
5
|
describe 'Accessibility' do
|
|
6
6
|
before do
|
|
7
|
-
|
|
8
|
-
browser.goto 'https://raider-test-site.onrender.com/'
|
|
9
|
-
|
|
10
|
-
driver.get 'https://raider-test-site.onrender.com/'
|
|
11
|
-
|
|
7
|
+
<%- if watir? -%>
|
|
8
|
+
browser.goto 'https://raider-test-site.onrender.com/'
|
|
9
|
+
<%- else -%>
|
|
10
|
+
driver.get 'https://raider-test-site.onrender.com/'
|
|
11
|
+
<%- end -%>
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
it 'has no accessibility violations on the page' do
|
|
15
|
-
|
|
16
|
-
expect(browser.driver).to be_axe_clean
|
|
17
|
-
|
|
18
|
-
expect(driver).to be_axe_clean
|
|
19
|
-
|
|
15
|
+
<%- if watir? -%>
|
|
16
|
+
expect(browser.driver).to be_axe_clean
|
|
17
|
+
<%- else -%>
|
|
18
|
+
expect(driver).to be_axe_clean
|
|
19
|
+
<%- end -%>
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -63,6 +63,8 @@ describe 'Account', :logged_in do
|
|
|
63
63
|
end
|
|
64
64
|
# rubocop:enable RSpec/MultipleDescribes
|
|
65
65
|
<%- else -%>
|
|
66
|
+
# frozen_string_literal: true
|
|
67
|
+
|
|
66
68
|
<% if cross_platform? -%>
|
|
67
69
|
require_relative '../helpers/appium_helper'
|
|
68
70
|
<%- end -%>
|
|
@@ -72,7 +74,6 @@ require_relative '../page_objects/pages/pdp'
|
|
|
72
74
|
|
|
73
75
|
class PdpSpec
|
|
74
76
|
describe 'PDP' do
|
|
75
|
-
|
|
76
77
|
let(:home_page) { Home.new(driver) }
|
|
77
78
|
let(:pdp_page) { Pdp.new(driver) }
|
|
78
79
|
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
3
|
gem 'activesupport'
|
|
4
|
-
|
|
5
|
-
gem 'appium_lib'
|
|
4
|
+
<%- if mobile? -%>
|
|
6
5
|
gem 'appium_console'
|
|
7
|
-
|
|
6
|
+
gem 'appium_lib'
|
|
7
|
+
<%- end -%>
|
|
8
8
|
<%- if axe_addon? -%>
|
|
9
9
|
gem 'axe-core-rspec'
|
|
10
10
|
gem 'axe-core-selenium'
|
|
11
11
|
<%- end -%>
|
|
12
|
+
<%- if cucumber? -%>
|
|
13
|
+
gem 'cucumber'
|
|
14
|
+
<%- end -%>
|
|
12
15
|
gem 'parallel_split_test'
|
|
13
16
|
gem 'parallel_tests'
|
|
17
|
+
gem 'raider_reporter', '~> 0.12'
|
|
14
18
|
gem 'rake'
|
|
15
19
|
gem 'reek'
|
|
16
|
-
gem '
|
|
20
|
+
gem 'rspec'
|
|
17
21
|
<%- if rspec? -%>
|
|
18
22
|
gem 'rspec-retry'
|
|
19
23
|
<%- end -%>
|
|
20
|
-
gem '<%= framework %>'
|
|
21
|
-
<%- if cucumber? -%>
|
|
22
|
-
gem 'rspec'
|
|
23
|
-
<%- end -%>
|
|
24
24
|
gem 'rubocop'
|
|
25
25
|
<%- if rspec? -%>
|
|
26
26
|
gem 'rubocop-rspec'
|
|
27
27
|
<%- end -%>
|
|
28
28
|
gem 'ruby_raider', '<%= ruby_raider_version_requirement %>'
|
|
29
|
-
<%- if selenium_based?
|
|
29
|
+
<%- if selenium_based? -%>
|
|
30
30
|
gem 'selenium-webdriver'
|
|
31
31
|
<%- end -%>
|
|
32
32
|
<%- if watir? -%>
|
|
@@ -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
|
|
|
@@ -1,27 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module AppiumHelper
|
|
2
4
|
def element(opts = {})
|
|
3
|
-
|
|
5
|
+
return driver.find_element(strategy(opts) => selector(opts)) if opts[os]
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
driver.find_element(opts)
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
def elements(opts = {})
|
|
9
|
-
|
|
11
|
+
return driver.find_elements(strategy(opts) => selector(opts)) if opts[os]
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
driver.find_elements(opts)
|
|
14
|
+
end
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
def os
|
|
17
|
+
driver.appium_device
|
|
18
|
+
end
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
private
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
def strategy(opts)
|
|
23
|
+
opts[os].keys.first
|
|
24
|
+
end
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
def selector(opts)
|
|
27
|
+
opts[os][strategy(opts)]
|
|
28
|
+
end
|
|
27
29
|
end
|
|
@@ -6,7 +6,6 @@ require 'watir'
|
|
|
6
6
|
require_relative 'video_helper'
|
|
7
7
|
|
|
8
8
|
module BrowserHelper
|
|
9
|
-
|
|
10
9
|
def browser(*args)
|
|
11
10
|
@browser ||= create_browser(*args)
|
|
12
11
|
end
|
|
@@ -23,7 +22,7 @@ module BrowserHelper
|
|
|
23
22
|
args = args.empty? ? config['browser_arguments'][browser_name] : args
|
|
24
23
|
args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
|
|
25
24
|
capitalized = browser_name == 'ie' ? browser_name.upcase : browser_name.capitalize
|
|
26
|
-
browser_options = "Selenium::WebDriver::#{capitalized}::Options".constantize.new(args:)
|
|
25
|
+
browser_options = "Selenium::WebDriver::#{capitalized}::Options".constantize.new(args: args)
|
|
27
26
|
arguments = { options: browser_options }
|
|
28
27
|
listener = VideoHelper.listener
|
|
29
28
|
arguments[:listener] = listener if listener
|