ruby_raider 3.10.0 → 3.12.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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/reek.yml +5 -1
  3. data/.github/workflows/release.yml +2 -1
  4. data/.github/workflows/system_tests.yml +1 -1
  5. data/.reek.yml +13 -0
  6. data/README.md +44 -4
  7. data/lib/commands/utility_commands.rb +1 -1
  8. data/lib/generators/automation/automation_generator.rb +15 -10
  9. data/lib/generators/automation/templates/appium_caps.tt +2 -4
  10. data/lib/generators/automation/templates/home.tt +1 -2
  11. data/lib/generators/automation/templates/login.tt +6 -6
  12. data/lib/generators/automation/templates/page.tt +10 -3
  13. data/lib/generators/automation/templates/partials/home_page_selector.tt +4 -6
  14. data/lib/generators/automation/templates/partials/pdp_page_selector.tt +4 -6
  15. data/lib/generators/automation/templates/pdp.tt +3 -2
  16. data/lib/generators/cucumber/cucumber_generator.rb +15 -2
  17. data/lib/generators/cucumber/templates/feature.tt +5 -0
  18. data/lib/generators/cucumber/templates/partials/driver_world.tt +9 -0
  19. data/lib/generators/cucumber/templates/partials/mobile_steps.tt +1 -1
  20. data/lib/generators/cucumber/templates/partials/selenium_env.tt +20 -0
  21. data/lib/generators/cucumber/templates/partials/watir_env.tt +16 -0
  22. data/lib/generators/cucumber/templates/partials/web_steps.tt +8 -0
  23. data/lib/generators/generator.rb +15 -4
  24. data/lib/generators/helper_generator.rb +15 -0
  25. data/lib/generators/invoke_generators.rb +23 -0
  26. data/lib/generators/menu_generator.rb +26 -14
  27. data/lib/generators/rspec/rspec_generator.rb +10 -3
  28. data/lib/generators/rspec/templates/spec.tt +27 -1
  29. data/lib/generators/template_renderer/template_error.rb +8 -8
  30. data/lib/generators/templates/common/git_ignore.tt +4 -1
  31. data/lib/generators/templates/common/partials/mobile_config.tt +1 -0
  32. data/lib/generators/templates/common/partials/web_config.tt +5 -2
  33. data/lib/generators/templates/common/read_me.tt +508 -0
  34. data/lib/generators/templates/helpers/partials/selenium_driver.tt +1 -3
  35. data/lib/generators/templates/helpers/raider_element.tt +159 -0
  36. data/lib/generators/templates/helpers/session_state.tt +246 -0
  37. data/lib/generators/templates/helpers/spec_helper.tt +28 -0
  38. data/lib/generators/templates/helpers/wait_helper.tt +470 -0
  39. data/lib/ruby_raider/ffmpeg.rb +7 -7
  40. data/lib/ruby_raider.rb +2 -0
  41. data/lib/scaffolding/project_detector.rb +6 -0
  42. data/lib/scaffolding/scaffolding.rb +4 -0
  43. data/lib/scaffolding/templates/component.tt +4 -0
  44. data/lib/scaffolding/templates/page_object.tt +8 -0
  45. data/lib/version +1 -1
  46. data/ruby_raider.gemspec +1 -0
  47. data/sig/generators/generator.rbs +2 -0
  48. data/sig/scaffolding/project_detector.rbs +1 -0
  49. data/sig/scaffolding/scaffolding.rbs +1 -0
  50. data/spec/commands/raider_commands_spec.rb +24 -0
  51. data/spec/fixtures/watir_master/cucumber_watir/Readme.md.snapshot +175 -0
  52. data/spec/fixtures/watir_master/cucumber_watir/features/step_definitions/login_steps.rb.snapshot +24 -0
  53. data/spec/fixtures/watir_master/cucumber_watir/page_objects/abstract/page.rb.snapshot +39 -0
  54. data/spec/fixtures/watir_master/cucumber_watir/page_objects/pages/login.rb.snapshot +33 -0
  55. data/spec/fixtures/watir_master/rspec_watir/Readme.md.snapshot +174 -0
  56. data/spec/fixtures/watir_master/rspec_watir/page_objects/abstract/page.rb.snapshot +39 -0
  57. data/spec/fixtures/watir_master/rspec_watir/page_objects/pages/login.rb.snapshot +33 -0
  58. data/spec/fixtures/watir_master/rspec_watir/spec/login_page_spec.rb.snapshot +49 -0
  59. data/spec/integration/commands/scaffolding_commands_spec.rb +49 -0
  60. data/spec/integration/commands/utility_commands_spec.rb +8 -0
  61. data/spec/integration/content/azure_content_spec.rb +4 -0
  62. data/spec/integration/content/ci_content_spec.rb +11 -0
  63. data/spec/integration/content/common_content_spec.rb +27 -0
  64. data/spec/integration/content/config_content_spec.rb +62 -0
  65. data/spec/integration/content/cucumber_reporter_content_spec.rb +25 -0
  66. data/spec/integration/content/helper_content_spec.rb +111 -0
  67. data/spec/integration/content/page_content_spec.rb +75 -4
  68. data/spec/integration/content/readme_content_spec.rb +149 -0
  69. data/spec/integration/content/syntax_validation_spec.rb +38 -0
  70. data/spec/integration/content/test_content_spec.rb +82 -0
  71. data/spec/integration/end_to_end_spec.rb +73 -28
  72. data/spec/integration/generators/automation_generator_spec.rb +50 -0
  73. data/spec/integration/generators/common_generator_spec.rb +20 -0
  74. data/spec/integration/generators/config_features_spec.rb +2 -2
  75. data/spec/integration/generators/cucumber_generator_spec.rb +36 -0
  76. data/spec/integration/generators/helpers_generator_spec.rb +60 -0
  77. data/spec/integration/generators/invoke_generators_spec.rb +70 -0
  78. data/spec/integration/generators/rspec_generator_spec.rb +20 -0
  79. data/spec/integration/helpers/session_state_spec.rb +474 -0
  80. data/spec/integration/helpers/wait_helper_mobile_spec.rb +281 -0
  81. data/spec/integration/helpers/wait_helper_spec.rb +496 -0
  82. data/spec/integration/helpers/wait_helper_support.rb +183 -0
  83. data/spec/integration/settings_helper.rb +4 -1
  84. data/spec/integration/spec_helper.rb +2 -2
  85. data/spec/menus/menu_generator_spec.rb +64 -2
  86. data/spec/scaffolding/scaffold_project_detector_spec.rb +12 -0
  87. metadata +31 -2
