ruby_raider 3.13.0 → 4.0.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 (225) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/runtime.yml +38 -0
  3. data/.github/workflows/system_tests.yml +1 -23
  4. data/.reek.yml +1 -0
  5. data/.rspec +1 -0
  6. data/Gemfile +2 -0
  7. data/README.md +16 -49
  8. data/RELEASE.md +22 -0
  9. data/bin/raider +5 -0
  10. data/lib/commands/scaffolding_commands.rb +2 -25
  11. data/lib/commands/utility_commands.rb +23 -3
  12. data/lib/generators/automation/automation_generator.rb +0 -5
  13. data/lib/generators/automation/templates/component.tt +1 -0
  14. data/lib/generators/automation/templates/home.tt +2 -2
  15. data/lib/generators/automation/templates/login.tt +6 -26
  16. data/lib/generators/automation/templates/page.tt +23 -18
  17. data/lib/generators/automation/templates/partials/home_page_selector.tt +1 -2
  18. data/lib/generators/automation/templates/partials/initialize_selector.tt +0 -8
  19. data/lib/generators/automation/templates/partials/pdp_page_selector.tt +1 -2
  20. data/lib/generators/automation/templates/partials/url_methods.tt +8 -2
  21. data/lib/generators/automation/templates/partials/visit_method.tt +0 -5
  22. data/lib/generators/automation/templates/pdp.tt +1 -9
  23. data/lib/generators/common_generator.rb +0 -2
  24. data/lib/generators/generator.rb +12 -19
  25. data/lib/generators/helper_generator.rb +21 -19
  26. data/lib/generators/infrastructure/templates/azure_pipelines.tt +1 -1
  27. data/lib/generators/infrastructure/templates/github.tt +2 -3
  28. data/lib/generators/infrastructure/templates/github_appium.tt +0 -8
  29. data/lib/generators/invoke_generators.rb +4 -23
  30. data/lib/generators/menu_generator.rb +1 -9
  31. data/lib/generators/rspec/templates/accessibility_spec.tt +1 -9
  32. data/lib/generators/rspec/templates/spec.tt +8 -24
  33. data/lib/generators/template_renderer/partial_cache.rb +3 -1
  34. data/lib/generators/template_renderer.rb +1 -1
  35. data/lib/generators/templates/common/gemfile.tt +3 -10
  36. data/lib/generators/templates/common/git_ignore.tt +0 -5
  37. data/lib/generators/templates/common/partials/mobile_config.tt +3 -1
  38. data/lib/generators/templates/common/partials/web_config.tt +10 -1
  39. data/lib/generators/templates/common/rakefile.tt +0 -15
  40. data/lib/generators/templates/common/read_me.tt +357 -250
  41. data/lib/generators/templates/common/rubocop.tt +0 -2
  42. data/lib/generators/templates/helpers/driver_helper.tt +8 -0
  43. data/lib/generators/templates/helpers/gesture_helper.tt +78 -0
  44. data/lib/generators/templates/helpers/network_helper.tt +521 -0
  45. data/lib/generators/templates/helpers/partials/quit_driver.tt +0 -2
  46. data/lib/generators/templates/helpers/partials/screenshot.tt +0 -2
  47. data/lib/generators/templates/helpers/partials/selenium_driver.tt +5 -2
  48. data/lib/generators/templates/helpers/screenshot_helper.tt +490 -0
  49. data/lib/generators/templates/helpers/spec_helper.tt +39 -29
  50. data/lib/generators/templates/helpers/trace_helper.tt +240 -95
  51. data/lib/generators/templates/helpers/video_helper.tt +3 -8
  52. data/lib/ruby_raider/runtime/actionability.rb +129 -0
  53. data/lib/ruby_raider/runtime/actionable.rb +49 -0
  54. data/lib/ruby_raider/runtime/by_role.rb +55 -0
  55. data/lib/ruby_raider/runtime/config.rb +79 -0
  56. data/lib/ruby_raider/runtime/context.rb +38 -0
  57. data/lib/ruby_raider/runtime/errors.rb +69 -0
  58. data/lib/ruby_raider/runtime/finders.rb +73 -0
  59. data/lib/ruby_raider/runtime/js/actionability.js +134 -0
  60. data/lib/ruby_raider/runtime/js/role_engine.js +315 -0
  61. data/lib/ruby_raider/runtime/locator_converter.rb +72 -0
  62. data/lib/ruby_raider/runtime/matchers/base.rb +222 -0
  63. data/lib/ruby_raider/runtime/matchers/element.rb +183 -0
  64. data/lib/ruby_raider/runtime/matchers/page.rb +71 -0
  65. data/lib/ruby_raider/runtime/matchers/presence.rb +41 -0
  66. data/lib/ruby_raider/runtime/matchers/retrying_block.rb +104 -0
  67. data/lib/ruby_raider/runtime/observer.rb +170 -0
  68. data/lib/ruby_raider/runtime/role_finder.rb +83 -0
  69. data/lib/ruby_raider/runtime/rspec.rb +78 -0
  70. data/lib/ruby_raider/runtime/selectors.rb +60 -0
  71. data/lib/ruby_raider/runtime/strict.rb +142 -0
  72. data/lib/ruby_raider/runtime/wait.rb +84 -0
  73. data/lib/ruby_raider/runtime.rb +20 -0
  74. data/lib/scaffolding/name_normalizer.rb +1 -1
  75. data/lib/scaffolding/project_detector.rb +2 -28
  76. data/lib/scaffolding/scaffolding.rb +0 -20
  77. data/lib/scaffolding/templates/component.tt +0 -14
  78. data/lib/scaffolding/templates/page_object.tt +2 -14
  79. data/lib/scaffolding/templates/spec.tt +1 -9
  80. data/lib/utilities/utilities.rb +10 -8
  81. data/lib/version +1 -1
  82. data/ruby_raider.gemspec +2 -1
  83. data/sig/commands/scaffolding_commands.rbs +0 -2
  84. data/sig/commands/utility_commands.rbs +6 -0
  85. data/sig/generators/generator.rbs +0 -4
  86. data/sig/generators/helper_generator.rbs +0 -1
  87. data/sig/generators/menu_generator.rbs +0 -1
  88. data/sig/ruby_raider/runtime/actionability.rbs +25 -0
  89. data/sig/ruby_raider/runtime/actionable.rbs +13 -0
  90. data/sig/ruby_raider/runtime/by_role.rbs +15 -0
  91. data/sig/ruby_raider/runtime/config.rbs +26 -0
  92. data/sig/ruby_raider/runtime/context.rbs +12 -0
  93. data/sig/ruby_raider/runtime/errors.rbs +36 -0
  94. data/sig/ruby_raider/runtime/finders.rbs +26 -0
  95. data/sig/ruby_raider/runtime/locator_converter.rbs +21 -0
  96. data/sig/ruby_raider/runtime/matchers/base.rbs +64 -0
  97. data/sig/ruby_raider/runtime/matchers/element.rbs +78 -0
  98. data/sig/ruby_raider/runtime/matchers/page.rbs +38 -0
  99. data/sig/ruby_raider/runtime/matchers/presence.rbs +21 -0
  100. data/sig/ruby_raider/runtime/matchers/retrying_block.rbs +27 -0
  101. data/sig/ruby_raider/runtime/observer.rbs +46 -0
  102. data/sig/ruby_raider/runtime/role_finder.rbs +18 -0
  103. data/sig/ruby_raider/runtime/selectors.rbs +21 -0
  104. data/sig/ruby_raider/runtime/strict.rbs +35 -0
  105. data/sig/ruby_raider/runtime/wait.rbs +17 -0
  106. data/sig/ruby_raider/runtime.rbs +2 -0
  107. data/sig/scaffolding/project_detector.rbs +0 -4
  108. data/sig/scaffolding/scaffolding.rbs +0 -5
  109. data/sig/utilities/utilities.rbs +2 -1
  110. data/sig/vendor/selenium_webdriver.rbs +71 -0
  111. data/spec/commands/raider_commands_spec.rb +38 -3
  112. data/spec/commands/scaffolding_commands_spec.rb +27 -0
  113. data/spec/commands/utility_commands_spec.rb +53 -0
  114. data/spec/generators/gesture_helper_spec.rb +123 -0
  115. data/spec/generators/network_helper_spec.rb +634 -0
  116. data/spec/generators/screenshot_helper_spec.rb +776 -0
  117. data/spec/generators/template_renderer_spec.rb +0 -4
  118. data/spec/generators/trace_helper_spec.rb +489 -97
  119. data/spec/generators/video_helper_spec.rb +0 -9
  120. data/spec/integration/commands/browser_update_after_creation_spec.rb +0 -8
  121. data/spec/integration/commands/scaffolding_commands_spec.rb +12 -70
  122. data/spec/integration/commands/utility_commands_spec.rb +19 -41
  123. data/spec/integration/content/azure_content_spec.rb +2 -6
  124. data/spec/integration/content/ci_content_spec.rb +1 -55
  125. data/spec/integration/content/common_content_spec.rb +1 -118
  126. data/spec/integration/content/config_content_spec.rb +34 -31
  127. data/spec/integration/content/gemfile_content_spec.rb +7 -72
  128. data/spec/integration/content/helper_content_spec.rb +105 -99
  129. data/spec/integration/content/page_content_spec.rb +85 -87
  130. data/spec/integration/content/readme_content_spec.rb +134 -75
  131. data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
  132. data/spec/integration/content/rubocop_content_spec.rb +1 -1
  133. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  134. data/spec/integration/content/test_content_spec.rb +23 -147
  135. data/spec/integration/end_to_end_features_spec.rb +1 -43
  136. data/spec/integration/end_to_end_spec.rb +319 -326
  137. data/spec/integration/generators/automation_generator_spec.rb +3 -37
  138. data/spec/integration/generators/axe_addon_spec.rb +2 -35
  139. data/spec/integration/generators/azure_generator_spec.rb +1 -1
  140. data/spec/integration/generators/common_generator_spec.rb +1 -40
  141. data/spec/integration/generators/config_features_spec.rb +1 -24
  142. data/spec/integration/generators/github_generator_spec.rb +0 -12
  143. data/spec/integration/generators/helpers_generator_spec.rb +74 -80
  144. data/spec/integration/generators/invoke_generators_spec.rb +54 -45
  145. data/spec/integration/generators/rspec_generator_spec.rb +0 -6
  146. data/spec/integration/scaffolding_e2e_spec.rb +17 -179
  147. data/spec/integration/spec_helper.rb +4 -6
  148. data/spec/integration/trace_format_checker.rb +11 -2
  149. data/spec/integration/trace_page_server.rb +72 -5
  150. data/spec/menus/menu_generator_spec.rb +14 -39
  151. data/spec/runtime/actionability_spec.rb +207 -0
  152. data/spec/runtime/actionable_spec.rb +125 -0
  153. data/spec/runtime/browser/actionability_script_browser_spec.rb +150 -0
  154. data/spec/runtime/browser/actionable_browser_spec.rb +98 -0
  155. data/spec/runtime/browser/finders_browser_spec.rb +148 -0
  156. data/spec/runtime/browser/matchers_browser_spec.rb +67 -0
  157. data/spec/runtime/browser/retrying_block_browser_spec.rb +37 -0
  158. data/spec/runtime/browser/role_engine_browser_spec.rb +184 -0
  159. data/spec/runtime/browser/strict_browser_spec.rb +38 -0
  160. data/spec/runtime/by_role_spec.rb +52 -0
  161. data/spec/runtime/config_spec.rb +79 -0
  162. data/spec/runtime/context_spec.rb +73 -0
  163. data/spec/runtime/errors_spec.rb +89 -0
  164. data/spec/runtime/finders_spec.rb +165 -0
  165. data/spec/runtime/fixtures/actionability.html +70 -0
  166. data/spec/runtime/fixtures/locators.html +46 -0
  167. data/spec/runtime/fixtures/matchers.html +33 -0
  168. data/spec/runtime/fixtures/roles.html +162 -0
  169. data/spec/runtime/matchers/base_spec.rb +165 -0
  170. data/spec/runtime/matchers/element_spec.rb +277 -0
  171. data/spec/runtime/matchers/page_spec.rb +102 -0
  172. data/spec/runtime/matchers/presence_spec.rb +59 -0
  173. data/spec/runtime/matchers/retrying_block_spec.rb +165 -0
  174. data/spec/runtime/observer_spec.rb +341 -0
  175. data/spec/runtime/role_finder_spec.rb +156 -0
  176. data/spec/runtime/role_query_spec.rb +75 -0
  177. data/spec/runtime/selectors_spec.rb +135 -0
  178. data/spec/runtime/strict_spec.rb +257 -0
  179. data/spec/runtime/support/browser.rb +48 -0
  180. data/spec/runtime/support/fake_bridge.rb +117 -0
  181. data/spec/runtime/wait_spec.rb +71 -0
  182. data/spec/scaffolding/name_normalizer_spec.rb +3 -2
  183. data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -43
  184. data/spec/system/selenium_spec.rb +1 -5
  185. data/spec/system/support/system_test_helper.rb +2 -5
  186. data/spec/utilities/headless_config_spec.rb +0 -7
  187. data/spec/utilities/utilities_spec.rb +24 -0
  188. metadata +113 -48
  189. data/lib/generators/automation/templates/header.tt +0 -9
  190. data/lib/generators/cucumber/cucumber_generator.rb +0 -58
  191. data/lib/generators/cucumber/templates/accessibility_feature.tt +0 -5
  192. data/lib/generators/cucumber/templates/accessibility_steps.tt +0 -17
  193. data/lib/generators/cucumber/templates/cucumber.tt +0 -1
  194. data/lib/generators/cucumber/templates/env.tt +0 -7
  195. data/lib/generators/cucumber/templates/feature.tt +0 -21
  196. data/lib/generators/cucumber/templates/partials/appium_env.tt +0 -26
  197. data/lib/generators/cucumber/templates/partials/driver_world.tt +0 -18
  198. data/lib/generators/cucumber/templates/partials/mobile_steps.tt +0 -20
  199. data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -120
  200. data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -48
  201. data/lib/generators/cucumber/templates/partials/watir_world.tt +0 -4
  202. data/lib/generators/cucumber/templates/partials/web_steps.tt +0 -28
  203. data/lib/generators/cucumber/templates/steps.tt +0 -5
  204. data/lib/generators/cucumber/templates/user_factory.tt +0 -15
  205. data/lib/generators/cucumber/templates/world.tt +0 -7
  206. data/lib/generators/templates/helpers/browser_helper.tt +0 -31
  207. data/lib/generators/templates/helpers/raider_element.tt +0 -159
  208. data/lib/generators/templates/helpers/wait_helper.tt +0 -611
  209. data/lib/scaffolding/templates/feature.tt +0 -6
  210. data/lib/scaffolding/templates/steps.tt +0 -19
  211. data/sig/generators/cucumber/cucumber_generator.rbs +0 -12
  212. data/spec/fixtures/watir_master/cucumber_watir/Readme.md.snapshot +0 -175
  213. data/spec/fixtures/watir_master/cucumber_watir/features/step_definitions/login_steps.rb.snapshot +0 -24
  214. data/spec/fixtures/watir_master/cucumber_watir/page_objects/abstract/page.rb.snapshot +0 -37
  215. data/spec/fixtures/watir_master/cucumber_watir/page_objects/pages/login.rb.snapshot +0 -33
  216. data/spec/fixtures/watir_master/rspec_watir/Readme.md.snapshot +0 -174
  217. data/spec/fixtures/watir_master/rspec_watir/page_objects/abstract/page.rb.snapshot +0 -37
  218. data/spec/fixtures/watir_master/rspec_watir/page_objects/pages/login.rb.snapshot +0 -33
  219. data/spec/fixtures/watir_master/rspec_watir/spec/login_page_spec.rb.snapshot +0 -49
  220. data/spec/integration/content/cucumber_reporter_content_spec.rb +0 -161
  221. data/spec/integration/generators/cucumber_generator_spec.rb +0 -100
  222. data/spec/integration/helpers/wait_helper_mobile_spec.rb +0 -281
  223. data/spec/integration/helpers/wait_helper_spec.rb +0 -679
  224. data/spec/integration/helpers/wait_helper_support.rb +0 -183
  225. data/spec/system/watir_spec.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e4d2e6fae8f8504215b558818a81660fca9652f52886c6b72f3e1d6850d01bd
