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.
- checksums.yaml +4 -4
- data/.github/workflows/runtime.yml +38 -0
- data/.github/workflows/system_tests.yml +1 -23
- data/.reek.yml +1 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/README.md +16 -49
- data/RELEASE.md +22 -0
- data/bin/raider +5 -0
- data/lib/commands/scaffolding_commands.rb +2 -25
- data/lib/commands/utility_commands.rb +23 -3
- data/lib/generators/automation/automation_generator.rb +0 -5
- data/lib/generators/automation/templates/component.tt +1 -0
- data/lib/generators/automation/templates/home.tt +2 -2
- data/lib/generators/automation/templates/login.tt +6 -26
- data/lib/generators/automation/templates/page.tt +23 -18
- data/lib/generators/automation/templates/partials/home_page_selector.tt +1 -2
- data/lib/generators/automation/templates/partials/initialize_selector.tt +0 -8
- data/lib/generators/automation/templates/partials/pdp_page_selector.tt +1 -2
- data/lib/generators/automation/templates/partials/url_methods.tt +8 -2
- data/lib/generators/automation/templates/partials/visit_method.tt +0 -5
- data/lib/generators/automation/templates/pdp.tt +1 -9
- data/lib/generators/common_generator.rb +0 -2
- data/lib/generators/generator.rb +12 -19
- data/lib/generators/helper_generator.rb +21 -19
- data/lib/generators/infrastructure/templates/azure_pipelines.tt +1 -1
- data/lib/generators/infrastructure/templates/github.tt +2 -3
- data/lib/generators/infrastructure/templates/github_appium.tt +0 -8
- data/lib/generators/invoke_generators.rb +4 -23
- data/lib/generators/menu_generator.rb +1 -9
- data/lib/generators/rspec/templates/accessibility_spec.tt +1 -9
- data/lib/generators/rspec/templates/spec.tt +8 -24
- data/lib/generators/template_renderer/partial_cache.rb +3 -1
- data/lib/generators/template_renderer.rb +1 -1
- data/lib/generators/templates/common/gemfile.tt +3 -10
- data/lib/generators/templates/common/git_ignore.tt +0 -5
- data/lib/generators/templates/common/partials/mobile_config.tt +3 -1
- data/lib/generators/templates/common/partials/web_config.tt +10 -1
- data/lib/generators/templates/common/rakefile.tt +0 -15
- data/lib/generators/templates/common/read_me.tt +357 -250
- data/lib/generators/templates/common/rubocop.tt +0 -2
- data/lib/generators/templates/helpers/driver_helper.tt +8 -0
- data/lib/generators/templates/helpers/gesture_helper.tt +78 -0
- data/lib/generators/templates/helpers/network_helper.tt +521 -0
- data/lib/generators/templates/helpers/partials/quit_driver.tt +0 -2
- data/lib/generators/templates/helpers/partials/screenshot.tt +0 -2
- data/lib/generators/templates/helpers/partials/selenium_driver.tt +5 -2
- data/lib/generators/templates/helpers/screenshot_helper.tt +490 -0
- data/lib/generators/templates/helpers/spec_helper.tt +39 -29
- data/lib/generators/templates/helpers/trace_helper.tt +240 -95
- data/lib/generators/templates/helpers/video_helper.tt +3 -8
- data/lib/ruby_raider/runtime/actionability.rb +129 -0
- data/lib/ruby_raider/runtime/actionable.rb +49 -0
- data/lib/ruby_raider/runtime/by_role.rb +55 -0
- data/lib/ruby_raider/runtime/config.rb +79 -0
- data/lib/ruby_raider/runtime/context.rb +38 -0
- data/lib/ruby_raider/runtime/errors.rb +69 -0
- data/lib/ruby_raider/runtime/finders.rb +73 -0
- data/lib/ruby_raider/runtime/js/actionability.js +134 -0
- data/lib/ruby_raider/runtime/js/role_engine.js +315 -0
- data/lib/ruby_raider/runtime/locator_converter.rb +72 -0
- data/lib/ruby_raider/runtime/matchers/base.rb +222 -0
- data/lib/ruby_raider/runtime/matchers/element.rb +183 -0
- data/lib/ruby_raider/runtime/matchers/page.rb +71 -0
- data/lib/ruby_raider/runtime/matchers/presence.rb +41 -0
- data/lib/ruby_raider/runtime/matchers/retrying_block.rb +104 -0
- data/lib/ruby_raider/runtime/observer.rb +170 -0
- data/lib/ruby_raider/runtime/role_finder.rb +83 -0
- data/lib/ruby_raider/runtime/rspec.rb +78 -0
- data/lib/ruby_raider/runtime/selectors.rb +60 -0
- data/lib/ruby_raider/runtime/strict.rb +142 -0
- data/lib/ruby_raider/runtime/wait.rb +84 -0
- data/lib/ruby_raider/runtime.rb +20 -0
- data/lib/scaffolding/name_normalizer.rb +1 -1
- data/lib/scaffolding/project_detector.rb +2 -28
- data/lib/scaffolding/scaffolding.rb +0 -20
- data/lib/scaffolding/templates/component.tt +0 -14
- data/lib/scaffolding/templates/page_object.tt +2 -14
- data/lib/scaffolding/templates/spec.tt +1 -9
- data/lib/utilities/utilities.rb +10 -8
- data/lib/version +1 -1
- data/ruby_raider.gemspec +2 -1
- data/sig/commands/scaffolding_commands.rbs +0 -2
- data/sig/commands/utility_commands.rbs +6 -0
- data/sig/generators/generator.rbs +0 -4
- data/sig/generators/helper_generator.rbs +0 -1
- data/sig/generators/menu_generator.rbs +0 -1
- data/sig/ruby_raider/runtime/actionability.rbs +25 -0
- data/sig/ruby_raider/runtime/actionable.rbs +13 -0
- data/sig/ruby_raider/runtime/by_role.rbs +15 -0
- data/sig/ruby_raider/runtime/config.rbs +26 -0
- data/sig/ruby_raider/runtime/context.rbs +12 -0
- data/sig/ruby_raider/runtime/errors.rbs +36 -0
- data/sig/ruby_raider/runtime/finders.rbs +26 -0
- data/sig/ruby_raider/runtime/locator_converter.rbs +21 -0
- data/sig/ruby_raider/runtime/matchers/base.rbs +64 -0
- data/sig/ruby_raider/runtime/matchers/element.rbs +78 -0
- data/sig/ruby_raider/runtime/matchers/page.rbs +38 -0
- data/sig/ruby_raider/runtime/matchers/presence.rbs +21 -0
- data/sig/ruby_raider/runtime/matchers/retrying_block.rbs +27 -0
- data/sig/ruby_raider/runtime/observer.rbs +46 -0
- data/sig/ruby_raider/runtime/role_finder.rbs +18 -0
- data/sig/ruby_raider/runtime/selectors.rbs +21 -0
- data/sig/ruby_raider/runtime/strict.rbs +35 -0
- data/sig/ruby_raider/runtime/wait.rbs +17 -0
- data/sig/ruby_raider/runtime.rbs +2 -0
- data/sig/scaffolding/project_detector.rbs +0 -4
- data/sig/scaffolding/scaffolding.rbs +0 -5
- data/sig/utilities/utilities.rbs +2 -1
- data/sig/vendor/selenium_webdriver.rbs +71 -0
- data/spec/commands/raider_commands_spec.rb +38 -3
- data/spec/commands/scaffolding_commands_spec.rb +27 -0
- data/spec/commands/utility_commands_spec.rb +53 -0
- data/spec/generators/gesture_helper_spec.rb +123 -0
- data/spec/generators/network_helper_spec.rb +634 -0
- data/spec/generators/screenshot_helper_spec.rb +776 -0
- data/spec/generators/template_renderer_spec.rb +0 -4
- data/spec/generators/trace_helper_spec.rb +489 -97
- data/spec/generators/video_helper_spec.rb +0 -9
- data/spec/integration/commands/browser_update_after_creation_spec.rb +0 -8
- data/spec/integration/commands/scaffolding_commands_spec.rb +12 -70
- data/spec/integration/commands/utility_commands_spec.rb +19 -41
- data/spec/integration/content/azure_content_spec.rb +2 -6
- data/spec/integration/content/ci_content_spec.rb +1 -55
- data/spec/integration/content/common_content_spec.rb +1 -118
- data/spec/integration/content/config_content_spec.rb +34 -31
- data/spec/integration/content/gemfile_content_spec.rb +7 -72
- data/spec/integration/content/helper_content_spec.rb +105 -99
- data/spec/integration/content/page_content_spec.rb +85 -87
- data/spec/integration/content/readme_content_spec.rb +134 -75
- data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
- data/spec/integration/content/rubocop_content_spec.rb +1 -1
- data/spec/integration/content/syntax_validation_spec.rb +2 -2
- data/spec/integration/content/test_content_spec.rb +23 -147
- data/spec/integration/end_to_end_features_spec.rb +1 -43
- data/spec/integration/end_to_end_spec.rb +319 -326
- data/spec/integration/generators/automation_generator_spec.rb +3 -37
- data/spec/integration/generators/axe_addon_spec.rb +2 -35
- data/spec/integration/generators/azure_generator_spec.rb +1 -1
- data/spec/integration/generators/common_generator_spec.rb +1 -40
- data/spec/integration/generators/config_features_spec.rb +1 -24
- data/spec/integration/generators/github_generator_spec.rb +0 -12
- data/spec/integration/generators/helpers_generator_spec.rb +74 -80
- data/spec/integration/generators/invoke_generators_spec.rb +54 -45
- data/spec/integration/generators/rspec_generator_spec.rb +0 -6
- data/spec/integration/scaffolding_e2e_spec.rb +17 -179
- data/spec/integration/spec_helper.rb +4 -6
- data/spec/integration/trace_format_checker.rb +11 -2
- data/spec/integration/trace_page_server.rb +72 -5
- data/spec/menus/menu_generator_spec.rb +14 -39
- data/spec/runtime/actionability_spec.rb +207 -0
- data/spec/runtime/actionable_spec.rb +125 -0
- data/spec/runtime/browser/actionability_script_browser_spec.rb +150 -0
- data/spec/runtime/browser/actionable_browser_spec.rb +98 -0
- data/spec/runtime/browser/finders_browser_spec.rb +148 -0
- data/spec/runtime/browser/matchers_browser_spec.rb +67 -0
- data/spec/runtime/browser/retrying_block_browser_spec.rb +37 -0
- data/spec/runtime/browser/role_engine_browser_spec.rb +184 -0
- data/spec/runtime/browser/strict_browser_spec.rb +38 -0
- data/spec/runtime/by_role_spec.rb +52 -0
- data/spec/runtime/config_spec.rb +79 -0
- data/spec/runtime/context_spec.rb +73 -0
- data/spec/runtime/errors_spec.rb +89 -0
- data/spec/runtime/finders_spec.rb +165 -0
- data/spec/runtime/fixtures/actionability.html +70 -0
- data/spec/runtime/fixtures/locators.html +46 -0
- data/spec/runtime/fixtures/matchers.html +33 -0
- data/spec/runtime/fixtures/roles.html +162 -0
- data/spec/runtime/matchers/base_spec.rb +165 -0
- data/spec/runtime/matchers/element_spec.rb +277 -0
- data/spec/runtime/matchers/page_spec.rb +102 -0
- data/spec/runtime/matchers/presence_spec.rb +59 -0
- data/spec/runtime/matchers/retrying_block_spec.rb +165 -0
- data/spec/runtime/observer_spec.rb +341 -0
- data/spec/runtime/role_finder_spec.rb +156 -0
- data/spec/runtime/role_query_spec.rb +75 -0
- data/spec/runtime/selectors_spec.rb +135 -0
- data/spec/runtime/strict_spec.rb +257 -0
- data/spec/runtime/support/browser.rb +48 -0
- data/spec/runtime/support/fake_bridge.rb +117 -0
- data/spec/runtime/wait_spec.rb +71 -0
- data/spec/scaffolding/name_normalizer_spec.rb +3 -2
- data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -43
- data/spec/system/selenium_spec.rb +1 -5
- data/spec/system/support/system_test_helper.rb +2 -5
- data/spec/utilities/headless_config_spec.rb +0 -7
- data/spec/utilities/utilities_spec.rb +24 -0
- metadata +113 -48
- data/lib/generators/automation/templates/header.tt +0 -9
- data/lib/generators/cucumber/cucumber_generator.rb +0 -58
- data/lib/generators/cucumber/templates/accessibility_feature.tt +0 -5
- data/lib/generators/cucumber/templates/accessibility_steps.tt +0 -17
- data/lib/generators/cucumber/templates/cucumber.tt +0 -1
- data/lib/generators/cucumber/templates/env.tt +0 -7
- data/lib/generators/cucumber/templates/feature.tt +0 -21
- data/lib/generators/cucumber/templates/partials/appium_env.tt +0 -26
- data/lib/generators/cucumber/templates/partials/driver_world.tt +0 -18
- data/lib/generators/cucumber/templates/partials/mobile_steps.tt +0 -20
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -120
- data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -48
- data/lib/generators/cucumber/templates/partials/watir_world.tt +0 -4
- data/lib/generators/cucumber/templates/partials/web_steps.tt +0 -28
- data/lib/generators/cucumber/templates/steps.tt +0 -5
- data/lib/generators/cucumber/templates/user_factory.tt +0 -15
- data/lib/generators/cucumber/templates/world.tt +0 -7
- data/lib/generators/templates/helpers/browser_helper.tt +0 -31
- data/lib/generators/templates/helpers/raider_element.tt +0 -159
- data/lib/generators/templates/helpers/wait_helper.tt +0 -611
- data/lib/scaffolding/templates/feature.tt +0 -6
- data/lib/scaffolding/templates/steps.tt +0 -19
- data/sig/generators/cucumber/cucumber_generator.rbs +0 -12
- data/spec/fixtures/watir_master/cucumber_watir/Readme.md.snapshot +0 -175
- data/spec/fixtures/watir_master/cucumber_watir/features/step_definitions/login_steps.rb.snapshot +0 -24
- data/spec/fixtures/watir_master/cucumber_watir/page_objects/abstract/page.rb.snapshot +0 -37
- data/spec/fixtures/watir_master/cucumber_watir/page_objects/pages/login.rb.snapshot +0 -33
- data/spec/fixtures/watir_master/rspec_watir/Readme.md.snapshot +0 -174
- data/spec/fixtures/watir_master/rspec_watir/page_objects/abstract/page.rb.snapshot +0 -37
- data/spec/fixtures/watir_master/rspec_watir/page_objects/pages/login.rb.snapshot +0 -33
- data/spec/fixtures/watir_master/rspec_watir/spec/login_page_spec.rb.snapshot +0 -49
- data/spec/integration/content/cucumber_reporter_content_spec.rb +0 -161
- data/spec/integration/generators/cucumber_generator_spec.rb +0 -100
- data/spec/integration/helpers/wait_helper_mobile_spec.rb +0 -281
- data/spec/integration/helpers/wait_helper_spec.rb +0 -679
- data/spec/integration/helpers/wait_helper_support.rb +0 -183
- data/spec/system/watir_spec.rb +0 -38
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative '../support/browser'
|
|
6
|
+
|
|
7
|
+
RSpec.describe 'Raider finders in a real browser', :browser do
|
|
8
|
+
include Raider::Helpers
|
|
9
|
+
|
|
10
|
+
let(:driver) { Raider::Spec::Browser.driver }
|
|
11
|
+
|
|
12
|
+
before(:all) { Raider::Spec::Browser.open('locators.html') } # rubocop:disable RSpec/BeforeAfterAll
|
|
13
|
+
|
|
14
|
+
after(:all) { Raider::Spec::Browser.quit } # rubocop:disable RSpec/BeforeAfterAll
|
|
15
|
+
|
|
16
|
+
def id_of(element)
|
|
17
|
+
element.attribute('id')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe 'label:' do
|
|
21
|
+
it 'finds a field through label for' do
|
|
22
|
+
expect(id_of(driver.find_element(label: 'Password'))).to eq('password')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'finds a field inside a wrapping label' do
|
|
26
|
+
expect(id_of(driver.find_element(label: 'Username'))).to eq('username')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'finds a field by aria-label' do
|
|
30
|
+
expect(id_of(driver.find_element(label: 'Search site'))).to eq('search')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'finds a field by aria-labelledby' do
|
|
34
|
+
expect(id_of(driver.find_element(label: 'Phone number'))).to eq('phone')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'matches a substring and skips the longer label under exact: true' do
|
|
38
|
+
expect(id_of(driver.find_element(label: 'Email address'))).to eq('email-address')
|
|
39
|
+
expect(id_of(driver.find_element(label: ['Email address', { exact: true }]))).to eq('email-address')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'skips the longer label when the exact label is scoped' do
|
|
43
|
+
scope = driver.find_element(id: 'scope')
|
|
44
|
+
expect(id_of(scope.find_element(label: ['Email', { exact: true }]))).to eq('scoped-email')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'treats a substring as ambiguous and exact as unique' do
|
|
48
|
+
expect { driver.find_element(label: 'Email') }.to raise_error(Raider::AmbiguousMatch)
|
|
49
|
+
expect(driver.find_elements(label: 'Email').size).to eq(3)
|
|
50
|
+
expect(driver.find_elements(label: ['Email', { exact: true }]).size).to eq(2)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'scopes to the section' do
|
|
54
|
+
scope = driver.find_element(id: 'scope')
|
|
55
|
+
expect(id_of(scope.find_element(label: 'Email'))).to eq('scoped-email')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'raises NoSuchElementError when nothing matches' do
|
|
59
|
+
expect { driver.find_element(label: 'Nope') }.to raise_error(Selenium::WebDriver::Error::NoSuchElementError)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe 'placeholder:' do
|
|
64
|
+
it 'matches a substring' do
|
|
65
|
+
expect(driver.find_elements(placeholder: 'Enter').size).to eq(2)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'matches exactly' do
|
|
69
|
+
expect(id_of(driver.find_element(placeholder: ['Enter code', { exact: true }]))).to eq('code')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'finds an image by alt' do
|
|
74
|
+
expect(id_of(driver.find_element(alt: 'Company logo'))).to eq('logo')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'finds a link by title' do
|
|
78
|
+
expect(id_of(driver.find_element(title: 'Read the docs'))).to eq('docs-link')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe 'text:' do
|
|
82
|
+
it 'returns the innermost element, not its ancestor' do
|
|
83
|
+
expect(id_of(driver.find_element(text: 'Sign in'))).to eq('sign-in')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'ignores the title and script elements that contain the text' do
|
|
87
|
+
expect(driver.find_elements(text: 'Sign in').size).to eq(1)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'raises AmbiguousMatch naming three buttons' do
|
|
91
|
+
expect { driver.find_element(text: 'Save') }.to raise_error(Raider::AmbiguousMatch) { |error|
|
|
92
|
+
expect(error.message).to include('matched 3 elements', '(+0 more)')
|
|
93
|
+
expect(error.message).to include('<button id="save-a" class="primary">Save</button>',
|
|
94
|
+
'<button id="save-b" class="secondary">Save</button>')
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'find_elements is never strict' do
|
|
99
|
+
expect(driver.find_elements(text: 'Save').size).to eq(3)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe 'test_id:' do
|
|
104
|
+
it 'matches the data-testid value exactly' do
|
|
105
|
+
expect(driver.find_elements(test_id: 'item').size).to eq(0)
|
|
106
|
+
expect(id_of(driver.find_element(test_id: 'item-1'))).to eq('item-one')
|
|
107
|
+
expect(id_of(driver.find_element(test_id: 'item-2'))).to eq('item-two')
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe 'by_role' do
|
|
112
|
+
before(:all) { Raider::Spec::Browser.open('roles.html') } # rubocop:disable RSpec/BeforeAfterAll
|
|
113
|
+
|
|
114
|
+
after(:all) { Raider::Spec::Browser.open('locators.html') } # rubocop:disable RSpec/BeforeAfterAll
|
|
115
|
+
|
|
116
|
+
it 'scopes to a shadow root and leaves the document search blind to it' do
|
|
117
|
+
host = driver.find_element(id: 'shadow-host')
|
|
118
|
+
expect(host.shadow_root.find_element(by_role('button', name: 'Shadow inner')).text).to eq('Shadow inner')
|
|
119
|
+
expect(driver.find_elements(by_role('button', name: 'Shadow inner'))).to be_empty # documented engine gap
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'filters headings by level' do
|
|
123
|
+
expect(driver.find_element(by_role('heading', level: 1)).text).to eq('Roles')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'finds a role inside the scope element' do
|
|
127
|
+
scope = driver.find_element(id: 'scope')
|
|
128
|
+
button = scope.find_element(by_role('button', name: 'Save'))
|
|
129
|
+
expect(driver.execute_script('return arguments[0].closest("#scope") !== null', button)).to be(true)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'is strict at the page level when two buttons match' do
|
|
133
|
+
expect { driver.find_element(by_role('button', name: 'Save')) }.to raise_error(Raider::AmbiguousMatch)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe 'with a BiDi session' do
|
|
138
|
+
let(:bidi_driver) { Raider::Spec::Browser.bidi_driver }
|
|
139
|
+
|
|
140
|
+
before(:all) { Raider::Spec::Browser.bidi_driver.navigate.to(Raider::Spec::Browser.fixture_url('locators.html')) } # rubocop:disable RSpec/BeforeAfterAll
|
|
141
|
+
|
|
142
|
+
after(:all) { Raider::Spec::Browser.quit_bidi } # rubocop:disable RSpec/BeforeAfterAll
|
|
143
|
+
|
|
144
|
+
it 'finds a field by label through the BiDi bridge' do
|
|
145
|
+
expect(id_of(bidi_driver.find_element(label: 'Password'))).to eq('password')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative '../../../lib/ruby_raider/runtime/rspec'
|
|
6
|
+
require_relative '../support/browser'
|
|
7
|
+
|
|
8
|
+
RSpec.describe 'Raider matchers in a real browser', :browser do
|
|
9
|
+
include Raider::Matchers
|
|
10
|
+
|
|
11
|
+
let(:driver) { Raider::Spec::Browser.driver }
|
|
12
|
+
let(:heading) { driver.find_element(id: 'heading') }
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
Raider.timeout = 2
|
|
16
|
+
Raider::Spec::Browser.open('matchers.html')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
after do
|
|
20
|
+
Raider.reset
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
after(:all) { Raider::Spec::Browser.quit } # rubocop:disable RSpec/BeforeAfterAll
|
|
24
|
+
|
|
25
|
+
it 'waits for the heading text to change' do
|
|
26
|
+
expect(heading).to have_text('Ready')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'waits for a button to hide after a click' do
|
|
30
|
+
button = driver.find_element(id: 'hide-me')
|
|
31
|
+
button.click
|
|
32
|
+
expect(button).to be_hidden
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'reads an input value and its attributes' do
|
|
36
|
+
field = driver.find_element(id: 'name')
|
|
37
|
+
expect(field).to have_value('Ann')
|
|
38
|
+
expect(field).to have_attribute('type', 'text')
|
|
39
|
+
expect(field).to be_enabled
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'waits for the list to grow to three items' do
|
|
43
|
+
expect { driver.find_elements(css: 'li') }.to have_count(3)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'waits for the title and url after a click' do
|
|
47
|
+
driver.find_element(id: 'finish').click
|
|
48
|
+
expect(driver).to have_title('Done')
|
|
49
|
+
expect(driver).to have_url(/#done$/)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'passes a negated matcher once the text has moved on' do
|
|
53
|
+
expect(heading).not_to have_text('Loading')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'works in a plain class that mixes in both matcher modules' do
|
|
57
|
+
world = Class.new do
|
|
58
|
+
include RSpec::Matchers
|
|
59
|
+
include Raider::Matchers
|
|
60
|
+
|
|
61
|
+
def check(element)
|
|
62
|
+
expect(element).to have_text('Ready', wait: 2)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
expect(world.new.check(heading)).to be(true)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative '../../../lib/ruby_raider/runtime/rspec'
|
|
6
|
+
require_relative '../support/browser'
|
|
7
|
+
|
|
8
|
+
RSpec.describe 'Raider block expectations in a real browser', :browser do
|
|
9
|
+
include Raider::Matchers
|
|
10
|
+
|
|
11
|
+
let(:driver) { Raider::Spec::Browser.driver }
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
Raider.reset
|
|
15
|
+
Raider.timeout = 3
|
|
16
|
+
Raider::Spec::Browser.open('actionability.html')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
after { Raider.reset }
|
|
20
|
+
|
|
21
|
+
after(:all) { Raider::Spec::Browser.quit } # rubocop:disable RSpec/BeforeAfterAll
|
|
22
|
+
|
|
23
|
+
it 'waits for text set after 300 ms' do
|
|
24
|
+
driver.execute_script(<<~JS)
|
|
25
|
+
document.body.insertAdjacentHTML('beforeend', '<p id="status">pending</p>');
|
|
26
|
+
setTimeout(() => { document.getElementById('status').textContent = 'done'; }, 300);
|
|
27
|
+
JS
|
|
28
|
+
expect { driver.find_element(id: 'status').text }.to eq('done')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'finds an element inserted after 300 ms through have_element' do
|
|
32
|
+
driver.execute_script(<<~JS)
|
|
33
|
+
setTimeout(() => document.body.insertAdjacentHTML('beforeend', '<p id="late-p">x</p>'), 300);
|
|
34
|
+
JS
|
|
35
|
+
expect(driver).to have_element(id: 'late-p')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative '../support/browser'
|
|
6
|
+
|
|
7
|
+
RSpec.describe 'The in-page role engine', :browser do
|
|
8
|
+
let(:driver) { Raider::Spec::Browser.driver }
|
|
9
|
+
|
|
10
|
+
before(:all) { Raider::Spec::Browser.open('roles.html') } # rubocop:disable RSpec/BeforeAfterAll
|
|
11
|
+
|
|
12
|
+
after(:all) { Raider::Spec::Browser.quit } # rubocop:disable RSpec/BeforeAfterAll
|
|
13
|
+
|
|
14
|
+
def engine(body, *args)
|
|
15
|
+
driver.execute_script("#{Raider::RoleFinder::ENGINE}\n#{body}", *args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def element(id)
|
|
19
|
+
driver.find_element(id: id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ids(elements)
|
|
23
|
+
elements.map { |e| e.attribute('id') }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def query(scope, role, options = {})
|
|
27
|
+
engine('return window.__raider.role(arguments[0] || document, arguments[1], arguments[2])',
|
|
28
|
+
scope, role, { 'name' => nil, 'exact' => false, 'includeHidden' => false, 'level' => nil }.merge(options))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
role_cases = {
|
|
32
|
+
'r-h1' => 'heading', 'r-h2' => 'heading', 'r-h3' => 'heading', 'r-h4' => 'heading', 'r-h5' => 'heading',
|
|
33
|
+
'r-h6' => 'heading', 'r-link' => 'link', 'r-anchor' => 'generic', 'r-area' => 'link',
|
|
34
|
+
'r-article' => 'article', 'r-header-in-article' => 'generic', 'r-footer-in-article' => 'generic',
|
|
35
|
+
'r-aside' => 'complementary', 'r-button' => 'button', 'r-datalist' => 'listbox', 'r-details' => 'group',
|
|
36
|
+
'r-summary' => nil, 'r-dialog' => 'dialog', 'r-fieldset' => 'group', 'r-figure' => 'figure',
|
|
37
|
+
'r-header' => 'banner', 'r-footer' => 'contentinfo', 'r-form' => 'form', 'r-hr' => 'separator',
|
|
38
|
+
'r-img' => 'img', 'r-img-empty-alt' => 'presentation', 'r-img-presentation' => 'presentation',
|
|
39
|
+
'r-in-button' => 'button', 'r-in-image' => 'button', 'r-in-reset' => 'button', 'r-in-submit' => 'button',
|
|
40
|
+
'r-in-checkbox' => 'checkbox', 'r-in-radio' => 'radio', 'r-in-range' => 'slider',
|
|
41
|
+
'r-in-number' => 'spinbutton', 'r-in-search' => 'searchbox', 'r-in-search-list' => 'combobox',
|
|
42
|
+
'r-in-email' => 'textbox', 'r-in-tel' => 'textbox', 'r-in-text' => 'textbox', 'r-in-url' => 'textbox',
|
|
43
|
+
'r-in-password' => 'textbox', 'r-in-default' => 'textbox', 'r-in-text-list' => 'combobox',
|
|
44
|
+
'r-in-hidden' => nil, 'r-ul' => 'list', 'r-ol' => 'list', 'r-menu' => 'list', 'r-li' => 'listitem',
|
|
45
|
+
'r-li-orphan' => 'generic', 'r-main' => 'main', 'r-math' => 'math', 'r-meter' => 'meter',
|
|
46
|
+
'r-nav' => 'navigation', 'r-select' => 'combobox', 'r-option' => 'option', 'r-select-multiple' => 'listbox',
|
|
47
|
+
'r-select-size' => 'listbox', 'r-output' => 'status', 'r-progress' => 'progressbar',
|
|
48
|
+
'r-section' => 'generic', 'r-section-label' => 'region', 'r-section-labelledby' => 'region',
|
|
49
|
+
'r-table' => 'table', 'r-thead' => 'rowgroup', 'r-tbody' => 'rowgroup', 'r-tfoot' => 'rowgroup',
|
|
50
|
+
'r-tr' => 'row', 'r-th-col' => 'columnheader', 'r-th-colscope' => 'columnheader',
|
|
51
|
+
'r-th-first' => 'rowheader', 'r-th-only-1' => 'columnheader', 'r-th-only-2' => 'columnheader',
|
|
52
|
+
'cy-self' => 'region', 'cy-a' => 'region', 'cy-b' => 'region', 'cy-button' => 'button', 'r-th-second' => 'columnheader', 'r-th-scoperow' => 'rowheader',
|
|
53
|
+
'r-td' => 'cell', 'r-td-grid' => 'gridcell', 'r-td-grid-ancestor' => 'gridcell', 'r-textarea' => 'textbox',
|
|
54
|
+
'r-div' => 'generic', 'r-span' => 'generic', 'r-div-button' => 'button', 'r-role-tokens' => 'button',
|
|
55
|
+
'r-bad-role' => 'button', 'r-explicit-over-implicit' => 'button',
|
|
56
|
+
'r-p' => 'paragraph', 'r-blockquote' => 'blockquote', 'r-code' => 'code', 'r-em' => 'emphasis',
|
|
57
|
+
'r-strong' => 'strong', 'r-del' => 'deletion', 'r-ins' => 'insertion', 'r-sub' => 'subscript',
|
|
58
|
+
'r-sup' => 'superscript', 'r-time' => 'time', 'r-dfn' => 'term', 'r-dt' => 'term', 'r-dd' => 'definition',
|
|
59
|
+
'r-caption' => 'caption', 'r-html' => 'document', 'r-search' => 'search', 'r-address' => 'group',
|
|
60
|
+
'r-optgroup' => 'group'
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
name_cases = {
|
|
64
|
+
'n-labelledby' => 'First Second label', 'n-lb-hidden' => 'Hidden label', 'n-lb-nested' => 'Nest text',
|
|
65
|
+
'n-lb-missing' => 'Falls back', 'n-arialabel' => 'Close dialog', 'n-labelfor' => 'Email address',
|
|
66
|
+
'n-wrapped' => 'Wrapped label', 'n-img' => 'Company logo', 'n-input-image' => 'Go', 'n-area' => 'Area name',
|
|
67
|
+
'n-submit' => 'Send', 'n-input-button' => 'Plain value', 'n-reset' => 'Clear',
|
|
68
|
+
# Known gap: a valueless submit has a browser-supplied default label ("Submit") the engine does not know.
|
|
69
|
+
'n-submit-empty' => '',
|
|
70
|
+
'n-table' => 'Sales', 'n-fieldset' => 'Contact', 'n-figure' => 'Chart caption', 'n-title' => 'Tooltip only',
|
|
71
|
+
'n-title-fallback' => 'Text wins', 'n-embedded' => 'Search for cats', 'n-slider' => 'Rate 7 stars',
|
|
72
|
+
'n-select' => 'Pick Two', 'n-content' => 'Go icon now', 'n-blocks' => 'One Two', 'n-inline' => 'Abcd',
|
|
73
|
+
'n-heading' => 'Spaced title', 'n-hidden-child' => 'Visible', 'n-div-button' => 'Div button',
|
|
74
|
+
'n-generic' => '', 'n-li' => '', 'n-unreferenced-hidden' => '', 'n-unlabelled' => '', 'cy-self' => '', 'cy-a' => 'B', 'cy-b' => 'A', 'cy-button' => 'Self button',
|
|
75
|
+
'n-placeholder' => 'Email address', 'n-placeholder-title' => 'Title wins', 'n-placeholder-area' => 'Say more'
|
|
76
|
+
}.freeze
|
|
77
|
+
|
|
78
|
+
hidden_cases = {
|
|
79
|
+
'h-visible' => false, 'h-aria-hidden' => true, 'h-anc-aria' => true, 'h-anc-none' => true,
|
|
80
|
+
'h-display-none' => true, 'h-attr' => true, 'h-visibility' => true, 'h-vis-parent-child' => false,
|
|
81
|
+
'h-vis-inherit' => true, 'r-in-hidden' => true, 'o-option' => false, 'dc-button' => false
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
84
|
+
describe 'getRole' do
|
|
85
|
+
role_cases.each do |id, role|
|
|
86
|
+
it "maps ##{id} to #{role.inspect}" do
|
|
87
|
+
expect(engine('return window.__raider.getRole(arguments[0])', element(id))).to eq(role)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe 'accessibleName' do
|
|
93
|
+
name_cases.each do |id, name|
|
|
94
|
+
it "names ##{id} #{name.inspect}" do
|
|
95
|
+
expect(engine('return window.__raider.accessibleName(arguments[0])', element(id))).to eq(name)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe 'isHidden' do
|
|
101
|
+
hidden_cases.each do |id, hidden|
|
|
102
|
+
it "reports ##{id} hidden=#{hidden}" do
|
|
103
|
+
expect(engine('return window.__raider.isHidden(arguments[0])', element(id))).to eq(hidden)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
describe 'role' do
|
|
109
|
+
it 'matches the name case-insensitively as a substring' do
|
|
110
|
+
expect(ids(query(element('saves'), 'button', 'name' => 'save'))).to eq(%w[save-1 save-2 save-3])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'matches a name exactly and case-sensitively' do
|
|
114
|
+
expect(ids(query(element('saves'), 'button', 'name' => 'Save', 'exact' => true))).to eq(['save-1'])
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'adds hidden elements under includeHidden' do
|
|
118
|
+
found = ids(query(element('saves'), 'button', 'name' => 'save', 'includeHidden' => true))
|
|
119
|
+
expect(found).to eq(%w[save-1 save-2 save-3 save-hidden])
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'searches the whole document in document order' do
|
|
123
|
+
expect(ids(query(nil, 'button', 'name' => 'Save', 'exact' => true))).to eq(%w[save-1 scope-save])
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'limits the search to the scope and leaves the outside button out' do
|
|
127
|
+
expect(ids(query(element('scope'), 'button'))).to eq(['scope-save'])
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'leaves out buttons under hidden ancestors and keeps the visible child of a hidden parent' do
|
|
131
|
+
found = ids(query(nil, 'button'))
|
|
132
|
+
expect(found).to include('h-visible', 'h-vis-parent-child')
|
|
133
|
+
expect(found).not_to include('h-anc-aria', 'h-anc-none', 'h-vis-inherit', 'h-visibility')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'finds an option inside a closed select and a display: contents button' do
|
|
137
|
+
expect(ids(query(nil, 'option', 'name' => 'Pick me'))).to eq(['o-option'])
|
|
138
|
+
expect(ids(query(nil, 'button', 'name' => 'Contents button'))).to eq(['dc-button'])
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'filters headings by level from the tag or aria-level' do
|
|
142
|
+
expect(ids(query(nil, 'heading', 'level' => 2))).to eq(%w[r-h2 lv-aria n-heading])
|
|
143
|
+
expect(ids(query(nil, 'heading', 'level' => 1))).to eq(['r-h1'])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'searches a shadow root scope and resolves labelledby inside it' do
|
|
147
|
+
host = element('shadow-host')
|
|
148
|
+
names = driver.execute_script('return arguments[0].shadowRoot.querySelectorAll("button").length', host)
|
|
149
|
+
expect(names).to eq(2)
|
|
150
|
+
found = engine('return window.__raider.role(arguments[0].shadowRoot, "button", { name: "Labelled inner" })', host)
|
|
151
|
+
expect(found.size).to eq(1)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'never returns the scope element itself' do
|
|
155
|
+
expect(ids(query(element('scope-button'), 'button'))).to eq(['scope-inner'])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'finds by an explicit role on a div' do
|
|
159
|
+
expect(ids(query(element('scope-button'), 'button', 'name' => 'inner'))).to eq(['scope-inner'])
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'still answers every role query with labelledby cycles on the page' do
|
|
163
|
+
expect(ids(query(element('cy-button').find_element(xpath: '..'), 'button', 'name' => 'Self button')))
|
|
164
|
+
.to include('cy-button')
|
|
165
|
+
expect(query(nil, 'button')).not_to be_empty
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'writes nothing to the page but window.__raider' do
|
|
169
|
+
added = driver.execute_script(
|
|
170
|
+
"delete window.__raider; var before = Object.keys(window);\n#{Raider::RoleFinder::ENGINE}\n" \
|
|
171
|
+
'return Object.keys(window).filter(function (k) { return before.indexOf(k) < 0 })'
|
|
172
|
+
)
|
|
173
|
+
expect(added).to eq(['__raider'])
|
|
174
|
+
expect(engine('return Object.keys(window.__raider).sort()')).to eq(%w[accessibleName getRole isHidden role])
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'merges into an existing window.__raider instead of replacing it' do
|
|
178
|
+
result = driver.execute_script(
|
|
179
|
+
"window.__raider = { keep: 1 }; #{Raider::RoleFinder::ENGINE}\nreturn [window.__raider.keep, typeof window.__raider.role]"
|
|
180
|
+
)
|
|
181
|
+
expect(result).to eq([1, 'function'])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative '../support/browser'
|
|
6
|
+
|
|
7
|
+
RSpec.describe 'Raider find waits in a real browser', :browser do
|
|
8
|
+
let(:driver) { Raider::Spec::Browser.driver }
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
Raider.reset
|
|
12
|
+
Raider.timeout = 3
|
|
13
|
+
Raider::Spec::Browser.open('actionability.html')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after { Raider.reset }
|
|
17
|
+
|
|
18
|
+
after(:all) { Raider::Spec::Browser.quit } # rubocop:disable RSpec/BeforeAfterAll
|
|
19
|
+
|
|
20
|
+
def insert_after(delay, html)
|
|
21
|
+
driver.execute_script(<<~JS, delay, html)
|
|
22
|
+
const [delay, html] = arguments;
|
|
23
|
+
setTimeout(() => document.body.insertAdjacentHTML('beforeend', html), delay);
|
|
24
|
+
JS
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'finds and clicks a button inserted after 300 ms' do
|
|
28
|
+
driver.execute_script('window.clicked = false')
|
|
29
|
+
insert_after(300, '<button id="late" onclick="window.clicked = true">Late</button>')
|
|
30
|
+
driver.find_element(id: 'late').click
|
|
31
|
+
expect(driver.execute_script('return window.clicked')).to be(true)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'returns three rows inserted after 300 ms from find_elements' do
|
|
35
|
+
insert_after(300, '<p class="late-row">1</p><p class="late-row">2</p><p class="late-row">3</p>')
|
|
36
|
+
expect(driver.find_elements(css: '.late-row').size).to eq(3)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../lib/ruby_raider/runtime'
|
|
5
|
+
|
|
6
|
+
RSpec.describe 'Raider.by_role' do
|
|
7
|
+
it 'returns just the role for a bare role' do
|
|
8
|
+
expect(Raider.by_role('button')).to eq(role: ['button'])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'adds the name option' do
|
|
12
|
+
expect(Raider.by_role('button', name: 'Save')).to eq(role: ['button', { name: 'Save' }])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'adds exact and include_hidden only when set' do
|
|
16
|
+
expect(Raider.by_role('link', name: 'Home', exact: true, include_hidden: true))
|
|
17
|
+
.to eq(role: ['link', { name: 'Home', exact: true, include_hidden: true }])
|
|
18
|
+
expect(Raider.by_role('link', include_hidden: true)).to eq(role: ['link', { include_hidden: true }])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'carries a heading level' do
|
|
22
|
+
expect(Raider.by_role('heading', name: 'Intro', level: 2)).to eq(role: ['heading', { name: 'Intro', level: 2 }])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'rejects a level on another role or outside 1 to 6' do
|
|
26
|
+
expect { Raider.by_role('button', level: 1) }.to raise_error(ArgumentError, /heading/)
|
|
27
|
+
expect { Raider.by_role('heading', level: 0) }.to raise_error(ArgumentError, /1 to 6/)
|
|
28
|
+
expect { Raider.by_role('heading', level: '2') }.to raise_error(ArgumentError, /1 to 6/)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'stringifies a symbol role' do
|
|
32
|
+
expect(Raider.by_role(:heading)).to eq(role: ['heading'])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'rejects an unknown role' do
|
|
36
|
+
expect { Raider.by_role('foo') }
|
|
37
|
+
.to raise_error(ArgumentError, "unknown role 'foo'; roles are the ARIA role names such as button, link, textbox")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'rejects a non-String name' do
|
|
41
|
+
expect { Raider.by_role('button', name: /Save/) }.to raise_error(ArgumentError)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'has a frozen ROLES list' do
|
|
45
|
+
expect(Raider::ROLES).to be_frozen.and include('button', 'generic')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'is available on Raider::Helpers' do
|
|
49
|
+
helper = Object.new.extend(Raider::Helpers)
|
|
50
|
+
expect(helper.by_role('tab', name: 'One')).to eq(role: ['tab', { name: 'One' }])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require_relative '../../lib/ruby_raider/runtime/config'
|
|
7
|
+
|
|
8
|
+
RSpec.describe 'Raider configuration' do
|
|
9
|
+
let(:env_keys) { %w[RAIDER_STRICT RAIDER_ACTIONABILITY RAIDER_TIMEOUT RAIDER_TEST_ID_ATTRIBUTE] }
|
|
10
|
+
|
|
11
|
+
around do |example|
|
|
12
|
+
saved = env_keys.to_h { |key| [key, ENV.fetch(key, nil)] }
|
|
13
|
+
env_keys.each { |key| ENV.delete(key) }
|
|
14
|
+
Raider.reset
|
|
15
|
+
Dir.mktmpdir do |dir|
|
|
16
|
+
Dir.chdir(dir) { example.run }
|
|
17
|
+
end
|
|
18
|
+
ensure
|
|
19
|
+
saved.each { |key, value| value ? ENV[key] = value : ENV.delete(key) }
|
|
20
|
+
Raider.reset
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def write_config(content)
|
|
24
|
+
FileUtils.mkdir_p('config')
|
|
25
|
+
File.write(Raider::CONFIG_FILE, content)
|
|
26
|
+
Raider.reset
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'uses defaults without a file or env' do
|
|
30
|
+
expect([Raider.strict, Raider.actionability, Raider.timeout, Raider.test_id_attribute])
|
|
31
|
+
.to eq([true, true, 10, 'data-testid'])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'prefers file values over defaults' do
|
|
35
|
+
write_config("strict: false\nactionability: 'off'\ntimeout: 3.5\ntest_id_attribute: data-qa\n")
|
|
36
|
+
expect([Raider.strict, Raider.actionability, Raider.timeout, Raider.test_id_attribute])
|
|
37
|
+
.to eq([false, false, 3.5, 'data-qa'])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'prefers env over the file' do
|
|
41
|
+
write_config("strict: true\n")
|
|
42
|
+
ENV['RAIDER_STRICT'] = 'off'
|
|
43
|
+
expect(Raider.strict).to be(false)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'reads env timeout and test id attribute' do
|
|
47
|
+
ENV['RAIDER_TIMEOUT'] = '2.5'
|
|
48
|
+
ENV['RAIDER_TEST_ID_ATTRIBUTE'] = 'data-cy'
|
|
49
|
+
expect([Raider.timeout, Raider.test_id_attribute]).to eq([2.5, 'data-cy'])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'prefers the setter over env' do
|
|
53
|
+
ENV['RAIDER_STRICT'] = 'off'
|
|
54
|
+
Raider.strict = true
|
|
55
|
+
expect(Raider.strict).to be(true)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'ignores a non-Hash YAML file' do
|
|
59
|
+
write_config("- one\n- two\n")
|
|
60
|
+
expect(Raider.timeout).to eq(10)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'reads a file that holds a symbol value' do
|
|
64
|
+
write_config("platform: :ios\ntimeout: 7\n")
|
|
65
|
+
expect(Raider.timeout).to eq(7)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'raises Raider::Error naming the file for malformed YAML' do
|
|
69
|
+
write_config("timeout: [7\n")
|
|
70
|
+
expect { Raider.timeout }.to raise_error(Raider::Error, %r{config/config\.yml})
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 're-reads the file after reset' do
|
|
74
|
+
write_config("timeout: 4\n")
|
|
75
|
+
expect(Raider.timeout).to eq(4)
|
|
76
|
+
write_config("timeout: 6\n")
|
|
77
|
+
expect(Raider.timeout).to eq(6)
|
|
78
|
+
end
|
|
79
|
+
end
|