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,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec/expectations'
|
|
4
|
+
require_relative '../runtime'
|
|
5
|
+
require_relative 'matchers/base'
|
|
6
|
+
require_relative 'matchers/element'
|
|
7
|
+
require_relative 'matchers/page'
|
|
8
|
+
require_relative 'matchers/presence'
|
|
9
|
+
require_relative 'matchers/retrying_block'
|
|
10
|
+
|
|
11
|
+
module Raider
|
|
12
|
+
# Web-first RSpec matchers that retry until the page settles.
|
|
13
|
+
# rubocop:disable Naming/PredicatePrefix -- the matcher names are RSpec's vocabulary
|
|
14
|
+
module Matchers
|
|
15
|
+
def have_text(text, exact: false, wait: nil)
|
|
16
|
+
Element::HaveText.new(text, exact: exact, wait: wait)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def have_value(value, wait: nil)
|
|
20
|
+
Element::HaveValue.new(value, wait: wait)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def have_attribute(name, value = Element::HaveAttribute::PRESENT, wait: nil)
|
|
24
|
+
Element::HaveAttribute.new(name, value, wait: wait)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def be_visible(wait: nil)
|
|
28
|
+
Element::BeVisible.new(wait: wait)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def be_hidden(wait: nil)
|
|
32
|
+
Element::BeHidden.new(wait: wait)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def be_enabled(wait: nil)
|
|
36
|
+
Element::BeEnabled.new(wait: wait)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def be_disabled(wait: nil)
|
|
40
|
+
Element::BeDisabled.new(wait: wait)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def be_checked(wait: nil)
|
|
44
|
+
Element::BeChecked.new(wait: wait)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def have_element(locator = nil, wait: nil, **by)
|
|
48
|
+
raise ArgumentError, 'have_element takes a locator, not both a hash and keywords' if locator && !by.empty?
|
|
49
|
+
raise ArgumentError, 'have_element needs a locator' if locator.nil? && by.empty?
|
|
50
|
+
|
|
51
|
+
Presence::HaveElement.new(locator || by, wait: wait)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def have_css(selector, wait: nil)
|
|
55
|
+
have_element({ css: selector }, wait: wait)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def have_xpath(selector, wait: nil)
|
|
59
|
+
have_element({ xpath: selector }, wait: wait)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def have_count(count, wait: nil)
|
|
63
|
+
Page::HaveCount.new(count, wait: wait)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def have_title(expected, wait: nil)
|
|
67
|
+
Page::HaveTitle.new(expected, wait: wait)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def have_url(expected, wait: nil)
|
|
71
|
+
Page::HaveUrl.new(expected, wait: wait)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
# rubocop:enable Naming/PredicatePrefix
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
target = RSpec::Expectations::BlockExpectationTarget
|
|
78
|
+
target.prepend(Raider::Matchers::RetryingBlock) unless target.ancestors.include?(Raider::Matchers::RetryingBlock)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'config'
|
|
4
|
+
|
|
5
|
+
module Raider
|
|
6
|
+
# Builds the XPath and CSS strings behind the label, placeholder, alt,
|
|
7
|
+
# title, text and test_id locator keys. Every caller-supplied value passes
|
|
8
|
+
# through xpath_literal or the CSS escaper; nothing else is interpolated.
|
|
9
|
+
module Selectors
|
|
10
|
+
ATTRIBUTE_NAME = /\A[A-Za-z_][\w-]*\z/
|
|
11
|
+
LABEL_CONTROLS = %w[input textarea select button meter output progress]
|
|
12
|
+
.map { |tag| "self::#{tag}" }.join(' or ').freeze
|
|
13
|
+
EXCLUDED_TEXT = 'not(self::script or self::style or self::noscript or self::template or self::title or self::head) ' \
|
|
14
|
+
'and not(ancestor::head)'
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def xpath_literal(string)
|
|
19
|
+
value = string.to_s
|
|
20
|
+
return "'#{value}'" unless value.include?("'")
|
|
21
|
+
return %("#{value}") unless value.include?('"')
|
|
22
|
+
|
|
23
|
+
parts = value.split("'", -1).map { |part| "'#{part}'" }
|
|
24
|
+
"concat(#{parts.join(%q(, "'", ))})"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def label(value, exact:)
|
|
28
|
+
literal = xpath_literal(value)
|
|
29
|
+
[
|
|
30
|
+
".//*[@id = //label[#{match('.', literal, exact)}]/@for]",
|
|
31
|
+
".//label[#{match('.', literal, exact)}]//*[(#{LABEL_CONTROLS}) and not(self::input[@type='hidden'])]",
|
|
32
|
+
".//*[#{match('@aria-label', literal, exact)}]",
|
|
33
|
+
".//*[@aria-labelledby and #{match('string(id(@aria-labelledby))', literal, exact)}]"
|
|
34
|
+
].join(' | ')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def text(value, exact:)
|
|
38
|
+
matcher = match('.', xpath_literal(value), exact)
|
|
39
|
+
candidate = "#{EXCLUDED_TEXT} and #{matcher}"
|
|
40
|
+
".//*[#{candidate} and not(.//*[#{candidate}])]"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def attribute_css(name, value, exact:)
|
|
44
|
+
raise ArgumentError, "invalid attribute name: #{name}" unless name.to_s.match?(ATTRIBUTE_NAME)
|
|
45
|
+
|
|
46
|
+
escaped = value.to_s.gsub('\\') { '\\\\' }.gsub('"') { '\\"' }
|
|
47
|
+
"[#{name}#{exact ? '=' : '*='}\"#{escaped}\"]"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_id(value)
|
|
51
|
+
attribute_css(Raider.test_id_attribute, value, exact: true)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def match(expression, literal, exact)
|
|
55
|
+
return "normalize-space(#{expression}) = #{literal}" if exact
|
|
56
|
+
|
|
57
|
+
"contains(normalize-space(#{expression}), #{literal})"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'selenium-webdriver'
|
|
4
|
+
require_relative 'config'
|
|
5
|
+
require_relative 'errors'
|
|
6
|
+
require_relative 'context'
|
|
7
|
+
require_relative 'finders'
|
|
8
|
+
require_relative 'observer'
|
|
9
|
+
|
|
10
|
+
module Raider
|
|
11
|
+
# Renders a find_element argument list for error messages.
|
|
12
|
+
module Locator
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def describe(args)
|
|
16
|
+
how, what = args
|
|
17
|
+
args.size == 1 && how.is_a?(Hash) ? from_hash(how) : "{ #{how.to_s.inspect}: #{what.inspect} }"
|
|
18
|
+
rescue StandardError
|
|
19
|
+
args.inspect
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def from_hash(hash)
|
|
23
|
+
key, value = hash.first
|
|
24
|
+
return "by_role(#{role_arguments(value)})" if key == :role && value.is_a?(Array)
|
|
25
|
+
|
|
26
|
+
"{ #{key}: #{plain_arguments(value)} }"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def role_arguments(value)
|
|
30
|
+
name, options = value
|
|
31
|
+
["'#{name}'", *render_options(options)].join(', ')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def plain_arguments(value)
|
|
35
|
+
return value.inspect unless value.is_a?(Array)
|
|
36
|
+
|
|
37
|
+
text, options = value
|
|
38
|
+
[text.inspect, *render_options(options)].join(', ')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def render_options(options)
|
|
42
|
+
return [] unless options.is_a?(Hash)
|
|
43
|
+
|
|
44
|
+
options.map { |key, option| "#{key}: #{option.inspect}" }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Prepended to SearchContext: find_element must match exactly one element, and
|
|
49
|
+
# both finds wait for a match.
|
|
50
|
+
module StrictFinder
|
|
51
|
+
def find_element(*args)
|
|
52
|
+
Finders.ensure_registered
|
|
53
|
+
attempt = element_attempt(args) { super(*args) }
|
|
54
|
+
Observer.stamp(await_element(args, attempt), args, self)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_elements(*args)
|
|
58
|
+
return Observer.stamp_all(super, args, self) if Wait.immediate?
|
|
59
|
+
|
|
60
|
+
attempt = elements_attempt(args) { super(*args) }
|
|
61
|
+
found = []
|
|
62
|
+
begin
|
|
63
|
+
Wait.until(timeout: Raider.timeout) { (found = attempt.call).any? }
|
|
64
|
+
rescue TimeoutError
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
Observer.stamp_all(found, args, self)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def await_element(args, attempt)
|
|
73
|
+
return attempt.call || raise(Selenium::WebDriver::Error::NoSuchElementError, not_found(args)) if Wait.immediate?
|
|
74
|
+
|
|
75
|
+
started = clock
|
|
76
|
+
begin
|
|
77
|
+
Wait.until(timeout: Raider.timeout) { attempt.call }
|
|
78
|
+
rescue TimeoutError
|
|
79
|
+
raise Selenium::WebDriver::Error::NoSuchElementError,
|
|
80
|
+
"#{not_found(args)} (waited #{Describe.seconds(clock - started)}s)"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# A callable making one attempt at the element: the element, or nil when nothing
|
|
85
|
+
# matched yet. The context is decided here, once per find, not per attempt.
|
|
86
|
+
def element_attempt(args, &raw)
|
|
87
|
+
if RoleFinder.role_args?(args)
|
|
88
|
+
queried = role_attempt(args)
|
|
89
|
+
strict = Raider.strict
|
|
90
|
+
return -> { sole(args, queried.call, strict) }
|
|
91
|
+
end
|
|
92
|
+
return -> { native_or_nil(&raw) } unless Raider.strict && Context.web?(bridge)
|
|
93
|
+
|
|
94
|
+
-> { sole(args, Wait.immediately { find_elements(*args) }, true) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def elements_attempt(args, &raw)
|
|
98
|
+
RoleFinder.role_args?(args) ? role_attempt(args) : raw
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Validates the role locator and checks the context once, then queries the engine directly.
|
|
102
|
+
def role_attempt(args)
|
|
103
|
+
role, options = RoleFinder.parse(args)
|
|
104
|
+
raise NotSupported, RoleFinder::NATIVE_MESSAGE unless Context.web?(bridge)
|
|
105
|
+
|
|
106
|
+
scope = is_a?(Selenium::WebDriver::Driver) ? nil : self
|
|
107
|
+
-> { RoleQuery.run(bridge, scope, role, options) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def sole(args, found, strict)
|
|
111
|
+
raise AmbiguousMatch.new(Locator.describe(args), found) if strict && found.size > 1
|
|
112
|
+
|
|
113
|
+
found.first
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def native_or_nil
|
|
117
|
+
yield
|
|
118
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def not_found(args)
|
|
123
|
+
"no such element: #{Locator.describe(args)}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def clock
|
|
127
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Selenium aliases `first` to find_element on Driver and Element, which
|
|
133
|
+
# bypasses the prepend above; `find_elements(...).first` stays the non-strict escape.
|
|
134
|
+
module Raider
|
|
135
|
+
# Routes Selenium's `first` alias through the strict find_element.
|
|
136
|
+
module StrictFirst
|
|
137
|
+
def first(*args) = find_element(*args)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
Selenium::WebDriver::SearchContext.prepend(Raider::StrictFinder)
|
|
142
|
+
[Selenium::WebDriver::Driver, Selenium::WebDriver::Element].each { |klass| klass.prepend(Raider::StrictFirst) }
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'selenium-webdriver'
|
|
4
|
+
require_relative 'config'
|
|
5
|
+
require_relative 'errors'
|
|
6
|
+
|
|
7
|
+
module Raider
|
|
8
|
+
# The runtime wait loop. Nested waits share one deadline through DEADLINE_KEY
|
|
9
|
+
# (the same key the generated wait helper uses).
|
|
10
|
+
module Wait
|
|
11
|
+
DEADLINE_KEY = :raider_wait_deadline
|
|
12
|
+
# Like DEADLINE_KEY, fiber-scoped on purpose (Thread.current[] is per fiber).
|
|
13
|
+
IMMEDIATE_KEY = :raider_wait_immediate
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def deadline
|
|
17
|
+
Thread.current[DEADLINE_KEY]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Runs the block with finds making one attempt each (the caller does the waiting).
|
|
21
|
+
def immediately
|
|
22
|
+
previous = Thread.current[IMMEDIATE_KEY]
|
|
23
|
+
Thread.current[IMMEDIATE_KEY] = true
|
|
24
|
+
yield
|
|
25
|
+
ensure
|
|
26
|
+
Thread.current[IMMEDIATE_KEY] = previous
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def immediate?
|
|
30
|
+
Thread.current[IMMEDIATE_KEY] == true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Polls until the block returns a truthy value. The block may
|
|
34
|
+
# `throw :not_ready, reason` to say why it is not ready yet.
|
|
35
|
+
def until(timeout: nil, interval: 0.1)
|
|
36
|
+
limit, = limits(timeout)
|
|
37
|
+
within(limit) do
|
|
38
|
+
reason = nil
|
|
39
|
+
loop do
|
|
40
|
+
result = nil
|
|
41
|
+
thrown = catch(:not_ready) do
|
|
42
|
+
result = yield
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
return result if result
|
|
46
|
+
|
|
47
|
+
reason = thrown || reason
|
|
48
|
+
raise TimeoutError, reason || 'condition not met' if now >= limit
|
|
49
|
+
|
|
50
|
+
pause(limit, interval)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# Inside an enclosing deadline, a call without timeout waits for what is left;
|
|
58
|
+
# one with timeout waits that long, never past the enclosing deadline.
|
|
59
|
+
def limits(timeout)
|
|
60
|
+
started = now
|
|
61
|
+
enclosing = deadline
|
|
62
|
+
own = started + (timeout || Raider.timeout) if timeout || !enclosing
|
|
63
|
+
limit = [own, enclosing].compact.min
|
|
64
|
+
[limit, limit - started]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def within(limit)
|
|
68
|
+
enclosing = deadline
|
|
69
|
+
Thread.current[DEADLINE_KEY] = limit
|
|
70
|
+
yield
|
|
71
|
+
ensure
|
|
72
|
+
Thread.current[DEADLINE_KEY] = enclosing
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def pause(limit, interval)
|
|
76
|
+
sleep([[interval, limit - now].min, 0].max)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def now
|
|
80
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'selenium-webdriver'
|
|
4
|
+
require_relative 'runtime/config'
|
|
5
|
+
require_relative 'runtime/errors'
|
|
6
|
+
require_relative 'runtime/by_role'
|
|
7
|
+
require_relative 'runtime/wait'
|
|
8
|
+
require_relative 'runtime/context'
|
|
9
|
+
require_relative 'runtime/selectors'
|
|
10
|
+
require_relative 'runtime/locator_converter'
|
|
11
|
+
require_relative 'runtime/finders'
|
|
12
|
+
require_relative 'runtime/role_finder'
|
|
13
|
+
require_relative 'runtime/observer'
|
|
14
|
+
require_relative 'runtime/actionability'
|
|
15
|
+
require_relative 'runtime/strict'
|
|
16
|
+
require_relative 'runtime/actionable'
|
|
17
|
+
|
|
18
|
+
Raider::Finders.check_api
|
|
19
|
+
Raider::Finders.install
|
|
20
|
+
Raider::Finders.guard
|
|
@@ -4,21 +4,13 @@ require 'yaml'
|
|
|
4
4
|
|
|
5
5
|
module ScaffoldProjectDetector
|
|
6
6
|
AUTOMATION_GEMS = {
|
|
7
|
-
'watir' => 'watir',
|
|
8
7
|
'selenium-webdriver' => 'selenium',
|
|
9
8
|
'appium_lib' => 'appium',
|
|
10
9
|
'eyes_selenium' => 'selenium',
|
|
11
|
-
'axe-core-rspec' => 'selenium'
|
|
12
|
-
'axe-core-cucumber' => 'selenium'
|
|
13
|
-
}.freeze
|
|
14
|
-
|
|
15
|
-
FRAMEWORK_GEMS = {
|
|
16
|
-
'rspec' => 'rspec',
|
|
17
|
-
'cucumber' => 'cucumber'
|
|
10
|
+
'axe-core-rspec' => 'selenium'
|
|
18
11
|
}.freeze
|
|
19
12
|
|
|
20
13
|
CONFIG_PATH = 'config/config.yml'
|
|
21
|
-
WAIT_HELPER_PATH = 'helpers/wait_helper.rb'
|
|
22
14
|
|
|
23
15
|
class Error < StandardError; end
|
|
24
16
|
|
|
@@ -28,9 +20,7 @@ module ScaffoldProjectDetector
|
|
|
28
20
|
gemfile = read_gemfile
|
|
29
21
|
{
|
|
30
22
|
automation: detect_automation(gemfile),
|
|
31
|
-
|
|
32
|
-
has_spec: Dir.exist?('spec'),
|
|
33
|
-
has_features: Dir.exist?('features')
|
|
23
|
+
has_spec: Dir.exist?('spec')
|
|
34
24
|
}
|
|
35
25
|
end
|
|
36
26
|
|
|
@@ -41,13 +31,6 @@ module ScaffoldProjectDetector
|
|
|
41
31
|
'selenium'
|
|
42
32
|
end
|
|
43
33
|
|
|
44
|
-
def detect_framework(gemfile = read_gemfile)
|
|
45
|
-
FRAMEWORK_GEMS.each do |gem_name, framework|
|
|
46
|
-
return framework if gemfile.include?("'#{gem_name}'") || gemfile.include?("\"#{gem_name}\"")
|
|
47
|
-
end
|
|
48
|
-
'rspec'
|
|
49
|
-
end
|
|
50
|
-
|
|
51
34
|
def read_gemfile
|
|
52
35
|
return '' unless File.exist?('Gemfile')
|
|
53
36
|
|
|
@@ -58,15 +41,6 @@ module ScaffoldProjectDetector
|
|
|
58
41
|
detect_automation == 'selenium'
|
|
59
42
|
end
|
|
60
43
|
|
|
61
|
-
def watir?
|
|
62
|
-
detect_automation == 'watir'
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Projects made before WaitHelper, or whose owner removed it, do not have the file.
|
|
66
|
-
def wait_helper?
|
|
67
|
-
File.exist?(WAIT_HELPER_PATH)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
44
|
# Validates that the current directory looks like a Ruby Raider project.
|
|
71
45
|
# Returns an array of warning messages (empty = valid project).
|
|
72
46
|
def validate_project
|
|
@@ -21,10 +21,6 @@ class Scaffolding < Thor::Group
|
|
|
21
21
|
template('page_object.tt', default_path("page_objects/pages/#{normalized_name}.rb", '_page.rb'))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def generate_feature
|
|
25
|
-
template('feature.tt', default_path("features/#{normalized_name}.feature", '.feature'))
|
|
26
|
-
end
|
|
27
|
-
|
|
28
24
|
def generate_spec
|
|
29
25
|
template('spec.tt', default_path("spec/#{normalized_name}_page_spec.rb", '_spec.rb'))
|
|
30
26
|
end
|
|
@@ -33,10 +29,6 @@ class Scaffolding < Thor::Group
|
|
|
33
29
|
template('helper.tt', default_path("helpers/#{normalized_name}_helper.rb", '_helper.rb'))
|
|
34
30
|
end
|
|
35
31
|
|
|
36
|
-
def generate_steps
|
|
37
|
-
template('steps.tt', default_path("features/step_definitions/#{normalized_name}_steps.rb", '_steps.rb'))
|
|
38
|
-
end
|
|
39
|
-
|
|
40
32
|
def generate_component
|
|
41
33
|
template('component.tt', default_path("page_objects/components/#{normalized_name}.rb", '.rb'))
|
|
42
34
|
end
|
|
@@ -76,10 +68,6 @@ class Scaffolding < Thor::Group
|
|
|
76
68
|
ScaffoldProjectDetector.detect_automation
|
|
77
69
|
end
|
|
78
70
|
|
|
79
|
-
def framework_type
|
|
80
|
-
ScaffoldProjectDetector.detect_framework
|
|
81
|
-
end
|
|
82
|
-
|
|
83
71
|
def project_config
|
|
84
72
|
ScaffoldProjectDetector.config
|
|
85
73
|
end
|
|
@@ -88,14 +76,6 @@ class Scaffolding < Thor::Group
|
|
|
88
76
|
automation_type == 'selenium'
|
|
89
77
|
end
|
|
90
78
|
|
|
91
|
-
def watir?
|
|
92
|
-
automation_type == 'watir'
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def wait_helper?
|
|
96
|
-
ScaffoldProjectDetector.wait_helper?
|
|
97
|
-
end
|
|
98
|
-
|
|
99
79
|
def default_path(standard_path, file_type)
|
|
100
80
|
path ? "#{path}/#{normalized_name}#{file_type}" : standard_path
|
|
101
81
|
end
|
|
@@ -12,20 +12,6 @@ class <%= class_name %> < Component
|
|
|
12
12
|
private
|
|
13
13
|
|
|
14
14
|
# Elements
|
|
15
|
-
<%- if wait_helper? -%>
|
|
16
15
|
# Example: component.find_element(css: '.selector').click
|
|
17
|
-
<%- else -%>
|
|
18
|
-
# Example: driver.find_element(css: '.selector')
|
|
19
|
-
<%- end -%>
|
|
20
|
-
<%- elsif watir? -%>
|
|
21
|
-
|
|
22
|
-
def content
|
|
23
|
-
component.text
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
# Elements
|
|
29
|
-
# Example: browser.element(css: '.selector')
|
|
30
16
|
<%- end -%>
|
|
31
17
|
end
|
|
@@ -18,13 +18,7 @@ require_relative '../abstract/page'
|
|
|
18
18
|
|
|
19
19
|
<%= ' ' * module_parts.size %> # Elements
|
|
20
20
|
<%- if selenium? -%>
|
|
21
|
-
|
|
22
|
-
<%= ' ' * module_parts.size %> # Example: click({ id: 'element_id' })
|
|
23
|
-
<%- else -%>
|
|
24
|
-
<%= ' ' * module_parts.size %> # Example: driver.find_element(id: 'element_id')
|
|
25
|
-
<%- end -%>
|
|
26
|
-
<%- elsif watir? -%>
|
|
27
|
-
<%= ' ' * module_parts.size %> # Example: browser.text_field(id: 'element_id')
|
|
21
|
+
<%= ' ' * module_parts.size %> # Example: def submit_button = driver.find_element(by_role('button', name: 'Submit'))
|
|
28
22
|
<%- end -%>
|
|
29
23
|
<%= ' ' * module_parts.size %>end
|
|
30
24
|
<% module_parts.each_with_index do |_mod, i| -%>
|
|
@@ -44,13 +38,7 @@ class <%= page_class_name %> < Page
|
|
|
44
38
|
|
|
45
39
|
# Elements
|
|
46
40
|
<%- if selenium? -%>
|
|
47
|
-
|
|
48
|
-
# Example: click({ id: 'element_id' })
|
|
49
|
-
<%- else -%>
|
|
50
|
-
# Example: driver.find_element(id: 'element_id')
|
|
51
|
-
<%- end -%>
|
|
52
|
-
<%- elsif watir? -%>
|
|
53
|
-
# Example: browser.text_field(id: 'element_id')
|
|
41
|
+
# Example: def submit_button = driver.find_element(by_role('button', name: 'Submit'))
|
|
54
42
|
<%- end -%>
|
|
55
43
|
end
|
|
56
44
|
<% end -%>
|
|
@@ -5,18 +5,10 @@ require_relative '../page_objects/pages/<%= normalized_name %>'
|
|
|
5
5
|
describe '<%= page_class_name %>' do
|
|
6
6
|
<%- if selenium? -%>
|
|
7
7
|
let(:page) { <%= page_class_name %>.new(driver) }
|
|
8
|
-
<%- elsif watir? -%>
|
|
9
|
-
let(:page) { <%= page_class_name %>.new(browser) }
|
|
10
8
|
<%- end -%>
|
|
11
9
|
<%- if project_config['url'] -%>
|
|
12
10
|
|
|
13
|
-
before
|
|
14
|
-
<%- if selenium? -%>
|
|
15
|
-
driver.navigate.to "\#{project_config['url']}/<%= normalized_name %>"
|
|
16
|
-
<%- elsif watir? -%>
|
|
17
|
-
browser.goto "\#{project_config['url']}/<%= normalized_name %>"
|
|
18
|
-
<%- end -%>
|
|
19
|
-
end
|
|
11
|
+
before { page.visit }
|
|
20
12
|
<%- end -%>
|
|
21
13
|
|
|
22
14
|
it 'loads successfully' do
|
data/lib/utilities/utilities.rb
CHANGED
|
@@ -23,10 +23,6 @@ module Utilities
|
|
|
23
23
|
set('spec_path', path)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def feature_path=(path)
|
|
27
|
-
set('feature_path', path)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
26
|
def helper_path=(path)
|
|
31
27
|
set('helper_path', path)
|
|
32
28
|
end
|
|
@@ -68,6 +64,14 @@ module Utilities
|
|
|
68
64
|
set('headless', enabled)
|
|
69
65
|
end
|
|
70
66
|
|
|
67
|
+
def strict=(enabled)
|
|
68
|
+
set('strict', enabled)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def actionability=(enabled)
|
|
72
|
+
set('actionability', enabled)
|
|
73
|
+
end
|
|
74
|
+
|
|
71
75
|
def video_enabled=(enabled)
|
|
72
76
|
reload_config
|
|
73
77
|
video = @config['video']
|
|
@@ -91,13 +95,11 @@ module Utilities
|
|
|
91
95
|
end
|
|
92
96
|
|
|
93
97
|
def run(opts = nil)
|
|
94
|
-
|
|
95
|
-
system "#{command} #{opts}"
|
|
98
|
+
system "rspec spec/ #{opts}"
|
|
96
99
|
end
|
|
97
100
|
|
|
98
101
|
def parallel_run(opts = nil, _settings = nil)
|
|
99
|
-
|
|
100
|
-
system "#{command} #{opts}"
|
|
102
|
+
system "parallel_rspec spec/ #{opts}"
|
|
101
103
|
end
|
|
102
104
|
|
|
103
105
|
private
|
data/lib/version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.0
|
data/ruby_raider.gemspec
CHANGED
|
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
s.bindir = 'bin'
|
|
15
15
|
s.executables << 'raider'
|
|
16
16
|
s.add_development_dependency 'bundler-audit', '~> 0.9'
|
|
17
|
+
s.add_development_dependency 'chunky_png', '~> 1.4'
|
|
17
18
|
s.add_development_dependency 'rake', '~> 13.0.6'
|
|
18
19
|
s.add_development_dependency 'reek', '~> 6.5'
|
|
19
20
|
s.add_development_dependency 'rspec', '~> 3.11.0'
|
|
@@ -22,9 +23,9 @@ Gem::Specification.new do |s|
|
|
|
22
23
|
s.add_development_dependency 'rubocop-rspec', '~> 2.9.0'
|
|
23
24
|
s.add_development_dependency 'ruby_audit', '~> 3.1'
|
|
24
25
|
s.add_development_dependency 'rubyzip', '>= 2.3', '< 4'
|
|
25
|
-
s.add_development_dependency 'selenium-webdriver', '~> 4.0'
|
|
26
26
|
s.add_development_dependency 'steep', '~> 1.9'
|
|
27
27
|
|
|
28
|
+
s.add_runtime_dependency 'selenium-webdriver', '>= 4.30', '< 5'
|
|
28
29
|
s.add_runtime_dependency 'thor', '~> 1.2.1'
|
|
29
30
|
s.add_runtime_dependency 'tty-prompt', '~> 0.23.1'
|
|
30
31
|
end
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Type definitions for ScaffoldingCommands
|
|
2
2
|
class ScaffoldingCommands < Thor
|
|
3
3
|
def page: (String name) -> void
|
|
4
|
-
def feature: (String name) -> void
|
|
5
4
|
def spec: (String name) -> void
|
|
6
5
|
def helper: (String name) -> void
|
|
7
|
-
def steps: (String name) -> void
|
|
8
6
|
def component: (String name) -> void
|
|
9
7
|
def scaffold: (*String names) -> void
|
|
10
8
|
|
|
@@ -4,6 +4,8 @@ class UtilityCommands < Thor
|
|
|
4
4
|
def url: (String default_url) -> void
|
|
5
5
|
def browser: (?String? default_browser) -> void
|
|
6
6
|
def browser_options: (*String opts) -> void
|
|
7
|
+
def strict: (String toggle) -> void
|
|
8
|
+
def actionability: (String toggle) -> void
|
|
7
9
|
def raid: () -> void
|
|
8
10
|
def timeout: (String seconds) -> void
|
|
9
11
|
def viewport: (String dimensions) -> void
|
|
@@ -16,4 +18,8 @@ class UtilityCommands < Thor
|
|
|
16
18
|
def video_toggle: (bool enabled) -> void
|
|
17
19
|
def trace: (String action) -> void
|
|
18
20
|
def trace_toggle: (bool enabled) -> void
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def on?: (String toggle) -> bool
|
|
19
25
|
end
|