4
- data.tar.gz: 3a8e5a4290303aab776bce5e6e9e8c86023e88a036b935de18a2c56ded688c8c
3
+ metadata.gz: 36caa6e3075d166277f9cafe8073ccd81f9ada8706a3df11ce93a6073b45d130
4
+ data.tar.gz: 6aeedcccd3623931f16eb8e082635f93f4946e6870ca79a7de9f03478d868034
5
5
  SHA512:
6
- metadata.gz: f0af4e104fc9cc2b0a99f0ebd4a996994f16500302e5f1cabf635569de8d404c30bf289ce4df145376fad0579b5cf1dc865cee19fd45e757954aa0ebcd4c75bc
7
- data.tar.gz: 67dd60f0a67b57a2cd09605a7aec9b91a822d198ed95c3be94d67663f6aef5b4db0d4b024ef09bd97e764396381d12c0f0b413552efde25f87fac44d57b48d88
6
+ metadata.gz: 7581c5bb57fd17624148332100f3e4c963323a02f5bff6dfda8aba500b5b478bfbbcb07741f55eb73909beca99e5a88b769a0af437820507c7b9bd3977038038
7
+ data.tar.gz: 353af9eab7da4783e38bff479bab069a35bbed7fcd57936b22eb525c443fc7b76136c5408371424690082f164d3e76334d93f093c2219c4bbe1cb8e56d49ed0a
@@ -0,0 +1,38 @@
1
+ name: Runtime
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ runtime:
7
+ name: Runtime (Ruby ${{ matrix.ruby }}, selenium ${{ matrix.selenium || 'latest' }})
8
+ runs-on: ubuntu-latest
9
+ env:
10
+ SELENIUM_VERSION: ${{ matrix.selenium }}
11
+
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby: ['3.2', '3.4']
16
+ selenium: ['4.30.0', '4.44.0', '']
17
+
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ bundler-cache: true
27
+
28
+ - name: Set up Chrome
29
+ uses: browser-actions/setup-chrome@v1
30
+
31
+ - name: Run runtime unit specs
32
+ run: bundle exec rspec spec/runtime
33
+
34
+ - name: Check the role engine syntax
35
+ run: node --check lib/ruby_raider/runtime/js/role_engine.js
36
+
37
+ - name: Run runtime browser specs
38
+ run: bundle exec rspec spec/runtime/browser --tag browser
@@ -17,7 +17,7 @@ jobs:
17
17
  bundler-cache: true
