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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/lib/capybara/core_ext.rb +7 -0
  3. data/lib/capybara/core_ext/capybara_error.rb +14 -0
  4. data/lib/capybara/core_ext/node/element.rb +35 -0
  5. data/lib/capybara/core_ext/selenium/node.rb +75 -0
  6. data/lib/headless/core_ext/random_display.rb +15 -0
  7. data/lib/qat/cli/plugins/web.rb +7 -0
  8. data/lib/qat/web.rb +12 -0
  9. data/lib/qat/web/browser.rb +21 -0
  10. data/lib/qat/web/browser/autoload.rb +22 -0
  11. data/lib/qat/web/browser/factory.rb +39 -0
  12. data/lib/qat/web/browser/firefox/harexporttrigger-0.5.0-beta.10.xpi +0 -0
  13. data/lib/qat/web/browser/firefox/loader/helper.rb +49 -0
  14. data/lib/qat/web/browser/html_dump.rb +46 -0
  15. data/lib/qat/web/browser/loader.rb +75 -0
  16. data/lib/qat/web/browser/profile.rb +92 -0
  17. data/lib/qat/web/browser/screenshot.rb +46 -0
  18. data/lib/qat/web/configuration.rb +46 -0
  19. data/lib/qat/web/cucumber.rb +19 -0
  20. data/lib/qat/web/drivers.rb +3 -0
  21. data/lib/qat/web/drivers/chrome.rb +5 -0
  22. data/lib/qat/web/drivers/default.rb +25 -0
  23. data/lib/qat/web/drivers/firefox.rb +6 -0
  24. data/lib/qat/web/drivers/firefox/har_exporter.rb +16 -0
  25. data/lib/qat/web/elements.rb +78 -0
  26. data/lib/qat/web/elements/base.rb +50 -0
  27. data/lib/qat/web/elements/collection.rb +16 -0
  28. data/lib/qat/web/elements/config.rb +19 -0
  29. data/lib/qat/web/elements/element.rb +16 -0
  30. data/lib/qat/web/elements/selector.rb +108 -0
  31. data/lib/qat/web/elements/waiters.rb +103 -0
  32. data/lib/qat/web/error.rb +14 -0
  33. data/lib/qat/web/error/enrichment.rb +14 -0
  34. data/lib/qat/web/exceptions.rb +15 -0
  35. data/lib/qat/web/finders.rb +49 -0
  36. data/lib/qat/web/hooks/common.rb +17 -0
  37. data/lib/qat/web/hooks/har_exporter.rb +32 -0
  38. data/lib/qat/web/hooks/html_dump.rb +21 -0
  39. data/lib/qat/web/hooks/screenshot.rb +20 -0
  40. data/lib/qat/web/page.rb +158 -0
  41. data/lib/qat/web/page/validators.rb +37 -0
  42. data/lib/qat/web/page_manager.rb +71 -0
  43. data/lib/qat/web/screen.rb +2 -0
  44. data/lib/qat/web/screen/autoload.rb +22 -0
  45. data/lib/qat/web/screen/factory.rb +68 -0
  46. data/lib/qat/web/screen/loader.rb +93 -0
  47. data/lib/qat/web/screen/wrapper.rb +96 -0
  48. data/lib/qat/web/version.rb +9 -0
  49. 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,3 @@
1
+ require_relative 'drivers/firefox'
2
+ require_relative 'drivers/chrome'
3
+ require_relative 'drivers/default'
@@ -0,0 +1,5 @@
1
+ require 'capybara'
2
+
3
+ Capybara.register_driver :chrome do |app|
4
+ Capybara::Selenium::Driver.new(app, browser: :chrome)
5
+ 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,6 @@
1
+ require 'capybara'
2
+ require_relative 'firefox/har_exporter'
3
+
4
+ Capybara.register_driver :firefox do |app|
5
+ Capybara::Selenium::Driver.new(app, browser: :firefox)
6
+ 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