@@ -0,0 +1,175 @@
1
+ # Ruby Raider
2
+
3
+ <!-- PROJECT LOGO -->
4
+ <br />
5
+ <div align="center">
6
+ <a href="https://github.com/RubyRaider/ruby_raider">
7
+ <img src="https://www.ruby-raider.com/assets/icon-DYY74ofR.png" alt="Logo" style="width:200px;">
8
+ </a>
9
+ <p align="center">
10
+ <a href="https://github.com/RubyRaider/ruby_raider#getting-started"><strong>Explore the docs</strong></a>
11
+ <br />
12
+ <br />
13
+ <a href="https://rubygems.org/gems/ruby_raider">Rubygems</a>
14
+ ·
15
+ <a href="https://github.com/RubyRaider/ruby_raider/issues">Report Bug</a>
16
+ ·
17
+ <a href="https://github.com/RubyRaider/ruby_raider/issues">Request Feature</a>
18
+ </p>
19
+ <p align="center"> For more information and updates on releases, see <a href="https://ruby-raider.com">https://ruby-raider.com</a></p>
20
+ </div>
21
+
22
+ > **Deprecated:** Watir is deprecated and will be removed in the next major version of ruby_raider; new projects should use Selenium. Watir is only available through `-p automation:watir`. See [Migrating from Watir](https://github.com/RaiderHQ/ruby_raider#migrating-from-watir).
23
+
24
+ ## What is Ruby Raider?
25
+
26
+ Ruby Raider is a generator and scaffolding gem to make UI test automation easier.
27
+
28
+ ### Supported Frameworks
29
+
30
+ | Web Testing Framework | Mobile Testing Framework |
31
+ |-----------------------|------------------------------------|
32
+ | Cucumber and Selenium | Cucumber and Appium for iOS |
33
+ | RSpec and Selenium | RSpec and Appium for iOS |
34
+ | Cucumber and Watir | Cucumber and Appium for Android |
35
+ | RSpec and Watir | RSpec and Appium for Android |
36
+ | | Cucumber and Appium Cross-platform |
37
+ | | RSpec and Appium Cross-platform |
38
+
39
+ **Optional add-on for web projects:**
40
+ - **Accessibility testing (axe)**: `--accessibility` flag
41
+
42
+ ***The web tests run against the [Raider Test Store](https://raider-test-site.onrender.com/).***
43
+
44
+ ***To run Appium tests, download the example [app](https://github.com/RaiderHQ/raider_test_app) and start the server:***
45
+
46
+ ```bash
47
+ raider u start_appium
48
+ ```
49
+
50
+ This works on all platforms (Mac OS, Linux and Windows).
51
+
52
+ ## Getting Started
53
+
54
+ ```bash
55
+ gem install ruby_raider
56
+ raider new [name_of_project]
57
+ ```
58
+
59
+ Or with parameters:
60
+
61
+ ```bash
62
+ raider new [name_of_project] -p framework:[framework] automation:[automation_type]
63
+ ```
64
+
65
+ ### Commands
66
+
67
+ ```
68
+ raider generate # Scaffolding commands
69
+ raider utility # Utility commands
70
+ raider new [NAME] # Create new project
71
+ raider version # Show version
72
+ raider help [COMMAND] # Describe commands
73
+ ```
74
+
75
+ Shortcuts: `g` (generate), `n` (new), `u` (utility), `v` (version)
76
+
77
+ ### Scaffolding
78
+
79
+ ```
80
+ raider g page [NAME] # Create page object
81
+ raider g spec [NAME] # Create RSpec test
82
+ raider g feature [NAME] # Create Cucumber feature
83
+ raider g steps [NAME] # Create step definitions
84
+ raider g scaffold [NAME] # Create page + test + steps
85
+ ```
86
+
87
+ ## Reusing the login
88
+
89
+ Tag an example `:logged_in` (RSpec) or a scenario `@logged_in` (Cucumber) and the suite logs in once, saves the
90
+ session, and restores it for the next tagged example or run instead of filling in the login form again. The generated
91
+ account example shows it. If the saved session no longer works, the real login runs again and the state is saved anew.
92
+
93
+ For another role, call it yourself with its own name:
94
+
95
+ ```ruby
96
+ SessionState.new(driver, name: 'admin').reuse(valid: -> { dashboard.tap(&:visit).admin? }) do
97
+ # the real login for the admin
98
+ end
99
+ ```
100
+
101
+ With Watir, pass `browser.driver`, the Selenium driver underneath the browser.
102
+
103
+ - `login_state: max_age:` in `config/config.yml` is how many seconds a saved login stays valid (default 3600).
104
+ - `RAIDER_LOGIN_STATE=off` always does the real login and saves nothing; `RAIDER_LOGIN_STATE=refresh` ignores the saved
105
+ file, logs in and saves again.
106
+ - The state lives in `.raider/auth/` and is git-ignored. It holds live session tokens, so it must not be committed,
107
+ cached or uploaded as a CI artifact. Nothing in it is ever printed.
108
+
109
+ ## Reporting
110
+
111
+ Every run writes machine-readable results to `raider-results/` (including `run.json`) and a browsable report,
112
+ `raider-results/index.html`. The reporter runs in plugin mode:
113
+ `features/support/env.rb` requires `raider_reporter/cucumber`, so no extra Cucumber formatter flag is needed.
114
+
115
+ To upload runs to Raider, set these environment variables before running the suite.
116
+ `RAIDER_TOKEN` and `RAIDER_ENDPOINT` come from your Raider Hideout server (its setup page, or `bin/rails hideout:setup` /
117
+ `hideout:token`):
118
+
119
+ ```bash
120
+ export RAIDER_TOKEN=your-token
121
+ export RAIDER_ENDPOINT=https://hideout.example.com
122
+ ```
123
+
124
+ Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. Failure screenshots are attached to the
125
+ report and to Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details.
126
+
127
+ ### Video replay
128
+
129
+ Every test is recorded, and the video is kept only for failing tests. It is attached
130
+ to the report as `video` and saved under `raider-results/attachments/`, next to the failure screenshot.
131
+ The video is a `video.webm` built from page screenshots with ffmpeg.
132
+
133
+ - Video is on by default: `video:` in `config/config.yml` (`enabled: true`, `fps: 5`, `max_width: 1280`).
134
+ - Turn it off for one run with `VIDEO=off bundle exec cucumber`, or for the project with
135
+ `raider u video off` (`raider u video on` turns it back on).
136
+ - ffmpeg is downloaded on first use, checked against a pinned SHA-256 and cached in `~/.cache/raider`
137
+ (`%LOCALAPPDATA%\raider` on Windows). `raider u video install` downloads it ahead of time (CI does this before the
138
+ tests and caches the folder), and `raider u video status` prints the binary in use.
139
+ - `RAIDER_FFMPEG_PATH` uses your own ffmpeg (it needs libvpx) instead of the download, and `RAIDER_CACHE_DIR` moves
140
+ the cache.
141
+ - The RaiderHQ `ffmpeg-7.1` release that the download comes from is not published yet. Until it is, set
142
+ `RAIDER_FFMPEG_PATH` to get videos; without it the suite runs as usual and prints one `[video]` line.
143
+ - If ffmpeg is missing or recording fails, the suite still runs and its exit code is unchanged; you get one `[video]`
144
+ line on stderr and no video.
145
+ - Known limits: a frame is taken after each page load, click and text change, plus one at the end of a failing test,
146
+ so changes the page makes on its own between actions show up in the next frame. There is one recorded driver per
147
+ process.
148
+
149
+ ### Quarantine
150
+
151
+ CI runs the suite through `bundle exec raider-reporter exec -- …` with `RAIDER_QUARANTINE` set. Tests quarantined in
152
+ Raider still run and are still reported (as `quarantined`), and a build whose only failures are quarantined stays green.
153
+ Local runs keep quarantine off. To turn it off in CI, remove `RAIDER_QUARANTINE` from the pipeline.
154
+
155
+ ### Quality gate
156
+
157
+ The `raider-gate` CI job runs `bundle exec raider-reporter gate` after the tests and
158
+ fails the pipeline when the Raider quality gate fails. It is off until you turn it on:
159
+
160
+ - set the CI variable `RAIDER_GATE=1` (a repository variable under Settings → Secrets and variables → Actions → Variables);
161
+ - turn on rules under the project's Gate page in Raider: a minimum pass rate, no new failures against the default
162
+ branch, no new flaky tests;
163
+ - optionally require the `raider/gate` check in branch protection.
164
+
165
+ `raider-reporter gate` exits 1 when the gate fails, naming the failed rules, and 2 when it gets no verdict (timeout,
166
+ Raider unreachable, token rejected, build not found). `RAIDER_GATE_TIMEOUT` sets how long it waits, in seconds
167
+ (default 600). The job stays off by default because a project without `RAIDER_TOKEN`, or a pull request from a fork,
168
+ would exit 2 on every run.
169
+
170
+ Parallel test jobs that upload to one build must each set `RAIDER_SHARD_TOTAL` (the number of jobs) and
171
+ `RAIDER_SHARD_INDEX` (1 to that number), so the gate waits for all of them instead of judging the first one to finish.
172
+
173
+ ### Sponsors
174
+
175
+ This project is tested with BrowserStack.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../page_objects/pages/account'
4
+ require_relative '../../page_objects/pages/login'
5
+
6
+ Given("I'm a {user} on the login page") do |user|
7
+ @user = user
8
+ @login_page = Login.new(browser)
9
+ @login_page.visit
10
+ end
11
+
12
+ Given("I'm a {user}") do |user|
13
+ @user = user
14
+ end
15
+
16
+ When('I login with my credentials') do
17
+ @login_page.login(@user['username'], @user['password'])
18
+ end
19
+
20
+ Then("I'm logged in") do
21
+ account_page = Account.new(browser)
22
+ account_page.visit
23
+ expect(account_page.header.customer_name).to eq "Welcome back #{@user['name']}"
24
+ end
@@ -0,0 +1,39 @@
1
+ require_relative '../components/header'
2
+
3
+ class Page
4
+
5
+ attr_reader :browser
6
+
7
+ def initialize(browser)
8
+ @browser = browser
9
+ end
10
+
11
+
12
+ def visit(*page)
13
+ @browser.goto full_url(page.first)
14
+ end
15
+
16
+
17
+
18
+ def full_url(*page)
19
+ "#{base_url}#{url(*page)}"
20
+ end
21
+
22
+ def base_url
23
+ YAML.load_file('config/config.yml')['url']
24
+ end
25
+
26
+ def url(_page)
27
+ raise 'Url must be defined on child pages'
28
+ end
29
+
30
+ def to_s
31
+ self.class.to_s.sub('Page', ' Page')
32
+ end
33
+
34
+ # Components
35
+
36
+ def header
37
+ Header.new(browser.element(css: '.welcome-msg'))
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../abstract/page'
4
+
5
+ class Login < Page
6
+ def url(_page)
7
+ 'index.php?rt=account/login'
8
+ end
9
+
10
+ # Actions
11
+
12
+ def login(username, password)
13
+ username_field.set username
14
+ password_field.set password
15
+ login_button.click
16
+ end
17
+
18
+ private
19
+
20
+ # Elements
21
+
22
+ def username_field
23
+ browser.text_field(id: 'loginFrm_loginname')
24
+ end
25
+
26
+ def password_field
27
+ browser.text_field(id: 'loginFrm_password')
28
+ end
29
+
30
+ def login_button
31
+ browser.button(xpath: "//button[@title='Login']")
32
+ end
33
+ end
@@ -0,0 +1,174 @@
1
+ # Ruby Raider
2
+
3
+ <!-- PROJECT LOGO -->
4
+ <br />
5
+ <div align="center">
6
+ <a href="https://github.com/RubyRaider/ruby_raider">
7
+ <img src="https://www.ruby-raider.com/assets/icon-DYY74ofR.png" alt="Logo" style="width:200px;">
8
+ </a>
9
+ <p align="center">
10
+ <a href="https://github.com/RubyRaider/ruby_raider#getting-started"><strong>Explore the docs</strong></a>
11
+ <br />
12
+ <br />
13
+ <a href="https://rubygems.org/gems/ruby_raider">Rubygems</a>
14
+ ·
15
+ <a href="https://github.com/RubyRaider/ruby_raider/issues">Report Bug</a>
16
+ ·
17
+ <a href="https://github.com/RubyRaider/ruby_raider/issues">Request Feature</a>
18
+ </p>
19
+ <p align="center"> For more information and updates on releases, see <a href="https://ruby-raider.com">https://ruby-raider.com</a></p>
20
+ </div>
21
+
22
+ > **Deprecated:** Watir is deprecated and will be removed in the next major version of ruby_raider; new projects should use Selenium. Watir is only available through `-p automation:watir`. See [Migrating from Watir](https://github.com/RaiderHQ/ruby_raider#migrating-from-watir).
23
+
24
+ ## What is Ruby Raider?
25
+
26
+ Ruby Raider is a generator and scaffolding gem to make UI test automation easier.
27
+
28
+ ### Supported Frameworks
29
+
30
+ | Web Testing Framework | Mobile Testing Framework |
31
+ |-----------------------|------------------------------------|
32
+ | Cucumber and Selenium | Cucumber and Appium for iOS |
33
+ | RSpec and Selenium | RSpec and Appium for iOS |
34
+ | Cucumber and Watir | Cucumber and Appium for Android |
35
+ | RSpec and Watir | RSpec and Appium for Android |
36
+ | | Cucumber and Appium Cross-platform |
37
+ | | RSpec and Appium Cross-platform |
38
+
39
+ **Optional add-on for web projects:**
40
+ - **Accessibility testing (axe)**: `--accessibility` flag
41
+
42
+ ***The web tests run against the [Raider Test Store](https://raider-test-site.onrender.com/).***
43
+
44
+ ***To run Appium tests, download the example [app](https://github.com/RaiderHQ/raider_test_app) and start the server:***
45
+
46
+ ```bash
47
+ raider u start_appium
48
+ ```
49
+
50
+ This works on all platforms (Mac OS, Linux and Windows).
51
+
52
+ ## Getting Started
53
+
54
+ ```bash
55
+ gem install ruby_raider
56
+ raider new [name_of_project]
57
+ ```
58
+
59
+ Or with parameters:
60
+
61
+ ```bash
62
+ raider new [name_of_project] -p framework:[framework] automation:[automation_type]
63
+ ```
64
+
65
+ ### Commands
66
+
67
+ ```
68
+ raider generate # Scaffolding commands
69
+ raider utility # Utility commands
70
+ raider new [NAME] # Create new project
71
+ raider version # Show version
72
+ raider help [COMMAND] # Describe commands
73
+ ```
74
+
75
+ Shortcuts: `g` (generate), `n` (new), `u` (utility), `v` (version)
76
+
77
+ ### Scaffolding
78
+
79
+ ```
80
+ raider g page [NAME] # Create page object
81
+ raider g spec [NAME] # Create RSpec test
82
+ raider g feature [NAME] # Create Cucumber feature
83
+ raider g steps [NAME] # Create step definitions
84
+ raider g scaffold [NAME] # Create page + test + steps
85
+ ```
86
+
87
+ ## Reusing the login
88
+
89
+ Tag an example `:logged_in` (RSpec) or a scenario `@logged_in` (Cucumber) and the suite logs in once, saves the
90
+ session, and restores it for the next tagged example or run instead of filling in the login form again. The generated
91
+ account example shows it. If the saved session no longer works, the real login runs again and the state is saved anew.
92
+
93
+ For another role, call it yourself with its own name:
94
+
95
+ ```ruby
96
+ SessionState.new(driver, name: 'admin').reuse(valid: -> { dashboard.tap(&:visit).admin? }) do
97
+ # the real login for the admin
98
+ end
99
+ ```
100
+
101
+ With Watir, pass `browser.driver`, the Selenium driver underneath the browser.
102
+
103
+ - `login_state: max_age:` in `config/config.yml` is how many seconds a saved login stays valid (default 3600).
104
+ - `RAIDER_LOGIN_STATE=off` always does the real login and saves nothing; `RAIDER_LOGIN_STATE=refresh` ignores the saved
105
+ file, logs in and saves again.
106
+ - The state lives in `.raider/auth/` and is git-ignored. It holds live session tokens, so it must not be committed,
107
+ cached or uploaded as a CI artifact. Nothing in it is ever printed.
108
+
109
+ ## Reporting
110
+
111
+ Every run writes machine-readable results to `raider-results/` (including `run.json`) and a browsable report,
112
+ `raider-results/index.html`. Open it in a browser after a run; `bundle exec raider-reporter report` rebuilds it.
113
+
114
+ To upload runs to Raider, set these environment variables before running the suite.
115
+ `RAIDER_TOKEN` and `RAIDER_ENDPOINT` come from your Raider Hideout server (its setup page, or `bin/rails hideout:setup` /
116
+ `hideout:token`):
117
+
118
+ ```bash
119
+ export RAIDER_TOKEN=your-token
120
+ export RAIDER_ENDPOINT=https://hideout.example.com
121
+ ```
122
+
123
+ Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. Failure screenshots are attached to the
124
+ report and to Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details.
125
+
126
+ ### Video replay
127
+
128
+ Every test is recorded, and the video is kept only for failing tests (on the last retry attempt). It is attached
129
+ to the report as `video` and saved under `raider-results/attachments/`, next to the failure screenshot.
130
+ The video is a `video.webm` built from page screenshots with ffmpeg.
131
+
132
+ - Video is on by default: `video:` in `config/config.yml` (`enabled: true`, `fps: 5`, `max_width: 1280`).
133
+ - Turn it off for one run with `VIDEO=off bundle exec rspec`, or for the project with
134
+ `raider u video off` (`raider u video on` turns it back on).
135
+ - ffmpeg is downloaded on first use, checked against a pinned SHA-256 and cached in `~/.cache/raider`
136
+ (`%LOCALAPPDATA%\raider` on Windows). `raider u video install` downloads it ahead of time (CI does this before the
137
+ tests and caches the folder), and `raider u video status` prints the binary in use.
138
+ - `RAIDER_FFMPEG_PATH` uses your own ffmpeg (it needs libvpx) instead of the download, and `RAIDER_CACHE_DIR` moves
139
+ the cache.
140
+ - The RaiderHQ `ffmpeg-7.1` release that the download comes from is not published yet. Until it is, set
141
+ `RAIDER_FFMPEG_PATH` to get videos; without it the suite runs as usual and prints one `[video]` line.
142
+ - If ffmpeg is missing or recording fails, the suite still runs and its exit code is unchanged; you get one `[video]`
143
+ line on stderr and no video.
144
+ - Known limits: a frame is taken after each page load, click and text change, plus one at the end of a failing test,
145
+ so changes the page makes on its own between actions show up in the next frame. There is one recorded driver per
146
+ process.
147
+
148
+ ### Quarantine
149
+
150
+ CI runs the suite through `bundle exec raider-reporter exec -- …` with `RAIDER_QUARANTINE` set. Tests quarantined in
151
+ Raider still run and are still reported (as `quarantined`), and a build whose only failures are quarantined stays green.
152
+ Local runs keep quarantine off. To turn it off in CI, remove `RAIDER_QUARANTINE` from the pipeline.
153
+
154
+ ### Quality gate
155
+
156
+ The `raider-gate` CI job runs `bundle exec raider-reporter gate` after the tests and
157
+ fails the pipeline when the Raider quality gate fails. It is off until you turn it on:
158
+
159
+ - set the CI variable `RAIDER_GATE=1` (a repository variable under Settings → Secrets and variables → Actions → Variables);
160
+ - turn on rules under the project's Gate page in Raider: a minimum pass rate, no new failures against the default
161
+ branch, no new flaky tests;
162
+ - optionally require the `raider/gate` check in branch protection.
163
+
164
+ `raider-reporter gate` exits 1 when the gate fails, naming the failed rules, and 2 when it gets no verdict (timeout,
165
+ Raider unreachable, token rejected, build not found). `RAIDER_GATE_TIMEOUT` sets how long it waits, in seconds
166
+ (default 600). The job stays off by default because a project without `RAIDER_TOKEN`, or a pull request from a fork,
167
+ would exit 2 on every run.
168
+
169
+ Parallel test jobs that upload to one build must each set `RAIDER_SHARD_TOTAL` (the number of jobs) and
170
+ `RAIDER_SHARD_INDEX` (1 to that number), so the gate waits for all of them instead of judging the first one to finish.
171
+
172
+ ### Sponsors
173
+
174
+ This project is tested with BrowserStack.
@@ -0,0 +1,39 @@
1
+ require_relative '../components/header'
2
+
3
+ class Page
4
+
5
+ attr_reader :browser
6
+
7
+ def initialize(browser)
8
+ @browser = browser
9
+ end
10
+
11
+
12
+ def visit(*page)
13
+ @browser.goto full_url(page.first)
14
+ end
15
+
16
+
17
+
18
+ def full_url(*page)
19
+ "#{base_url}#{url(*page)}"
20
+ end
21
+
22
+ def base_url
23
+ YAML.load_file('config/config.yml')['url']
24
+ end
25
+
26
+ def url(_page)
27
+ raise 'Url must be defined on child pages'
28
+ end
29
+
30
+ def to_s
31
+ self.class.to_s.sub('Page', ' Page')
32
+ end
33
+
34
+ # Components
35
+
36
+ def header
37
+ Header.new(browser.element(css: '.welcome-msg'))
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../abstract/page'
4
+
5
+ class Login < Page
6
+ def url(_page)
7
+ 'index.php?rt=account/login'
8
+ end
9
+
10
+ # Actions
11
+
12
+ def login(username, password)
13
+ username_field.set username
14
+ password_field.set password
15
+ login_button.click
16
+ end
17
+
18
+ private
19
+
20
+ # Elements
21
+
22
+ def username_field
23
+ browser.text_field(id: 'loginFrm_loginname')
24
+ end
25
+
26
+ def password_field
27
+ browser.text_field(id: 'loginFrm_password')
28
+ end
29
+
30
+ def login_button
31
+ browser.button(xpath: "//button[@title='Login']")
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/spec_helper'
4
+ require_relative '../models/model_factory'
5
+ require_relative '../page_objects/pages/account'
6
+ require_relative '../page_objects/pages/login'
7
+
8
+ # rubocop:disable RSpec/MultipleDescribes
9
+ describe 'Login' do
10
+ subject(:header) { account_page.header.customer_name }
11
+
12
+ let(:user) { ModelFactory.for('users')['registered user'] }
13
+ let(:login_page) { Login.new(browser) }
14
+ let(:account_page) { Account.new(browser) }
15
+
16
+ before do
17
+ login_page.visit
18
+ login_page.login(user['username'], password)
19
+ end
20
+
21
+ context 'with right credentials' do
22
+ let(:password) { user['password'] }
23
+
24
+ it 'can successfully log in' do
25
+ account_page.visit
26
+ expect(header).to eq "Welcome back #{user['name']}"
27
+ end
28
+ end
29
+
30
+ context 'with wrong credentials' do
31
+ let(:password) { 'wrongPassword' }
32
+
33
+ it 'cannot log in' do
34
+ login_page.visit
35
+ expect(header).to eq 'Login or register'
36
+ end
37
+ end
38
+ end
39
+
40
+ describe 'Account', :logged_in do
41
+ let(:user) { ModelFactory.for('users')['registered user'] }
42
+ let(:account_page) { Account.new(browser) }
43
+
44
+ it 'opens the account page with the saved login' do
45
+ account_page.visit
46
+ expect(account_page.header.customer_name).to eq "Welcome back #{user['name']}"
47
+ end
48
+ end
49
+ # rubocop:enable RSpec/MultipleDescribes
@@ -94,4 +94,53 @@ describe ScaffoldingCommands do
94
94
  expect(Pathname.new("#{new_path}/#{name}_helper.rb")).to be_file
95
95
  end
96
96
  end
97
+
98
+ describe 'waiting examples' do
99
+ let(:tmp_dir) { 'tmp_scaffold_wait_helper_test' }
100
+ let(:selenium_example) { "driver.find_element(id: 'element_id')" }
101
+ let(:waiting_example) { "click({ id: 'element_id' })" }
102
+
103
+ def scaffold_in_project(gem_name, kind, wait_helper:)
104
+ FileUtils.mkdir_p("#{tmp_dir}/helpers")
105
+ File.write("#{tmp_dir}/Gemfile", "gem 'rspec'\ngem '#{gem_name}'\n")
106
+ File.write("#{tmp_dir}/helpers/wait_helper.rb", '') if wait_helper
107
+ Dir.chdir(tmp_dir) { scaffold.new.invoke(kind, nil, %w[foo]) }
108
+ end
109
+
110
+ after do
111
+ Dir.chdir orig_dir
112
+ FileUtils.rm_rf(tmp_dir)
113
+ end
114
+
115
+ it 'shows the waiting example in a page when the project has the wait helper' do
116
+ scaffold_in_project('selenium-webdriver', :page, wait_helper: true)
117
+ content = File.read("#{tmp_dir}/page_objects/pages/foo.rb")
118
+ expect(content).to include(waiting_example)
119
+ expect(content).not_to include(selenium_example)
120
+ end
121
+
122
+ it 'keeps the driver example in a page without the wait helper' do
123
+ scaffold_in_project('selenium-webdriver', :page, wait_helper: false)
124
+ content = File.read("#{tmp_dir}/page_objects/pages/foo.rb")
125
+ expect(content).to include(selenium_example)
126
+ expect(content).not_to include(waiting_example)
127
+ end
128
+
129
+ it 'shows the waiting example in a component when the project has the wait helper' do
130
+ scaffold_in_project('selenium-webdriver', :component, wait_helper: true)
131
+ expect(File.read("#{tmp_dir}/page_objects/components/foo.rb")).to include("component.find_element(css: '.selector').click")
132
+ end
133
+
134
+ it 'keeps the driver example in a component without the wait helper' do
135
+ scaffold_in_project('selenium-webdriver', :component, wait_helper: false)
136
+ expect(File.read("#{tmp_dir}/page_objects/components/foo.rb")).to include("driver.find_element(css: '.selector')")
137
+ end
138
+
139
+ it 'leaves Watir output unchanged even with a wait helper present' do
140
+ scaffold_in_project('watir', :page, wait_helper: true)
141
+ content = File.read("#{tmp_dir}/page_objects/pages/foo.rb")
142
+ expect(content).to include("browser.text_field(id: 'element_id')")
143
+ expect(content).not_to include(waiting_example)
144
+ end
145
+ end
97
146
  end
@@ -110,4 +110,12 @@ describe UtilityCommands do
110
110
  expect(config.dig('browser_arguments', 'firefox')).to eq([])
111
111
  end
112
112
  end
113
+
114
+ # Generated drivers connect to appium_lib's default, http://127.0.0.1:4723, with no base path.
115
+ it 'starts Appium on the default path the generated drivers connect to' do
116
+ command = utility.new
117
+ allow(command).to receive(:system)
118
+ command.start_appium
119
+ expect(command).to have_received(:system).with('appium')
120
+ end
113
121
  end
@@ -124,6 +124,10 @@ describe 'Azure Pipelines content' do
124
124
  expect(raw).not_to match(/allure/i)
125
125
  end
126
126
 
127
+ it 'does not mention the saved login state' do
128
+ expect(raw).not_to include('.raider')
129
+ end
130
+
127
131
  it 'documents Azure Pipelines in the README and no GitHub Actions setup' do
128
132
  readme = read_generated(name, 'Readme.md')
129
133
  expect(readme).to include('## Azure Pipelines', 'RAIDER_TOKEN', 'Contribute to pull requests',
@@ -319,4 +319,15 @@ describe 'CI pipeline content' do
319
319
  include_examples 'github raider-gate job', name, 'appium_pipeline.yml', test_jobs
320
320
  end
321
321
  end
322
+
323
+ context 'with the saved login state' do
324
+ workflows = FRAMEWORKS.product(%w[selenium watir]).map { |f, a| ["#{f}_#{a}", 'test_pipeline.yml'] } +
325
+ FRAMEWORKS.product(%w[android ios cross_platform]).map { |f, a| ["#{f}_#{a}", 'appium_pipeline.yml'] }
326
+
327
+ workflows.each do |name, file|
328
+ it "keeps .raider/auth out of #{name}/#{file}" do
329
+ expect(read_generated(name, ".github/workflows/#{file}")).not_to include('.raider')
330
+ end
331
+ end
332
+ end
322
333
  end