18
18
 
19
19
  selenium:
20
- name: Selenium frameworks
20
+ name: Selenium RSpec
21
21
  needs: setup
22
22
  runs-on: ubuntu-latest
23
23
  steps:
@@ -37,25 +37,3 @@ jobs:
37
37
 
38
38
  - name: Run Selenium system tests
39
39
  run: bundle exec rspec spec/system/selenium_spec.rb --format documentation
40
-
41
- watir:
42
- name: Watir frameworks (deprecated)
43
- needs: setup
44
- runs-on: ubuntu-latest
45
- steps:
46
- - name: Checkout repository
47
- uses: actions/checkout@v4
48
-
49
- - name: Set up Ruby
50
- uses: ruby/setup-ruby@v1
51
- with:
52
- ruby-version: '3.4'
53
- bundler-cache: true
54
-
55
- - name: Set up Chrome
56
- uses: browser-actions/setup-chrome@v1
57
- with:
58
- chrome-version: stable
59
-
60
- - name: Run Watir system tests
61
- run: bundle exec rspec spec/system/watir_spec.rb --format documentation
data/.reek.yml CHANGED
@@ -62,3 +62,4 @@ detectors:
62
62
  TooManyConstants:
63
63
  exclude:
64
64
  - 'RubyRaider::Ffmpeg' # pinned version, release URL and manifest constants
