qat-web 6.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 +7 -0
- data/lib/capybara/core_ext.rb +7 -0
- data/lib/capybara/core_ext/capybara_error.rb +14 -0
- data/lib/capybara/core_ext/node/element.rb +35 -0
- data/lib/capybara/core_ext/selenium/node.rb +75 -0
- data/lib/headless/core_ext/random_display.rb +15 -0
- data/lib/qat/cli/plugins/web.rb +7 -0
- data/lib/qat/web.rb +12 -0
- data/lib/qat/web/browser.rb +21 -0
- data/lib/qat/web/browser/autoload.rb +22 -0
- data/lib/qat/web/browser/factory.rb +39 -0
- data/lib/qat/web/browser/firefox/harexporttrigger-0.5.0-beta.10.xpi +0 -0
- data/lib/qat/web/browser/firefox/loader/helper.rb +49 -0
- data/lib/qat/web/browser/html_dump.rb +46 -0
- data/lib/qat/web/browser/loader.rb +75 -0
- data/lib/qat/web/browser/profile.rb +92 -0
- data/lib/qat/web/browser/screenshot.rb +46 -0
- data/lib/qat/web/configuration.rb +46 -0
- data/lib/qat/web/cucumber.rb +19 -0
- data/lib/qat/web/drivers.rb +3 -0
- data/lib/qat/web/drivers/chrome.rb +5 -0
- data/lib/qat/web/drivers/default.rb +25 -0
- data/lib/qat/web/drivers/firefox.rb +6 -0
- data/lib/qat/web/drivers/firefox/har_exporter.rb +16 -0
- data/lib/qat/web/elements.rb +78 -0
- data/lib/qat/web/elements/base.rb +50 -0
- data/lib/qat/web/elements/collection.rb +16 -0
- data/lib/qat/web/elements/config.rb +19 -0
- data/lib/qat/web/elements/element.rb +16 -0
- data/lib/qat/web/elements/selector.rb +108 -0
- data/lib/qat/web/elements/waiters.rb +103 -0
- data/lib/qat/web/error.rb +14 -0
- data/lib/qat/web/error/enrichment.rb +14 -0
- data/lib/qat/web/exceptions.rb +15 -0
- data/lib/qat/web/finders.rb +49 -0
- data/lib/qat/web/hooks/common.rb +17 -0
- data/lib/qat/web/hooks/har_exporter.rb +32 -0
- data/lib/qat/web/hooks/html_dump.rb +21 -0
- data/lib/qat/web/hooks/screenshot.rb +20 -0
- data/lib/qat/web/page.rb +158 -0
- data/lib/qat/web/page/validators.rb +37 -0
- data/lib/qat/web/page_manager.rb +71 -0
- data/lib/qat/web/screen.rb +2 -0
- data/lib/qat/web/screen/autoload.rb +22 -0
- data/lib/qat/web/screen/factory.rb +68 -0
- data/lib/qat/web/screen/loader.rb +93 -0
- data/lib/qat/web/screen/wrapper.rb +96 -0
- data/lib/qat/web/version.rb +9 -0
- metadata +278 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'qat/logger'
|
3
|
+
|
4
|
+
module QAT::Web
|
5
|
+
module Browser
|
6
|
+
# Module to provide browsers specific profile.
|
7
|
+
module Profile
|
8
|
+
include QAT::Logger
|
9
|
+
|
10
|
+
ADDONS = {
|
11
|
+
firefox: {
|
12
|
+
har_exporter: 'harexporttrigger-0.5.0-beta.10.xpi'
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
def create_profile (driver, browser, properties, addons)
|
17
|
+
return nil unless properties && properties.any?
|
18
|
+
begin
|
19
|
+
method("#{driver}_#{browser}_profile".to_sym).call(properties, addons)
|
20
|
+
rescue NoMethodError => exception
|
21
|
+
if exception.message.match(/.*_.*_profile/)
|
22
|
+
raise(HandlerNotImplemented, "A profile handler for driver '#{driver.capitalize}' and/or '#{browser.capitalize}' does not exist at this moment!")
|
23
|
+
else
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def selenium_firefox_profile(properties, addons)
|
31
|
+
profile = selenium_profile(:firefox, properties)
|
32
|
+
|
33
|
+
profile = load_firefox_addons(profile, addons)
|
34
|
+
|
35
|
+
profile = set_profile(profile, properties)
|
36
|
+
|
37
|
+
{ profile: profile }
|
38
|
+
end
|
39
|
+
|
40
|
+
def selenium_chrome_profile (properties, addons)
|
41
|
+
profile = selenium_profile(:chrome, properties)
|
42
|
+
|
43
|
+
profile = set_profile(profile, properties)
|
44
|
+
|
45
|
+
{ prefs: profile.send(:prefs) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def selenium_profile(browser, properties)
|
49
|
+
begin
|
50
|
+
require 'selenium-webdriver'
|
51
|
+
rescue LoadError => e
|
52
|
+
if e.message =~ /selenium-webdriver/
|
53
|
+
raise LoadError, "QAT-Web is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
54
|
+
else
|
55
|
+
raise e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
Selenium::WebDriver.const_get(browser.capitalize)::Profile.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_profile(profile, properties)
|
62
|
+
properties.each { |name, changes| profile[name] = changes }
|
63
|
+
profile
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_firefox_addons(profile, addons)
|
67
|
+
|
68
|
+
addons.each do |addon|
|
69
|
+
path = if File.exist?(addon)
|
70
|
+
File.realpath(addon)
|
71
|
+
elsif ADDONS[:firefox][addon.to_sym]
|
72
|
+
File.expand_path(File.join(File.dirname(__FILE__), "firefox", ADDONS[:firefox][addon.to_sym]))
|
73
|
+
else
|
74
|
+
raise(InvalidAddonError, "Addon #{addon} could not be loaded!")
|
75
|
+
end
|
76
|
+
|
77
|
+
log.debug "Loading Firefox addon from '#{path}'"
|
78
|
+
|
79
|
+
profile.add_extension(path)
|
80
|
+
end if addons&.any?
|
81
|
+
|
82
|
+
|
83
|
+
profile
|
84
|
+
end
|
85
|
+
|
86
|
+
class HandlerNotImplemented < StandardError
|
87
|
+
end
|
88
|
+
class InvalidAddonError < StandardError
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative '../version'
|
2
|
+
require 'capybara'
|
3
|
+
require 'qat/logger'
|
4
|
+
|
5
|
+
module QAT::Web
|
6
|
+
# Browser namespace
|
7
|
+
module Browser
|
8
|
+
#Module to handle browser screenshots.
|
9
|
+
#@since 1.0.0
|
10
|
+
module Screenshot
|
11
|
+
include QAT::Logger
|
12
|
+
extend self
|
13
|
+
|
14
|
+
#Function to take a browser screenshot. Will handle the usage of driver that don't support screenshots.
|
15
|
+
#@param page [Capybara::Session] Browser to take screenshot from
|
16
|
+
#@param path [String] File path to save screenshot file
|
17
|
+
#@return [String/NilClass]File path to where the screenshot file was saved or nil if the browser doesn't support screenshots
|
18
|
+
#@since 1.0.0
|
19
|
+
def take_screenshot page=Capybara.current_session, path=screenshot_path
|
20
|
+
log.info {"Saving screenshot to #{path}"}
|
21
|
+
raise ArgumentError.new "File #{path} already exists! Choose another filename" if ::File.exists? path
|
22
|
+
path = page.save_screenshot path
|
23
|
+
log.info {"Screenshot available"}
|
24
|
+
path
|
25
|
+
rescue Capybara::NotSupportedByDriverError
|
26
|
+
log.warn {"Driver #{page.mode} does not support screenshots!"}
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
#Default screenshot path. Can be set with {#screenshot_path=}.
|
31
|
+
#@return [String] Screenshot path
|
32
|
+
#@since 1.0.0
|
33
|
+
def screenshot_path
|
34
|
+
@screenshot_path || ::File.join('public', "browser_#{Time.new.strftime("%H%M%S%L")}.png")
|
35
|
+
end
|
36
|
+
|
37
|
+
#Set new default screenshot path.
|
38
|
+
#@param value [String] Screenshot path
|
39
|
+
#@since 1.0.0
|
40
|
+
def screenshot_path= value
|
41
|
+
@screenshot_path = value
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'active_support/hash_with_indifferent_access'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
|
5
|
+
module QAT::Web
|
6
|
+
module Configuration
|
7
|
+
# Parses the configuration value into a usable Capybara selector
|
8
|
+
#@param config [Hash] value from configuration to save
|
9
|
+
#@option config [String] :type Selector type (Capybara.default_selector)
|
10
|
+
#@option config [String] :value Selector value
|
11
|
+
#@see Capybara::Queries::SelectorQuery
|
12
|
+
#@return [Array]
|
13
|
+
def parse_configuration config
|
14
|
+
Configuration.last_access = config
|
15
|
+
@last_access = config
|
16
|
+
type = config.fetch :type, Capybara.default_selector
|
17
|
+
value = config.fetch :value
|
18
|
+
opts = config.select { |key, _| Capybara::Queries::SelectorQuery::VALID_KEYS.include? key }
|
19
|
+
return type.to_sym, value, opts.to_hash.symbolize_keys
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the last value accessed from configuration
|
23
|
+
#@return [Hash]
|
24
|
+
def last_access
|
25
|
+
@last_access
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Returns the last value accessed from configuration in all QAT::Web::Configuration instances
|
30
|
+
#@return [Hash]
|
31
|
+
def last_access
|
32
|
+
@last_access
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets the last value accessed from configuration in all QAT::Web::Configuration instances
|
36
|
+
#@param value [Hash] value from configuration to save
|
37
|
+
#@option value [String] :type Selector type (Capybara.default_selector)
|
38
|
+
#@option value [String] :value Selector value
|
39
|
+
#@see Capybara::Queries::SelectorQuery
|
40
|
+
#@return [Hash]
|
41
|
+
def last_access=(value)
|
42
|
+
@last_access=value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'capybara/cucumber'
|
2
|
+
require_relative '../web'
|
3
|
+
require_relative 'hooks/html_dump'
|
4
|
+
require_relative 'hooks/screenshot'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
|
8
|
+
World QAT::Web::Finders
|
9
|
+
|
10
|
+
After do |scenario|
|
11
|
+
if scenario.failed?
|
12
|
+
if QAT::Web::Exceptions::GLOBAL.any? { |exception| scenario.exception.kind_of?(exception) }
|
13
|
+
QAT::Web::Browser.print_url
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Resets the configuration's access registry after each scenario
|
18
|
+
QAT::Web::Configuration.last_access = nil
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module QAT::Web
|
2
|
+
# Drivers namespace
|
3
|
+
module Drivers
|
4
|
+
# Module with drivers default for browsers.
|
5
|
+
module Default
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Returns the default drivers for browsers
|
9
|
+
#@return [Hash]
|
10
|
+
def mapping
|
11
|
+
setup_defaults unless @defaults
|
12
|
+
@defaults
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Initializes the default drivers for browsers
|
18
|
+
#@return [Hash]
|
19
|
+
def setup_defaults
|
20
|
+
@defaults ||= Hash.new(:selenium)
|
21
|
+
@defaults[:phantomjs] = :poltergeist
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'qat/web/error'
|
2
|
+
require 'qat/web/exceptions'
|
3
|
+
|
4
|
+
module QAT::Web
|
5
|
+
# Drivers namespace
|
6
|
+
module Drivers
|
7
|
+
# Module with drivers default for browsers.
|
8
|
+
module Firefox
|
9
|
+
class HarExporter
|
10
|
+
TOKEN = 'qat_web_automation'
|
11
|
+
|
12
|
+
EXCEPTIONS = QAT::Web::Exceptions::GLOBAL.dup
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative 'elements/selector'
|
2
|
+
require_relative 'elements/waiters'
|
3
|
+
require_relative 'elements/base'
|
4
|
+
require_relative 'elements/element'
|
5
|
+
require_relative 'elements/collection'
|
6
|
+
|
7
|
+
module QAT
|
8
|
+
module Web
|
9
|
+
# Helper methods to define elements and load configurations.
|
10
|
+
# Auxiliary methods for the elements are also defined here.
|
11
|
+
module Elements
|
12
|
+
include Selector
|
13
|
+
include Waiters
|
14
|
+
|
15
|
+
# Loads a configuration file
|
16
|
+
# @param file_path [String] configuration file path
|
17
|
+
# @return [Hash]
|
18
|
+
def load_elements_file(file_path)
|
19
|
+
begin
|
20
|
+
config = YAML::load(ERB.new(File.read(file_path)).result)
|
21
|
+
rescue Psych::SyntaxError
|
22
|
+
log.error "Failed to load file '#{file_path}'."
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
config
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
# Defines all dynamic web elements methods
|
30
|
+
# @param web_element [Element|Collection] web element configuration
|
31
|
+
def define_web_element_methods(web_element)
|
32
|
+
define_method(web_element.name) { web_element.finder.call }
|
33
|
+
define_selectors(web_element)
|
34
|
+
define_config_accessor(web_element)
|
35
|
+
define_waiters(web_element)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Defines the configuration accessor for the web elements
|
39
|
+
# @param web_element [Element] web element object
|
40
|
+
def define_config_accessor(web_element)
|
41
|
+
define_method "config_#{web_element.name}" do
|
42
|
+
web_element.config
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Defines the selector accessor for the web elements
|
47
|
+
# @param web_element [Element] web element object
|
48
|
+
def define_selectors(web_element)
|
49
|
+
define_method "selector_#{web_element.name}" do
|
50
|
+
web_element.selector
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Defines the waiting methods for the web elements
|
55
|
+
# @param web_element [Element] web element object
|
56
|
+
def define_waiters(web_element)
|
57
|
+
selector = web_element.selector
|
58
|
+
name = web_element.name
|
59
|
+
|
60
|
+
define_method "wait_until_#{name}_visible" do |timeout|
|
61
|
+
wait_until_visible(selector, timeout)
|
62
|
+
end
|
63
|
+
|
64
|
+
define_method "wait_until_#{name}_present" do |timeout|
|
65
|
+
wait_until_present(selector, timeout)
|
66
|
+
end
|
67
|
+
|
68
|
+
define_method "wait_while_#{name}_visible" do |timeout|
|
69
|
+
wait_until_not_visible(selector, timeout)
|
70
|
+
end
|
71
|
+
|
72
|
+
define_method "wait_while_#{name}_present" do |timeout|
|
73
|
+
wait_until_not_present(selector, timeout)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'capybara/dsl'
|
2
|
+
require_relative 'selector'
|
3
|
+
require_relative 'config'
|
4
|
+
|
5
|
+
module QAT
|
6
|
+
module Web
|
7
|
+
module Elements
|
8
|
+
# Web Element wrapper base class
|
9
|
+
class Base
|
10
|
+
include Capybara::DSL
|
11
|
+
include QAT::Logger
|
12
|
+
include Selector
|
13
|
+
include Config
|
14
|
+
|
15
|
+
attr_reader :name, :config, :selector
|
16
|
+
|
17
|
+
# New instance
|
18
|
+
# @param elements [Hash] Page Object related elements configuration
|
19
|
+
# @param args [Array] One or two arguments are expected. The first argument is the element *name*
|
20
|
+
# and the second an optional *configuration*.
|
21
|
+
# If none is given, it will be automatically loaded from the loaded configuration by the element name
|
22
|
+
def initialize(elements, *args)
|
23
|
+
get_element_info(elements, *args)
|
24
|
+
@selector = extract_selector(@config)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
# Parsed the configuration information for element or collection of elements
|
29
|
+
# @param args [Array] One or two arguments are expected. The first argument is the element *name*
|
30
|
+
# and the second an optional *configuration*.
|
31
|
+
# If none is given, it will be automatically loaded from the loaded configuration by the element name
|
32
|
+
# @raise ArgumentError if more than two arguments are given.
|
33
|
+
# @raise ArgumentError if a nil configuration is given.
|
34
|
+
# @raise ArgumentError if no configuration is found in cache.
|
35
|
+
# @return [Array]
|
36
|
+
def get_element_info(elements, *args)
|
37
|
+
raise ArgumentError.new "Wrong number of arguments! Expecting [name, configuration]!" if args.size > 2
|
38
|
+
|
39
|
+
@name, @config = args
|
40
|
+
raise ArgumentError.new "Configuration given for element '#{@name}' is nil!" if args.size == 2 && @config.nil?
|
41
|
+
|
42
|
+
@config ||= elements[:locators][@name]
|
43
|
+
raise ArgumentError.new "No configuration found for web element '#{@name}', please check definition" if @config.nil? or (@config.is_a? Hash and @config.empty?)
|
44
|
+
|
45
|
+
[@name, @config]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module QAT
|
4
|
+
module Web
|
5
|
+
module Elements
|
6
|
+
# Web Element wrapper class for web elements collection
|
7
|
+
class Collection < Base
|
8
|
+
# Returns the collection finder block
|
9
|
+
# @return [Proc]
|
10
|
+
def finder
|
11
|
+
proc { all(*@selector) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module QAT
|
2
|
+
module Web
|
3
|
+
module Elements
|
4
|
+
# Configuration handling helper
|
5
|
+
module Config
|
6
|
+
# Validates that the given config is in the correct format
|
7
|
+
# @param config [Hash] configuration hash
|
8
|
+
# @param collection [String] collection name
|
9
|
+
# @return [Boolean]
|
10
|
+
def valid_config?(config, collection)
|
11
|
+
unless config.kind_of?(Hash) && config.any?
|
12
|
+
raise(ArgumentError, "Empty or invalid configuration was given for #{collection.capitalize}! A hash like configuration is expected!")
|
13
|
+
end
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module QAT
|
4
|
+
module Web
|
5
|
+
module Elements
|
6
|
+
# Web Element wrapper class for a web element
|
7
|
+
class Element < Base
|
8
|
+
# Returns the element finder block
|
9
|
+
# @return [Proc]
|
10
|
+
def finder
|
11
|
+
proc { find(*@selector) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|