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,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../lib/ruby_raider/runtime/context'
|
|
5
|
+
|
|
6
|
+
RSpec.describe 'Raider::Context' do
|
|
7
|
+
let(:appium_bridge_class) do
|
|
8
|
+
stub_const('Appium::Core::Base::Bridge', Class.new(Selenium::WebDriver::Remote::Bridge))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def appium_bridge(context)
|
|
12
|
+
bridge = appium_bridge_class.allocate
|
|
13
|
+
bridge.define_singleton_method(:current_context) { context }
|
|
14
|
+
bridge
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def plain_bridge
|
|
18
|
+
Selenium::WebDriver::Remote::Bridge.allocate
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def holder(klass, bridge)
|
|
22
|
+
klass.allocate.tap { |object| object.instance_variable_set(:@bridge, bridge) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '.web?' do
|
|
26
|
+
it 'is false on an Appium NATIVE_APP context' do
|
|
27
|
+
expect(Raider::Context.web?(appium_bridge('NATIVE_APP'))).to be(false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'is false on NATIVE_APP when a listener wraps the Appium bridge' do
|
|
31
|
+
wrapped = Selenium::WebDriver::Support::EventFiringBridge.new(appium_bridge('NATIVE_APP'), [])
|
|
32
|
+
expect(Raider::Context.web?(wrapped)).to be(false)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'is true on an Appium webview context' do
|
|
36
|
+
expect(Raider::Context.web?(appium_bridge('WEBVIEW_1'))).to be(true)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'is true on an Appium CHROMIUM context' do
|
|
40
|
+
expect(Raider::Context.web?(appium_bridge('CHROMIUM'))).to be(true)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'is true for a non-Appium bridge without asking current_context' do
|
|
44
|
+
bridge = plain_bridge
|
|
45
|
+
expect(bridge).not_to respond_to(:current_context)
|
|
46
|
+
expect(Raider::Context.web?(bridge)).to be(true)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'is true for a non-Appium bridge when Appium is not loaded' do
|
|
50
|
+
expect(Raider::Context.web?(Object.new)).to be(true)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '.web_for?' do
|
|
55
|
+
it 'reads the bridge of a driver' do
|
|
56
|
+
driver = Selenium::WebDriver::Driver
|
|
57
|
+
expect(Raider::Context.web_for?(holder(driver, appium_bridge('NATIVE_APP')))).to be(false)
|
|
58
|
+
expect(Raider::Context.web_for?(holder(driver, appium_bridge('WEBVIEW_1')))).to be(true)
|
|
59
|
+
expect(Raider::Context.web_for?(holder(driver, plain_bridge))).to be(true)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'reads the bridge of an element' do
|
|
63
|
+
element = Selenium::WebDriver::Element
|
|
64
|
+
expect(Raider::Context.web_for?(holder(element, appium_bridge('NATIVE_APP')))).to be(false)
|
|
65
|
+
expect(Raider::Context.web_for?(holder(element, appium_bridge('CHROMIUM')))).to be(true)
|
|
66
|
+
expect(Raider::Context.web_for?(holder(element, plain_bridge))).to be(true)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'is true when the object has no bridge' do
|
|
70
|
+
expect(Raider::Context.web_for?(Object.new)).to be(true)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../lib/ruby_raider/runtime/errors'
|
|
5
|
+
|
|
6
|
+
RSpec.describe 'Raider runtime errors' do
|
|
7
|
+
after { Raider.reset }
|
|
8
|
+
|
|
9
|
+
def fake(tag: 'button', id: nil, klass: nil, text: '')
|
|
10
|
+
instance_double(Selenium::WebDriver::Element, tag_name: tag, text: text).tap do |element|
|
|
11
|
+
allow(element).to receive(:attribute).with('id').and_return(id)
|
|
12
|
+
allow(element).to receive(:attribute).with('class').and_return(klass)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe 'ancestry' do
|
|
17
|
+
it 'subclasses the Selenium errors' do
|
|
18
|
+
expect(Raider::Error.ancestors).to include(StandardError)
|
|
19
|
+
expect(Raider::AmbiguousMatch.ancestors).to include(Selenium::WebDriver::Error::WebDriverError)
|
|
20
|
+
expect(Raider::NotActionable.ancestors).to include(Selenium::WebDriver::Error::ElementNotInteractableError)
|
|
21
|
+
expect(Raider::NotSupported.ancestors).to include(Selenium::WebDriver::Error::UnsupportedOperationError)
|
|
22
|
+
expect(Raider::TimeoutError.ancestors).to include(Selenium::WebDriver::Error::TimeoutError)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe Raider::AmbiguousMatch do
|
|
27
|
+
it 'describes the first three elements and counts the rest' do
|
|
28
|
+
elements = [fake(id: 'save', text: 'Save'), fake(klass: 'toolbar'), fake, fake]
|
|
29
|
+
error = described_class.new("by_role('button', name: 'Save')", elements)
|
|
30
|
+
expect(error.message).to eq(
|
|
31
|
+
"`by_role('button', name: 'Save')` matched 4 elements: `<button id=\"save\">Save</button>`, " \
|
|
32
|
+
'`<button class="toolbar">`, `<button>` (+1 more); use find_elements, or narrow the locator'
|
|
33
|
+
)
|
|
34
|
+
expect(error.elements).to eq(elements)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'shows +0 more with three or fewer' do
|
|
38
|
+
error = described_class.new('x', [fake, fake])
|
|
39
|
+
expect(error.message).to eq('`x` matched 2 elements: `<button>`, `<button>` (+0 more); ' \
|
|
40
|
+
'use find_elements, or narrow the locator')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe Raider::NotActionable do
|
|
45
|
+
it 'reports the check and the covering element' do
|
|
46
|
+
covering = { element: '<div class="overlay">', x: 412, y: 300 }
|
|
47
|
+
error = described_class.new('<button id="save">', 'receive events', covering: covering)
|
|
48
|
+
expect(error.message).to eq('waited 10s for `<button id="save">` to receive events; ' \
|
|
49
|
+
'`<div class="overlay">` covers it at (412, 300)')
|
|
50
|
+
expect(error.check).to eq('receive events')
|
|
51
|
+
expect(error.covering).to eq(covering)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'omits the covering clause' do
|
|
55
|
+
error = described_class.new('<input id="q">', 'be visible')
|
|
56
|
+
expect(error.message).to eq('waited 10s for `<input id="q">` to be visible')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'reports the waited seconds when given' do
|
|
60
|
+
expect(described_class.new('<a>', 'be visible', waited: 0.25).message).to start_with('waited 0.25s for')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'formats fractional timeouts' do
|
|
64
|
+
Raider.timeout = 2.5
|
|
65
|
+
expect(described_class.new('<a>', 'be visible').message).to start_with('waited 2.5s for')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe Raider::Describe do
|
|
70
|
+
it 'formats seconds' do
|
|
71
|
+
expect([10, 2.5, 10.0, 2.504].map { |value| described_class.seconds(value) }).to eq([10, 2.5, 10, 2.5])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'limits classes to two and truncates text to 30 characters' do
|
|
75
|
+
element = fake(klass: 'a b c', text: " #{'x' * 40} ")
|
|
76
|
+
expect(described_class.element(element)).to eq("<button class=\"a b\">#{'x' * 30}</button>")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'skips empty id and class' do
|
|
80
|
+
expect(described_class.element(fake(tag: 'div', id: '', klass: ' '))).to eq('<div>')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'returns <element> on a WebDriver error' do
|
|
84
|
+
element = instance_double(Selenium::WebDriver::Element)
|
|
85
|
+
allow(element).to receive(:tag_name).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
|
86
|
+
expect(described_class.element(element)).to eq('<element>')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require_relative '../../lib/ruby_raider/runtime'
|
|
5
|
+
require_relative 'support/fake_bridge'
|
|
6
|
+
|
|
7
|
+
RSpec.describe 'Raider finders' do
|
|
8
|
+
let(:bridge) { Raider::Spec::FakeBridge.new }
|
|
9
|
+
let(:driver) { Raider::Spec.driver(bridge) }
|
|
10
|
+
let(:search_context) { Selenium::WebDriver::SearchContext }
|
|
11
|
+
|
|
12
|
+
before { bridge.enqueue(['a']) }
|
|
13
|
+
|
|
14
|
+
it 'registers every Raider key with Selenium' do
|
|
15
|
+
expect(search_context.finders).to include(Raider::FINDERS)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'converts label to xpath' do
|
|
19
|
+
driver.find_element(label: 'Email')
|
|
20
|
+
expect(bridge.calls.last).to eq([:find_elements_by, 'xpath', Raider::Selectors.label('Email', exact: false), [:driver, nil]])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'converts text to xpath' do
|
|
24
|
+
driver.find_element(text: ['Go', { exact: true }])
|
|
25
|
+
expect(bridge.calls.last[1..2]).to eq(['xpath', Raider::Selectors.text('Go', exact: true)])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'converts placeholder with exact to css' do
|
|
29
|
+
driver.find_element(placeholder: ['Name', { exact: true }])
|
|
30
|
+
expect(bridge.calls.last[1..2]).to eq(['css selector', '[placeholder="Name"]'])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'converts alt and title to css' do
|
|
34
|
+
driver.find_element(alt: 'Logo')
|
|
35
|
+
Raider::Wait.immediately { driver.find_elements(title: 'Help') }
|
|
36
|
+
expect(bridge.calls.map { |call| call[2] }).to eq(['[alt*="Logo"]', '[title*="Help"]'])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'converts test_id to an exact css match' do
|
|
40
|
+
driver.find_element(test_id: 'x')
|
|
41
|
+
expect(bridge.calls.last[1..2]).to eq(['css selector', Raider::Selectors.test_id('x')])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'leaves other keys to the inner converter' do
|
|
45
|
+
driver.find_element(id: 'x')
|
|
46
|
+
expect(bridge.calls.last[1..2]).to eq(['css selector', '#x'])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'passes the element ref for an element scope' do
|
|
50
|
+
element = Selenium::WebDriver::Element.new(bridge, 'root')
|
|
51
|
+
element.find_element(label: 'Email')
|
|
52
|
+
expect(bridge.calls.last.last).to eq([:element, 'root'])
|
|
53
|
+
expect(bridge.calls.last[2].split(' | ')).to all(start_with('.//'))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'guards the converter role branch with an internal error' do
|
|
57
|
+
expect { Raider::LocatorConverter.new(nil).convert(Raider::LocatorConverter::ROLE, 'button') }
|
|
58
|
+
.to raise_error(Raider::NotSupported, /did not intercept/)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'raises ArgumentError naming the key for a malformed value' do
|
|
62
|
+
expect { driver.find_element(label: [1, {}]) }.to raise_error(ArgumentError, /label/)
|
|
63
|
+
expect { driver.find_element(label: %w[a b]) }.to raise_error(ArgumentError, /label/)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'rejects nil and blank values' do
|
|
67
|
+
[{ label: nil }, { text: '' }, { placeholder: [' ', {}] }].each do |locator|
|
|
68
|
+
expect { driver.find_element(locator) }.to raise_error(ArgumentError, /#{locator.keys.first}: needs a non-empty value/)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe '.install_converter' do
|
|
73
|
+
it 'is idempotent' do
|
|
74
|
+
bridge_class = Selenium::WebDriver::Remote::Bridge
|
|
75
|
+
before_converter = bridge_class.locator_converter
|
|
76
|
+
Raider::Finders.install_converter(bridge_class)
|
|
77
|
+
expect(bridge_class.locator_converter).to equal(before_converter)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'wraps a plain converter exactly once' do
|
|
81
|
+
klass = Class.new { class << self; attr_accessor :locator_converter; end }
|
|
82
|
+
klass.locator_converter = Selenium::WebDriver::Remote::Bridge::LocatorConverter.new
|
|
83
|
+
2.times { Raider::Finders.install_converter(klass) }
|
|
84
|
+
expect(klass.locator_converter.inner).not_to be_a(Raider::LocatorConverter)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe 'with Appium finders assigned after Raider' do
|
|
89
|
+
around do |example|
|
|
90
|
+
previous = search_context.extra_finders
|
|
91
|
+
example.run
|
|
92
|
+
ensure
|
|
93
|
+
search_context.extra_finders = previous
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
before do
|
|
97
|
+
stub_const('Appium::Core::Base::SearchContext::APPIUM_EXTRA_FINDERS', { accessibility_id: 'accessibility id' })
|
|
98
|
+
search_context.extra_finders = Appium::Core::Base::SearchContext::APPIUM_EXTRA_FINDERS
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'keeps both tables after register' do
|
|
102
|
+
Raider::Finders.register
|
|
103
|
+
expect(search_context.finders).to include(:accessibility_id, :label)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'restores the keys through ensure_registered' do
|
|
107
|
+
expect(search_context.finders).not_to include(:label)
|
|
108
|
+
Raider::Finders.ensure_registered
|
|
109
|
+
expect(search_context.finders).to include(:accessibility_id, :label)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '.ensure_registered with a late Appium bridge' do
|
|
114
|
+
it 'wraps the converter Appium assigned' do
|
|
115
|
+
appium = stub_const('Appium::Core::Base::Bridge', Class.new(Selenium::WebDriver::Remote::Bridge))
|
|
116
|
+
inner = Selenium::WebDriver::Remote::Bridge::LocatorConverter.new
|
|
117
|
+
appium.locator_converter = inner
|
|
118
|
+
Raider::Finders.ensure_registered
|
|
119
|
+
expect(appium.locator_converter).to be_a(Raider::LocatorConverter)
|
|
120
|
+
expect(appium.locator_converter.inner).to equal(inner)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe 'BiDi bridges' do
|
|
125
|
+
it 'gives the Selenium BiDi bridge class the Raider converter' do
|
|
126
|
+
expect(Selenium::WebDriver::Remote::BiDiBridge.locator_converter).to be_a(Raider::LocatorConverter)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'gives a BiDi bridge instance the Raider converter' do
|
|
130
|
+
bidi = Raider::Spec.real_bridge(Selenium::WebDriver::Remote::BiDiBridge)
|
|
131
|
+
expect(bidi.instance_variable_get(:@locator_converter)).to be_a(Raider::LocatorConverter)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'wraps a late Appium BiDi bridge in ensure_registered' do
|
|
135
|
+
appium = stub_const('Appium::Core::Base::BiDiBridge', Class.new(Selenium::WebDriver::Remote::BiDiBridge))
|
|
136
|
+
inner = Selenium::WebDriver::Remote::Bridge::LocatorConverter.new
|
|
137
|
+
appium.locator_converter = inner
|
|
138
|
+
Raider::Finders.ensure_registered
|
|
139
|
+
expect(appium.locator_converter).to be_a(Raider::LocatorConverter)
|
|
140
|
+
expect(appium.locator_converter.inner).to equal(inner)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe '.guard' do
|
|
145
|
+
it 'raises when a bridge already exists' do
|
|
146
|
+
existing = Selenium::WebDriver::Remote::Bridge.allocate
|
|
147
|
+
expect { Raider::Finders.guard }.to raise_error(Raider::Error, /before creating the driver/)
|
|
148
|
+
expect(existing).to be_a(Selenium::WebDriver::Remote::Bridge)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe '.check_api' do
|
|
153
|
+
it 'raises when the bridge cannot take a converter' do
|
|
154
|
+
allow(Selenium::WebDriver::Remote::Bridge).to receive(:respond_to?).and_call_original
|
|
155
|
+
allow(Selenium::WebDriver::Remote::Bridge).to receive(:respond_to?).with(:locator_converter=).and_return(false)
|
|
156
|
+
expect { Raider::Finders.check_api }.to raise_error(Raider::Error, /locator_converter=/)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'raises when SearchContext cannot take extra finders' do
|
|
160
|
+
allow(search_context).to receive(:respond_to?).and_call_original
|
|
161
|
+
allow(search_context).to receive(:respond_to?).with(:extra_finders=).and_return(false)
|
|
162
|
+
expect { Raider::Finders.check_api }.to raise_error(Raider::Error, /extra_finders=/)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Actionability fixture</title>
|
|
6
|
+
<style>
|
|
7
|
+
html { overflow-x: hidden; }
|
|
8
|
+
body { margin: 0; padding: 80px 20px 0; font-family: sans-serif; }
|
|
9
|
+
#overlay { position: fixed; top: 0; left: 0; width: 220px; height: 60px; background: #c00; opacity: 0.9; z-index: 10; }
|
|
10
|
+
#covered { position: absolute; top: 10px; left: 10px; width: 120px; height: 30px; }
|
|
11
|
+
#hidden-display { display: none; }
|
|
12
|
+
#hidden-visibility { visibility: hidden; }
|
|
13
|
+
#moving { position: relative; animation: slide 60s linear forwards; }
|
|
14
|
+
#bouncer { position: absolute; top: 1200px; left: 600px; animation: fall 60s linear forwards; }
|
|
15
|
+
@keyframes fall { from { transform: translateY(0); } to { transform: translateY(2000px); } }
|
|
16
|
+
#hidden-box { display: none; }
|
|
17
|
+
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(3000px); } }
|
|
18
|
+
#far { position: absolute; top: 3000px; left: 400px; }
|
|
19
|
+
#sticky-zone { position: absolute; top: 4000px; left: 0; width: 100%; height: 2500px; }
|
|
20
|
+
#top { position: sticky; top: 0; height: 70vh; background: #036; opacity: 0.9; z-index: 5; }
|
|
21
|
+
#under-header { position: absolute; top: 1500px; left: 400px; }
|
|
22
|
+
#scroller { width: 300px; height: 120px; overflow: auto; border: 1px solid #999; }
|
|
23
|
+
#scroller .pad { height: 300px; }
|
|
24
|
+
#zero { position: absolute; top: 0; left: 0; width: 0; height: 0; padding: 0; border: 0; overflow: hidden; }
|
|
25
|
+
#file { position: absolute; width: 0; height: 0; opacity: 0; }
|
|
26
|
+
</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div id="overlay"></div>
|
|
30
|
+
<button id="covered">Covered</button>
|
|
31
|
+
|
|
32
|
+
<p><button id="plain">Plain</button></p>
|
|
33
|
+
<p><button id="hidden-display">Gone</button> <button id="hidden-visibility">Invisible</button></p>
|
|
34
|
+
<p><button id="disabled" disabled>Disabled</button></p>
|
|
35
|
+
<fieldset id="locked" disabled>
|
|
36
|
+
<legend><button id="legend-button">Legend</button></legend>
|
|
37
|
+
<button id="in-fieldset">In fieldset</button>
|
|
38
|
+
</fieldset>
|
|
39
|
+
<p>
|
|
40
|
+
<input id="readonly" type="text" readonly value="read only">
|
|
41
|
+
<input id="writable" type="text" value="writable">
|
|
42
|
+
<textarea id="area"></textarea>
|
|
43
|
+
<div id="editable" contenteditable="true">Edit me</div>
|
|
44
|
+
</p>
|
|
45
|
+
<div id="inert-region" inert><button id="in-inert">Inert</button></div>
|
|
46
|
+
<dialog id="dlg"><button id="in-dialog">Inside</button><div id="shadow-host"></div></dialog>
|
|
47
|
+
<div id="scroller"><div class="pad"></div><button id="deep" onclick="this.setAttribute('data-clicked', '1')">Deep</button></div>
|
|
48
|
+
<button id="zero">Zero</button>
|
|
49
|
+
<p id="many"><button id="m0">0</button><button id="m1">1</button><button id="m2">2</button><button id="m3">3</button><button id="m4">4</button><button id="m5">5</button><button id="m6">6</button><button id="m7">7</button><button id="m8">8</button><button id="m9">9</button></p>
|
|
50
|
+
<p><button id="outside-dialog">Outside</button></p>
|
|
51
|
+
<p><button id="moving">Moving</button></p>
|
|
52
|
+
<p>
|
|
53
|
+
<select id="choices"><option id="opt-a" value="a">A</option><option id="opt-b" value="b">B</option><option id="opt-blue" value="blue">Blue</option></select>
|
|
54
|
+
<label>File <input id="file" type="file"></label>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
<button id="bouncer">Bouncing</button>
|
|
58
|
+
<div id="hidden-box"><input id="file-hidden" type="file"></div>
|
|
59
|
+
<button id="far" onclick="this.setAttribute('data-clicked', '1')">Far away</button>
|
|
60
|
+
<div id="sticky-zone">
|
|
61
|
+
<header id="top">Sticky header</header>
|
|
62
|
+
<button id="under-header">Under the header</button>
|
|
63
|
+
</div>
|
|
64
|
+
<details id="collapsed"><summary>More</summary><button id="in-details">Hidden in details</button></details>
|
|
65
|
+
<div style="height: 7000px"></div>
|
|
66
|
+
<script>
|
|
67
|
+
document.getElementById('shadow-host').attachShadow({ mode: 'open' }).innerHTML = '<button id="shadow-button">Shadow</button>';
|
|
68
|
+
</script>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head><meta charset="utf-8"><title>Sign in</title></head>
|
|
4
|
+
<body>
|
|
5
|
+
<script type="application/json">{"action": "Sign in"}</script>
|
|
6
|
+
<form>
|
|
7
|
+
<label for="email">Email</label>
|
|
8
|
+
<input id="email" type="text">
|
|
9
|
+
|
|
10
|
+
<label for="email-address">Email address</label>
|
|
11
|
+
<input id="email-address" type="text">
|
|
12
|
+
|
|
13
|
+
<label for="password">Password</label>
|
|
14
|
+
<input id="password" type="password">
|
|
15
|
+
|
|
16
|
+
<label>Username <input type="hidden" name="remember" value="0"><input id="username" type="text"></label>
|
|
17
|
+
|
|
18
|
+
<input id="search" type="text" aria-label="Search site">
|
|
19
|
+
|
|
20
|
+
<span id="phone-label">Phone number</span>
|
|
21
|
+
<input id="phone" type="text" aria-labelledby="phone-label">
|
|
22
|
+
|
|
23
|
+
<input id="code" type="text" placeholder="Enter code">
|
|
24
|
+
<input id="name-field" type="text" placeholder="Enter your full name">
|
|
25
|
+
</form>
|
|
26
|
+
|
|
27
|
+
<section id="scope">
|
|
28
|
+
<label for="scoped-email">Email</label>
|
|
29
|
+
<input id="scoped-email" type="text">
|
|
30
|
+
</section>
|
|
31
|
+
|
|
32
|
+
<img id="logo" src="data:image/gif;base64,R0lGODlhAQABAAAAACw=" alt="Company logo">
|
|
33
|
+
<a id="docs-link" href="#docs" title="Read the docs">Docs</a>
|
|
34
|
+
|
|
35
|
+
<div id="nested"><p id="sign-in">Sign in</p></div>
|
|
36
|
+
|
|
37
|
+
<ul>
|
|
38
|
+
<li id="item-one" data-testid="item-1">One</li>
|
|
39
|
+
<li id="item-two" data-testid="item-2">Two</li>
|
|
40
|
+
</ul>
|
|
41
|
+
|
|
42
|
+
<button id="save-a" class="primary">Save</button>
|
|
43
|
+
<button id="save-b" class="secondary">Save</button>
|
|
44
|
+
<button id="save-c" class="tertiary">Save</button>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Matchers</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<h1 id="heading">Loading</h1>
|
|
9
|
+
<input id="name" type="text" value="Ann">
|
|
10
|
+
<button id="hide-me" type="button">Hide me</button>
|
|
11
|
+
<ul id="items"><li>One</li></ul>
|
|
12
|
+
<a id="finish" href="#">Finish</a>
|
|
13
|
+
<script>
|
|
14
|
+
setTimeout(function () { document.getElementById('heading').textContent = 'Ready'; }, 500);
|
|
15
|
+
document.getElementById('hide-me').addEventListener('click', function (event) {
|
|
16
|
+
setTimeout(function () { event.target.style.display = 'none'; }, 300);
|
|
17
|
+
});
|
|
18
|
+
setTimeout(function () {
|
|
19
|
+
var list = document.getElementById('items');
|
|
20
|
+
['Two', 'Three'].forEach(function (text) {
|
|
21
|
+
var item = document.createElement('li');
|
|
22
|
+
item.textContent = text;
|
|
23
|
+
list.appendChild(item);
|
|
24
|
+
});
|
|
25
|
+
}, 400);
|
|
26
|
+
document.getElementById('finish').addEventListener('click', function (event) {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
document.title = 'Done';
|
|
29
|
+
location.hash = '#done';
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" id="r-html">
|
|
3
|
+
<head><meta charset="utf-8"><title>Roles fixture</title></head>
|
|
4
|
+
<body>
|
|
5
|
+
<h1 id="r-h1">Roles</h1><h2 id="r-h2">Two</h2><h3 id="r-h3">Three</h3><h4 id="r-h4">Four</h4><h5 id="r-h5">Five</h5><h6 id="r-h6">Six</h6>
|
|
6
|
+
|
|
7
|
+
<!-- implicit roles -->
|
|
8
|
+
<a id="r-link" href="#x">Link</a>
|
|
9
|
+
<a id="r-anchor" name="x">Anchor</a>
|
|
10
|
+
<map name="m"><area id="r-area" href="#x" alt="Area alt" shape="rect" coords="0,0,1,1"></map>
|
|
11
|
+
<article id="r-article">Article <header id="r-header-in-article">Inner header</header><footer id="r-footer-in-article">Inner footer</footer></article>
|
|
12
|
+
<aside id="r-aside">Aside</aside>
|
|
13
|
+
<button id="r-button">Plain button</button>
|
|
14
|
+
<datalist id="r-datalist"><option value="a"></option></datalist>
|
|
15
|
+
<details id="r-details"><summary id="r-summary">Summary</summary>Body</details>
|
|
16
|
+
<dialog id="r-dialog">Dialog</dialog>
|
|
17
|
+
<fieldset id="r-fieldset"><legend>Group</legend></fieldset>
|
|
18
|
+
<figure id="r-figure">Figure</figure>
|
|
19
|
+
<header id="r-header">Top header</header>
|
|
20
|
+
<footer id="r-footer">Bottom footer</footer>
|
|
21
|
+
<form id="r-form"></form>
|
|
22
|
+
<hr id="r-hr">
|
|
23
|
+
<img id="r-img" src="data:," alt="Photo">
|
|
24
|
+
<img id="r-img-empty-alt" src="data:," alt="">
|
|
25
|
+
<img id="r-img-presentation" src="data:," alt="Decor" role="presentation">
|
|
26
|
+
<input id="r-in-button" type="button" value="B">
|
|
27
|
+
<input id="r-in-image" type="image" alt="I" src="data:,">
|
|
28
|
+
<input id="r-in-reset" type="reset">
|
|
29
|
+
<input id="r-in-submit" type="submit" value="S">
|
|
30
|
+
<input id="r-in-checkbox" type="checkbox">
|
|
31
|
+
<input id="r-in-radio" type="radio">
|
|
32
|
+
<input id="r-in-range" type="range">
|
|
33
|
+
<input id="r-in-number" type="number">
|
|
34
|
+
<input id="r-in-search" type="search">
|
|
35
|
+
<input id="r-in-search-list" type="search" list="r-datalist">
|
|
36
|
+
<input id="r-in-email" type="email">
|
|
37
|
+
<input id="r-in-tel" type="tel">
|
|
38
|
+
<input id="r-in-text" type="text">
|
|
39
|
+
<input id="r-in-url" type="url">
|
|
40
|
+
<input id="r-in-password" type="password">
|
|
41
|
+
<input id="r-in-default">
|
|
42
|
+
<input id="r-in-text-list" type="text" list="r-datalist">
|
|
43
|
+
<input id="r-in-hidden" type="hidden">
|
|
44
|
+
<ul id="r-ul"><li id="r-li">Item</li></ul>
|
|
45
|
+
<ol id="r-ol"><li>One</li></ol>
|
|
46
|
+
<menu id="r-menu"><li>M</li></menu>
|
|
47
|
+
<li id="r-li-orphan">Orphan</li>
|
|
48
|
+
<main id="r-main">Main</main>
|
|
49
|
+
<math id="r-math"><mi>x</mi></math>
|
|
50
|
+
<meter id="r-meter" value="0.5"></meter>
|
|
51
|
+
<nav id="r-nav">Nav</nav>
|
|
52
|
+
<select id="r-select"><option id="r-option" value="1">One</option></select>
|
|
53
|
+
<select id="r-select-multiple" multiple><option>One</option></select>
|
|
54
|
+
<select id="r-select-size" size="3"><option>One</option></select>
|
|
55
|
+
<output id="r-output">0</output>
|
|
56
|
+
<progress id="r-progress" value="1" max="2"></progress>
|
|
57
|
+
<section id="r-section">Unnamed section</section>
|
|
58
|
+
<section id="r-section-label" aria-label="Named section">Named</section>
|
|
59
|
+
<span id="r-lb-target">Heading words</span>
|
|
60
|
+
<section id="r-section-labelledby" aria-labelledby="r-lb-target">Named by id</section>
|
|
61
|
+
<table id="r-table">
|
|
62
|
+
<caption id="r-caption">Cap</caption>
|
|
63
|
+
<thead id="r-thead"><tr id="r-tr"><th id="r-th-col">Name</th><th id="r-th-colscope" scope="col">Age</th></tr></thead>
|
|
64
|
+
<tbody id="r-tbody">
|
|
65
|
+
<tr><th id="r-th-first">Row one</th><td id="r-td">Cell</td></tr>
|
|
66
|
+
<tr><td>x</td><th id="r-th-second">Late th</th><th id="r-th-scoperow" scope="row">Scoped row</th></tr>
|
|
67
|
+
</tbody>
|
|
68
|
+
<tfoot id="r-tfoot"><tr><td>f</td></tr></tfoot>
|
|
69
|
+
</table>
|
|
70
|
+
<table><tbody><tr><th id="r-th-only-1">Name</th><th id="r-th-only-2">Age</th></tr></tbody></table>
|
|
71
|
+
<table role="grid"><tbody><tr><td id="r-td-grid">Grid cell</td></tr></tbody></table>
|
|
72
|
+
<div role="grid"><div role="row"><table><tbody><tr><td id="r-td-grid-ancestor">Deep</td></tr></tbody></table></div></div>
|
|
73
|
+
<textarea id="r-textarea"></textarea>
|
|
74
|
+
<div id="r-div">Div</div>
|
|
75
|
+
<span id="r-span">Span</span>
|
|
76
|
+
<div id="r-div-button" role="button">Div button</div>
|
|
77
|
+
<div id="r-role-tokens" role="bogus button">Tokens</div>
|
|
78
|
+
<button id="r-bad-role" role="bogus">Bad role</button>
|
|
79
|
+
<a id="r-explicit-over-implicit" href="#x" role="button">Link as button</a>
|
|
80
|
+
|
|
81
|
+
<p id="r-p">Para</p><blockquote id="r-blockquote">Quote</blockquote><code id="r-code">c</code><em id="r-em">e</em>
|
|
82
|
+
<strong id="r-strong">s</strong><del id="r-del">d</del><ins id="r-ins">i</ins><sub id="r-sub">b</sub><sup id="r-sup">p</sup>
|
|
83
|
+
<time id="r-time">now</time><dfn id="r-dfn">term</dfn><dl><dt id="r-dt">T</dt><dd id="r-dd">D</dd></dl>
|
|
84
|
+
<search id="r-search">Search</search><address id="r-address">Addr</address>
|
|
85
|
+
<select id="o-select"><optgroup id="r-optgroup" label="G"><option id="o-option">Pick me</option></optgroup><option id="o-other">Other</option></select>
|
|
86
|
+
<div style="display:contents"><button id="dc-button" style="display:contents">Contents button</button></div>
|
|
87
|
+
<div id="lv-aria" role="heading" aria-level="2">Aria two</div>
|
|
88
|
+
<div id="shadow-host"></div>
|
|
89
|
+
<script>
|
|
90
|
+
document.getElementById('shadow-host').attachShadow({ mode: 'open' }).innerHTML =
|
|
91
|
+
'<span id="sl">Labelled inner</span><button>Shadow inner</button><button aria-labelledby="sl">x</button>';
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<!-- accessible names -->
|
|
95
|
+
<input id="n-placeholder" placeholder="Email address">
|
|
96
|
+
<input id="n-placeholder-title" title="Title wins" placeholder="Ignored">
|
|
97
|
+
<textarea id="n-placeholder-area" placeholder="Say more"></textarea>
|
|
98
|
+
<span id="n-ll1">First</span>
|
|
99
|
+
<span id="n-ll2" aria-label="Second label">ignored text</span>
|
|
100
|
+
<button id="n-labelledby" aria-labelledby="n-ll1 n-ll2">own text</button>
|
|
101
|
+
<span id="n-hid" style="display:none">Hidden <b>label</b></span>
|
|
102
|
+
<button id="n-lb-hidden" aria-labelledby="n-hid">own</button>
|
|
103
|
+
<span id="n-nest" aria-labelledby="n-ll1">Nest text</span>
|
|
104
|
+
<button id="n-lb-nested" aria-labelledby="n-nest">own</button>
|
|
105
|
+
<button id="n-lb-missing" aria-labelledby="nope-nothing">Falls back</button>
|
|
106
|
+
<button id="n-arialabel" aria-label="Close dialog">X</button>
|
|
107
|
+
<label for="n-labelfor">Email
|
|
108
|
+
address</label><input id="n-labelfor" type="text">
|
|
109
|
+
<label>Wrapped label <input id="n-wrapped" type="text"></label>
|
|
110
|
+
<img id="n-img" src="data:," alt="Company logo">
|
|
111
|
+
<input id="n-input-image" type="image" src="data:," alt="Go">
|
|
112
|
+
<map name="m2"><area id="n-area" href="#x" alt="Area name" shape="rect" coords="0,0,1,1"></map>
|
|
113
|
+
<input id="n-submit" type="submit" value="Send">
|
|
114
|
+
<input id="n-input-button" type="button" value="Plain value">
|
|
115
|
+
<input id="n-reset" type="reset" value="Clear">
|
|
116
|
+
<input id="n-submit-empty" type="submit">
|
|
117
|
+
<table id="n-table"><caption>Sales</caption><tbody><tr><td>1</td></tr></tbody></table>
|
|
118
|
+
<fieldset id="n-fieldset"><legend>Contact</legend><input type="text"></fieldset>
|
|
119
|
+
<figure id="n-figure"><img src="data:," alt=""><figcaption>Chart caption</figcaption></figure>
|
|
120
|
+
<button id="n-title" title="Tooltip only"></button>
|
|
121
|
+
<a id="n-title-fallback" href="#x" title="ignored title">Text wins</a>
|
|
122
|
+
<a id="n-embedded" href="#x">Search for <input type="text" value="cats"></a>
|
|
123
|
+
<a id="n-slider" href="#x">Rate <input type="range" min="0" max="10" value="7"> stars</a>
|
|
124
|
+
<a id="n-select" href="#x">Pick <select><option>One</option><option selected>Two</option></select></a>
|
|
125
|
+
<button id="n-content">Go <img src="data:," alt="icon"> now</button>
|
|
126
|
+
<a id="n-blocks" href="#x"><div>One</div><div>Two</div></a>
|
|
127
|
+
<a id="n-inline" href="#x"><span>Ab</span><span>cd</span></a>
|
|
128
|
+
<h2 id="n-heading"> Spaced
|
|
129
|
+
title </h2>
|
|
130
|
+
<button id="n-hidden-child">Visible<span style="display:none"> secret</span><span aria-hidden="true"> aria</span></button>
|
|
131
|
+
<div id="n-div-button" role="button">Div button</div>
|
|
132
|
+
<div id="n-generic">Generic text</div>
|
|
133
|
+
<ul><li id="n-li">Item text</li></ul>
|
|
134
|
+
<button id="n-unreferenced-hidden" style="display:none">Nope</button>
|
|
135
|
+
<input id="n-unlabelled" type="text">
|
|
136
|
+
|
|
137
|
+
<section id="cy-self" aria-labelledby="cy-self">Self</section>
|
|
138
|
+
<section id="cy-a" aria-labelledby="cy-b">A</section>
|
|
139
|
+
<section id="cy-b" aria-labelledby="cy-a">B</section>
|
|
140
|
+
<button id="cy-button" aria-labelledby="cy-button">Self button</button>
|
|
141
|
+
|
|
142
|
+
<!-- hidden cases -->
|
|
143
|
+
<button id="h-visible">Shown</button>
|
|
144
|
+
<button id="h-aria-hidden" aria-hidden="true">Aria hidden</button>
|
|
145
|
+
<div aria-hidden="true"><button id="h-anc-aria">Under aria-hidden</button></div>
|
|
146
|
+
<div style="display:none"><button id="h-anc-none">Under display none</button></div>
|
|
147
|
+
<button id="h-display-none" style="display:none">Display none</button>
|
|
148
|
+
<button id="h-attr" hidden>Hidden attribute</button>
|
|
149
|
+
<button id="h-visibility" style="visibility:hidden">Visibility hidden</button>
|
|
150
|
+
<div style="visibility:hidden"><button id="h-vis-parent-child" style="visibility:visible">Visible child</button><button id="h-vis-inherit">Inherits hidden</button></div>
|
|
151
|
+
|
|
152
|
+
<!-- queries -->
|
|
153
|
+
<div id="saves">
|
|
154
|
+
<button id="save-1">Save</button>
|
|
155
|
+
<button id="save-2">Save as</button>
|
|
156
|
+
<button id="save-3">save</button>
|
|
157
|
+
<button id="save-hidden" style="display:none">Save later</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div id="scope"><button id="scope-save">Save</button></div>
|
|
160
|
+
<div id="scope-button" role="button"><button id="scope-inner">Inner</button></div>
|
|
161
|
+
</body>
|
|
162
|
+
</html>
|