65
+ - 'TracePageServer' # routes, redirects and delays of the e2e page
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --tag ~browser
data/Gemfile CHANGED
@@ -3,3 +3,5 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
+
7
+ gem 'selenium-webdriver', ENV['SELENIUM_VERSION'] unless ENV['SELENIUM_VERSION'].to_s.empty?
data/README.md CHANGED
@@ -33,19 +33,17 @@ Ruby Raider is a CLI gem for scaffolding and generating UI test automation frame
33
33
 
34
34
  ### Web Testing
35
35
 
36
- | Test Framework | Selenium | Watir (deprecated) |
37
- |----------------|----------|--------------------|
38
- | RSpec | ✅ | ✅ |
39
- | Cucumber | ✅ | ✅ |
36
+ | Test Framework | Selenium |
37
+ |----------------|----------|
38
+ | RSpec | ✅ |
40
39
 
41
- Watir is deprecated and will be removed in the next major version of ruby_raider (4.0); new projects should use Selenium. It is no longer offered in the interactive menu and is available only through `-p automation:watir` until 4.0. See [Migrating from Watir](#migrating-from-watir).
40
+ ruby_raider 4.0 generates Selenium projects with RSpec only. Projects generated by 3.x (Cucumber, Watir) keep working on the 3.x line through the `ruby_raider` pin in their Gemfile (`~> 3.15` or an older `~> 3.x` requirement); 4.0 does not read or convert them.
42
41
 
43
42
  ### Mobile Testing (Appium)
44
43
 
45
44
  | Test Framework | iOS | Android | Cross-Platform |
46
45
  |----------------|-----|---------|----------------|
47
46
  | RSpec | ✅ | ✅ | ✅ |
48
- | Cucumber | ✅ | ✅ | ✅ |
49
47
 
50
48
  ### Optional Add-on (Web Only)
51
49
 
@@ -62,26 +60,22 @@ Every generated project comes with:
62
60
 
63
61
  - **Page Object Model** — Base page and component classes with example pages
64
62
  - **Test examples** — Working login tests against the [Raider Test Store](https://raider-test-site.onrender.com/)
65
- - **Raider reporting** — RSpec and Cucumber projects include [raider_reporter](https://github.com/RaiderHQ/raider_reporter), which writes results to `raider-results/` and uploads runs to your Raider Hideout server when `RAIDER_TOKEN` and `RAIDER_ENDPOINT` are set; generated CI runs the suite through `raider-reporter exec` with `RAIDER_QUARANTINE`, so quarantined tests do not fail the build; an opt-in `raider-gate` job (`RAIDER_GATE=1`) fails CI when the Raider quality gate fails
66
- - **Waiting** — generated Selenium and Appium projects wait for elements by themselves (`WaitHelper` and `RaiderElement`): `click`, `send_keys`, `text` and the rest poll until the element is ready, and `eventually { … }` retries assertions, so there is no implicit wait and no `sleep`; `timeout` in `config.yml` sets how long to wait
63
+ - **Raider reporting** — Generated projects include [raider_reporter](https://github.com/RaiderHQ/raider_reporter), which writes results to `raider-results/` and uploads runs to your Raider Hideout server when `RAIDER_TOKEN` and `RAIDER_ENDPOINT` are set; generated CI runs the suite through `raider-reporter exec` with `RAIDER_QUARANTINE`, so quarantined tests do not fail the build; an opt-in `raider-gate` job (`RAIDER_GATE=1`) fails CI when the Raider quality gate fails
64
+ - **Locators, waiting and assertions**: The runtime (`ruby_raider/runtime`), loaded by every generated project, adds waiting to Selenium and Appium themselves. Locators go beyond `id` and `css`: `label:`, `placeholder:`, `alt:`, `title:`, `text:`, `test_id:` and `by_role('button', name: 'Save')` find elements the way a person sees them, and `find_element` fails when a locator matches more than one. `find_element` and `find_elements` wait for a match, so there is no implicit wait and no `sleep`. Before an action the runtime waits until the element is ready: `click` waits until it is visible, enabled, not inert, stable and not covered; `send_keys` and `clear` until it is visible, enabled, editable and not inert; `submit` until it is visible, enabled and not inert (on native apps, displayed and enabled); `force: true` skips the checks for one call. `timeout`, `strict` and `actionability` in `config.yml` (or `RAIDER_TIMEOUT`, `RAIDER_STRICT`, `RAIDER_ACTIONABILITY`) set the rules. RSpec matchers (`have_text`, `have_value`, `have_attribute`, `have_css`, `have_xpath`, `be_visible`, `be_hidden`, `be_enabled`, `be_disabled`, `be_checked`, `have_count`, `have_title`, `have_url`, `have_element`) retry until the page matches, and a block expectation such as `expect { driver.find_element(id: 'status').text }.to eq('done')` retries with any value matcher
67
65
  - **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
- - **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
- - **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
66
+ - **Login reuse** — generated web projects (Selenium) log in once and restore the saved session for examples tagged `:logged_in`; `RAIDER_LOGIN_STATE=off|refresh` and `login_state: max_age:` control it, and the state stays in git-ignored `.raider/auth/`
67
+ - **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, 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
68
+ - **Failure trace replay** (Selenium) — 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
69
+ - **Network mocking** (Selenium) — Answer a request with your own response or block it, from the test: `mock_request('**/api/users', json: { users: [] })`, `mock_request(url, status: 500)` and `block_request('**/*.png')`, with `calls` to check what the page asked for. It uses WebDriver BiDi on Chrome, Edge and Firefox (Safari and `BIDI=off` raise `NetworkHelper::Unavailable`), handles CORS for cross-origin mocks, clears mocks after each test, and marks mocked requests in the trace
70
+ - **Screenshot comparison** (Selenium) — `match_screenshot('home')` compares the page, or an element with `element:`, with a baseline in `screenshots/baselines/<browser>/`. The first run saves the baseline; later runs fail on a difference and attach the expected, actual and diff pictures to the report and the trace. `SCREENSHOTS=update` rewrites baselines, a missing baseline fails on CI, and tolerances (`threshold`, `max_diff_pixels`, `max_diff_ratio`) go in `config/config.yml` under `screenshots:`. Generate baselines where CI runs: a Retina laptop makes 2x pictures
71
71
  - **GitHub Actions CI** — Ready-to-use test pipeline that caches ffmpeg for failure videos
72
72
  - **Rake tasks** — Default, smoke, and regression tasks with tag-based filtering
73
73
  - **Retry support** — RSpec retry with configurable retry count via `RETRY_COUNT` env var
74
- - **Re-run failed tests** — RSpec keeps `spec/examples.txt` for `--only-failures`; Cucumber projects write failed scenarios to `rerun.txt`
74
+ - **Re-run failed tests** — RSpec keeps `spec/examples.txt` for `--only-failures`
75
75
  - **Parallel execution** — parallel_tests and parallel_split_test gems included
76
76
  - **Configurable browser** — Chrome/Firefox with customizable arguments, viewport, timeout, and headless mode
77
77
  - **Linting** — RuboCop and Reek configs included
78
78
 
79
- > **Upgrading from 3.9 or earlier:** existing projects are not changed automatically. To get failure videos, generate a
80
- > new project with the same options and copy `helpers/video_helper.rb`, the video lines in `helpers/spec_helper.rb`
81
- > (RSpec) or `features/support/env.rb` (Cucumber), the `listener:` in the driver or browser helper, the `video:` block
82
- > in `config/config.yml`, and the ffmpeg cache and install steps in your CI pipeline. raider_reporter 0.10 or later
83
- > adds the inline video player in `raider-results/index.html` and a larger size limit for video attachments.
84
-
85
79
  For Appium projects, download the example [app](https://github.com/RaiderHQ/raider_test_app) and start the server:
86
80
 
87
81
  ```bash
@@ -129,34 +123,7 @@ Generate an empty skeleton, without the example pages and tests (the menu asks `
129
123
  raider new my_project -p framework:rspec automation:selenium examples:false
130
124
  ```
131
125
 
132
- The skeleton keeps the helpers, config, CI and README, and its empty suite passes; grow it with `raider g`. Watir projects always include the examples.
133
-
134
- ## Migrating from Watir
135
-
136
- Watir is deprecated; to move a Watir project to Selenium, generate a Selenium project and port your pages. The common mappings:
137
-
138
- | Watir | Selenium project (waiting layer) |
139
- |-------|----------------------------------|
140
- | `browser.goto(url)` | `driver.navigate.to(url)` |
141
- | `browser.text_field(id: 'x').set(v)` | `clear({ id: 'x' })` and `send_keys({ id: 'x' }, v)` |
142
- | `browser.button(id: 'x').click` | `click({ id: 'x' })` |
143
- | `Watir.default_timeout = n` | `timeout: n` in `config/config.yml` |
144
-
145
- ## Adopting waiting in an existing project
146
-
147
- Gem upgrades do not rewrite generated code, so existing projects keep their implicit wait until you opt in:
148
-
149
- 1. Copy both `helpers/wait_helper.rb` and `helpers/raider_element.rb` from a freshly generated project into your
150
- `helpers/` folder. Keep them side by side: `wait_helper.rb` loads the other with `require_relative 'raider_element'`.
151
- 2. Include it in `Page`, the spec helper, or the World, after loading it with `require_relative`, for example
152
- `require_relative '../../helpers/wait_helper'` at the top of `page_objects/abstract/page.rb`.
153
- 3. Set how long it waits: add `timeout: 10` to `config/config.yml`. Single-platform mobile projects (iOS or Android
154
- only) have no `config/config.yml`, so pass `timeout:` to a call, or create `config/config.yml` with that line;
155
- without either, it waits 10 seconds.
156
- 4. Remove the `implicit_wait` line.
157
- 5. Move flaky spots to `click`, `send_keys` and `eventually` first.
158
-
159
- Keep in mind that keeping the implicit wait while using the layer makes every `find` miss cost up to `timeout` seconds per poll cycle, so remove it once you have moved over.
126
+ The skeleton keeps the helpers, config, CI and README, and its empty suite passes; grow it with `raider g`.
160
127
 
161
128
  ## Commands
162
129
 
@@ -179,11 +146,9 @@ Shortcuts: `n` (new), `g` (generate), `u` (utility), `v` (version)
179
146
  ```
180
147
  raider g page [NAME] # Create a page object
181
148
  raider g spec [NAME] # Create an RSpec test
182
- raider g feature [NAME] # Create a Cucumber feature
183
- raider g steps [NAME] # Create step definitions
184
149
  raider g helper [NAME] # Create a helper class
185
150
  raider g component [NAME] # Create a component class
186
- raider g scaffold [NAME(S)] # Create page + test + steps
151
+ raider g scaffold [NAME(S)] # Create page + spec
187
152
  ```
188
153
 
189
154
  Options:
@@ -198,6 +163,8 @@ raider u url [URL] # Set default project URL
198
163
  raider u browser [BROWSER] # Set default browser
199
164
  raider u browser_options [OPTS] # Set browser arguments (e.g., no-sandbox, headless)
200
165
  raider u headless [on/off] # Toggle headless mode
166
+ raider u strict [on/off] # Toggle strict find_element (fail on multiple matches)
167
+ raider u actionability [on/off] # Toggle the actionability wait before element actions
201
168
  raider u raid # Run all tests (-p for parallel)
202
169
  raider u timeout [SECONDS] # Set test timeout
203
170
  raider u viewport [DIMENSIONS] # Set viewport size (e.g., 1920x1080)
data/RELEASE.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## Release Notes
4
4
 
5
+ ### 4.0.0
6
+
7
+ - Watir is removed: `automation:watir` is no longer a valid automation value.
8
+ - Cucumber is removed: `raider new` generates RSpec projects only and `framework:cucumber` is no longer a valid framework value.
9
+ - `raider g feature`, `raider g steps` and `raider u path --feature` are removed; `raider g scaffold` always writes a spec and a page.
10
+ - Generated projects no longer emit `cucumber_step` trace groups.
11
+ - ruby_raider now depends on selenium-webdriver >= 4.30.
12
+ - Every generated project loads `require 'ruby_raider/runtime'`, which adds waiting and actionability to Selenium and Appium themselves.
13
+ - `WaitHelper`, `RaiderElement`, `helpers/wait_helper.rb` and `helpers/raider_element.rb` are removed. Page objects call Selenium and Appium directly (`driver.find_element(...)`, `element.click`, `element.send_keys`).
14
+ - New locator keys `label`, `placeholder`, `alt`, `title`, `text` and `test_id`, and `by_role(role, name:, exact:, include_hidden:, level:)`.
15
+ - `find_element` is strict: it fails with `Raider::AmbiguousMatch` when a locator matches more than one element. `find_elements` never does.
16
+ - `find_element` and `find_elements` wait for a match (`find_elements` returns `[]` at the deadline). A missing element fails with `no such element: { id: "x" } (waited 10s)`.
17
+ - `eventually` is removed. Block-form expectations retry: `expect { value }.to eq(...)` works with any value matcher. RSpec's block matchers (`raise_error`, `change`, `output`, `throw_symbol`, `yield_*`) keep their stock behaviour, and `expect(value)` without a block does not retry.
18
+ - New RSpec matchers `have_text`, `have_value`, `have_attribute`, `be_visible`, `be_hidden`, `be_enabled`, `be_disabled`, `be_checked`, `have_count`, `have_title`, `have_url`, `have_element`, `have_css` and `have_xpath`, each with `wait:`.
19
+ - `click`, `send_keys`, `clear` and `submit` wait until the element is actionable (visible, enabled, not inert, stable, receives events). `force: true` skips the checks for one call.
20
+ - Native Appium actions wait for the element to be displayed and enabled. On XCUITest an element reported as not displayed waits the full `timeout` and then raises `Raider::NotActionable`; use `force: true` or `Raider.actionability = false` as the way out.
21
+ - `config/config.yml` gets `strict` and `actionability` next to `timeout`. `raider u strict on|off` and `raider u actionability on|off` set them, and `RAIDER_TIMEOUT`, `RAIDER_STRICT`, `RAIDER_ACTIONABILITY` and `RAIDER_QUIET` apply to every project, including single-platform mobile projects, which have no `config.yml`.
22
+ - Mobile gestures (`swipe`, `scroll`, `scroll_to`) move to `helpers/gesture_helper.rb`.
23
+ - Traces record element actions from the runtime: `click`, `send_keys`, `clear` and `submit` with their locators, `within` and `waited_ms`. `text`, `find_element` and other reads are no longer steps, and there are no `eventually` groups.
24
+ - The generated Gemfile pins `raider_reporter` `~> 1.0` (1.0.0 drops the Cucumber plugin).
25
+ - Existing projects pin `gem 'ruby_raider', '~> 3.15'` (or an older `~> 3.x`) in their Gemfile, so `bundle update` never moves them to 4.0; to upgrade, change the pin to `~> 4.0` and run `bundle update ruby_raider`.
26
+
5
27
  ### 3.7.0
6
28
 
7
29
  - Generated RSpec projects no longer include Allure: the `allure-rspec` and `allure-ruby-commons` gems, `helpers/allure_helper.rb`, the `allure-results` folder and `.gitignore` entry, and the Allure report and gh-pages steps in the GitHub Actions workflow are gone. `raider_reporter` is now the only reporter.
data/bin/raider CHANGED
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ # Read and write UTF-8 whatever the locale: a shell with no LANG (macOS GUI apps) defaults Ruby to US-ASCII,
5
+ # which broke the generator templates and any command that edits a generated file.
6
+ Encoding.default_external = Encoding::UTF_8
7
+ Encoding.default_internal = Encoding::UTF_8
8
+
4
9
  require_relative '../lib/ruby_raider'
5
10
 
6
11
  RubyRaider::Raider.start
@@ -20,14 +20,6 @@ class ScaffoldingCommands < Thor
20
20
  generate_scaffolding(name, 'page', options[:path])
21
21
  end
22
22
 
23
- desc 'feature [NAME]', 'Creates a new feature'
24
- option :path, type: :string, required: false,
25
- desc: 'The path where your feature will be created', aliases: '-p'
26
-
27
- def feature(name)
28
- generate_scaffolding(name, 'feature', options[:path])
29
- end
30
-
31
23
  desc 'spec [SPEC_NAME]', 'Creates a new spec'
32
24
  option :path, type: :string, required: false,
33
25
  desc: 'The path where your spec will be created', aliases: '-p'
@@ -44,14 +36,6 @@ class ScaffoldingCommands < Thor
44
36
  generate_scaffolding(name, 'helper', options[:path])
45
37
  end
46
38
 
47
- desc 'steps [STEPS_NAME]', 'Creates a new steps definition'
48
- option :path, type: :string, required: false,
49
- desc: 'The path where your steps will be created', aliases: '-p'
50
-
51
- def steps(name)
52
- generate_scaffolding(name, 'steps', options[:path])
53
- end
54
-
55
39
  desc 'component [NAME]', 'Creates a component inheriting from Component'
56
40
  option :path, type: :string, required: false,
57
41
  desc: 'The path where your component will be created', aliases: '-p'
@@ -60,7 +44,7 @@ class ScaffoldingCommands < Thor
60
44
  generate_scaffolding(name, 'component', options[:path])
61
45
  end
62
46
 
63
- desc 'scaffold [NAMES...]', 'Generates pages, specs/features, and helpers for one or more names'
47
+ desc 'scaffold [NAMES...]', 'Generates pages, specs, and helpers for one or more names'
64
48
 
65
49
  def scaffold(*names)
66
50
  names.each { |name| generate_default_scaffold(name) }
@@ -83,9 +67,7 @@ class ScaffoldingCommands < Thor
83
67
  say 'Warning: No Gemfile found, defaulting to selenium + rspec templates.', :yellow
84
68
  else
85
69
  automation = ScaffoldProjectDetector.detect_automation(gemfile)
86
- framework = ScaffoldProjectDetector.detect_framework(gemfile)
87
70
  say 'Warning: Could not detect automation library from Gemfile, defaulting to selenium.', :yellow if automation.nil?
88
- say 'Warning: Could not detect test framework from Gemfile, defaulting to rspec.', :yellow if framework.nil?
89
71
  end
90
72
  end
91
73
 
@@ -97,12 +79,7 @@ class ScaffoldingCommands < Thor
97
79
 
98
80
  def generate_default_scaffold(name)
99
81
  validate_project!
100
- if Pathname.new('spec').exist? && !Pathname.new('features').exist?
101
- generate_scaffolding(name, 'spec', load_config_path('spec'))
102
- else
103
- generate_scaffolding(name, 'feature', load_config_path('feature'))
104
- generate_scaffolding(name, 'steps', load_config_path('steps'))
105
- end
82
+ generate_scaffolding(name, 'spec', load_config_path('spec'))
106
83
  generate_scaffolding(name, 'page', load_config_path('page'))
107
84
  end
108
85
  end
@@ -7,8 +7,6 @@ class UtilityCommands < Thor
7
7
  def self.exit_on_failure? = true
8
8
 
9
9
  desc 'path [PATH]', 'Sets the default path for scaffolding'
10
- option :feature,
11
- type: :boolean, required: false, desc: 'The default path for your features', aliases: '-f'
12
10
  option :helper,
13
11
  type: :boolean, required: false, desc: 'The default path for your helpers', aliases: '-h'
14
12
  option :spec,
@@ -49,12 +47,28 @@ class UtilityCommands < Thor
49
47
  desc 'headless [on/off]', 'Toggles headless mode for browser tests'
50
48
 
51
49
  def headless(toggle)
52
- enabled = %w[on true 1 yes].include?(toggle.downcase)
50
+ enabled = on?(toggle)
53
51
  Utilities.headless = enabled
54
52
  state = enabled ? 'enabled' : 'disabled'
55
53
  say "Headless mode #{state}", :green
56
54
  end
57
55
 
56
+ desc 'strict [on/off]', 'Toggles strict find_element (fails when a locator matches more than one element)'
57
+
58
+ def strict(toggle)
59
+ enabled = on?(toggle)
60
+ Utilities.strict = enabled
61
+ say "Strict mode #{enabled ? 'enabled' : 'disabled'}", :green
62
+ end
63
+
64
+ desc 'actionability [on/off]', 'Toggles the actionability wait before element actions'
65
+
66
+ def actionability(toggle)
67
+ enabled = on?(toggle)
68
+ Utilities.actionability = enabled
69
+ say "Actionability #{enabled ? 'enabled' : 'disabled'}", :green
70
+ end
71
+
58
72
  desc 'raid', 'It runs all the tests in a project'
59
73
  option :parallel,
60
74
  type: :boolean, required: false, desc: 'It runs the tests in parallel', aliases: '-p'
@@ -160,4 +174,10 @@ class UtilityCommands < Thor
160
174
  say "Failure video #{enabled ? 'enabled' : 'disabled'}", :green
161
175
  end
162
176
  end
177
+
178
+ private
179
+
180
+ def on?(toggle)
181
+ %w[on true 1 yes].include?(toggle.downcase)
182
+ end
163
183
  end
@@ -17,7 +17,6 @@ class AutomationGenerator < Generator
17
17
  generate_home_page
18
18
  generate_pdp_page
19
19
  else
20
- generate_header_component
21
20
  generate_model_files
22
21
  generate_pages
23
22
  end
@@ -67,10 +66,6 @@ class AutomationGenerator < Generator
67
66
  template('pdp.tt', "#{name}/page_objects/pages/pdp.rb")
68
67
  end
69
68
 
70
- def generate_header_component
71
- template('header.tt', "#{name}/page_objects/components/header.rb")
72
- end
73
-
74
69
  def generate_model_factory
75
70
  template('factory.tt', "#{name}/models/model_factory.rb")
76
71
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Component
4
+ # `component` is a Selenium element: `component.find_element(...)` searches inside it.
4
5
  attr_reader :component
5
6
 
6
7
  def initialize(component)
@@ -6,7 +6,7 @@ class Home < Page
6
6
  # Actions
7
7
 
8
8
  def go_to_product_detail
9
- tap(first_product)
9
+ first_product.click
10
10
  end
11
11
 
12
12
  private
@@ -14,6 +14,6 @@ class Home < Page
14
14
  # Elements
15
15
 
16
16
  def first_product
17
- <%= partial('home_page_selector') -%>
17
+ <%= cross_platform? ? 'element(' : 'driver.find_element(' -%><%= partial('home_page_selector').strip -%>)
18
18
  end
19
19
  end
@@ -9,46 +9,26 @@ class Login < Page
9
9
 
10
10
  # Actions
11
11
 
12
- <%- if selenium_based? -%>
13
12
  def login(username, password)
14
- send_keys(username_field, username)
15
- send_keys(password_field, password)
16
- click(login_button)
17
- end
18
- alias log_as login
19
- <%- else -%>
20
- def login(username, password)
21
- username_field.set username
22
- password_field.set password
13
+ username_field.send_keys(username)
14
+ password_field.send_keys(password)
23
15
  login_button.click
24
16
  end
25
- <%- end -%>
17
+ alias log_as login
26
18
 
27
19
  private
28
20
 
29
21
  # Elements
30
22
 
31
23
  def username_field
32
- <%- if selenium_based? -%>
33
- { id: 'loginFrm_loginname' }
34
- <%- else -%>
35
- browser.text_field(id: 'loginFrm_loginname')
36
- <%- end -%>
24
+ driver.find_element(id: 'loginFrm_loginname')
37
25
  end
38
26
 
39
27
  def password_field
40
- <%- if selenium_based? -%>
41
- { id: 'loginFrm_password' }
42
- <%- else -%>
43
- browser.text_field(id: 'loginFrm_password')
44
- <%- end -%>
28
+ driver.find_element(id: 'loginFrm_password')
45
29
  end
46
30
 
47
31
  def login_button
48
- <%- if selenium_based? -%>
49
- { xpath: "//button[@title='Login']" }
50
- <%- else -%>
51
- browser.button(xpath: "//button[@title='Login']")
52
- <%- end -%>
32
+ driver.find_element(by_role('button', name: 'Login'))
53
33
  end
54
34
  end
@@ -1,22 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- <%- if web? && examples? -%>
4
- require_relative '../components/header'
3
+ require 'ruby_raider/runtime'
4
+ <%- if cross_platform? -%>
5
+ require_relative '../../helpers/appium_helper'
6
+ <%- end -%>
7
+ <%- if mobile? -%>
8
+ require_relative '../../helpers/gesture_helper'
9
+ <%- end -%>
10
+ <%- if network_helper? -%>
11
+ require_relative '../../helpers/network_helper'
5
12
  <%- end -%>
6
- <%- if wait_helper? -%>
7
- require_relative '../../helpers/wait_helper'
13
+ <%- if screenshot_helper? -%>
14
+ require_relative '../../helpers/screenshot_helper'
8
15
  <%- end -%>
9
16
 
10
17
  class Page
11
18
  <%- if cross_platform? -%>
12
19
  include AppiumHelper
13
20
  <%- end -%>
14
- <%- if wait_helper? -%>
15
- include WaitHelper
21
+ include Raider::Helpers
22
+ <%- if mobile? -%>
23
+ include GestureHelper
16
24
  <%- end -%>
17
- <%- if cross_platform? || wait_helper? -%>
18
-
25
+ <%- if network_helper? -%>
26
+ include NetworkHelper
27
+ <%- end -%>
28
+ <%- if screenshot_helper? -%>
29
+ include ScreenshotHelper
19
30
  <%- end -%>
31
+
20
32
  <%= partial('initialize_selector') -%>
21
33
  <%= partial('visit_method') -%>
22
34
  <%= partial('url_methods') -%>
@@ -26,17 +38,10 @@ class Page
26
38
  end
27
39
  <%- if selenium_based? && examples? -%>
28
40
 
29
- # Components
30
-
31
- def header
32
- Header.new(find_element(css: '.welcome-msg'))
33
- end
34
- <%- elsif watir? -%>
35
-
36
- # Components
41
+ # Elements
37
42
 
38
- def header
39
- Header.new(browser.element(css: '.welcome-msg'))
43
+ def welcome_message
44
+ driver.find_element(css: '.welcome-msg')
40
45
  end
41
46
  <%- end -%>
42
47
  end
@@ -1,6 +1,5 @@
1
1
  <%- if cross_platform? -%>
2
- { ios: { accessibility_id: 'product-item-1' },
3
- android: { accessibility_id: 'product-item-1' } }
2
+ { ios: { accessibility_id: 'product-item-1' }, android: { accessibility_id: 'product-item-1' } }
4
3
  <%- else -%>
5
4
  { accessibility_id: 'product-item-1' }
6
5
  <%- end -%>
@@ -1,13 +1,5 @@
1
- <%- if watir? -%>
2
- attr_reader :browser
3
-
4
- def initialize(browser)
5
- @browser = browser
6
- end
7
- <%- else -%>
8
1
  attr_reader :driver
9
2
 
10
3
  def initialize(driver)
11
4
  @driver = driver
12
5
  end
13
- <%- end -%>
@@ -1,6 +1,5 @@
1
1
  <%- if cross_platform? -%>
2
- { ios: { accessibility_id: 'add-to-cart-button' },
3
- android: { accessibility_id: 'add-to-cart-button' } }
2
+ { ios: { accessibility_id: 'add-to-cart-button' }, android: { accessibility_id: 'add-to-cart-button' } }
4
3
  <%- else -%>
5
4
  { accessibility_id: 'add-to-cart-button' }
6
5
  <%- end -%>
@@ -1,11 +1,17 @@
1
1
  <%- if web? -%>
2
2
 
3
3
  def full_url(*page)
4
- "#{base_url}#{url(*page)}"
4
+ path = url(*page).to_s
5
+ base = base_url
6
+ base.empty? ? path : "#{base}#{path.delete_prefix('/')}"
5
7
  end
6
8
 
9
+ # The configured url with a trailing slash, so page paths never run into its last segment.
7
10
  def base_url
8
- YAML.load_file('config/config.yml')['url']
11
+ base = YAML.load_file('config/config.yml')['url'].to_s
12
+ return base if base.empty? || base.end_with?('/')
13
+
14
+ "#{base}/"
9
15
  end
10
16
 
11
17
  def url(_page)
@@ -3,9 +3,4 @@
3
3
  def visit(*page)
4
4
  @driver.navigate.to full_url(page.first)
5
5
  end
6
- <%- elsif watir? -%>
7
-
8
- def visit(*page)
9
- @browser.goto full_url(page.first)
10
- end
11
6
  <%- end -%>
@@ -3,17 +3,9 @@
3
3
  require_relative '../abstract/page'
4
4
 
5
5
  class Pdp < Page
6
- # Actions
7
-
8
- def add_to_cart_text
9
- text(add_to_cart_button)
10
- end
11
-
12
- private
13
-
14
6
  # Elements
15
7
 
16
8
  def add_to_cart_button
17
- <%= partial('pdp_page_selector') -%>
9
+ <%= cross_platform? ? 'element(' : 'driver.find_element(' -%><%= partial('pdp_page_selector').strip -%>)
18
10
  end
19
11
  end
@@ -34,8 +34,6 @@ class CommonGenerator < Generator
34
34
  end
35
35
 
36
36
  def generate_rspec_file
37
- return unless rspec?
38
-
39
37
  template('common/rspec.tt', "#{name}/.rspec")
40
38
  end
41
39
  end