summaryrefslogtreecommitdiff
path: root/qa/qa/resource/base.rb
diff options
context:
space:
mode:
Diffstat (limited to 'qa/qa/resource/base.rb')
-rw-r--r--qa/qa/resource/base.rb218
1 files changed, 123 insertions, 95 deletions
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index 873ba353051..ca0087cf709 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -1,70 +1,143 @@
# frozen_string_literal: true
-require 'forwardable'
require 'capybara/dsl'
require 'active_support/core_ext/array/extract_options'
module QA
module Resource
class Base
- extend SingleForwardable
include ApiFabricator
extend Capybara::DSL
NoValueError = Class.new(RuntimeError)
- def_delegators :evaluator, :attribute
+ class << self
+ # Initialize new instance of class without fabrication
+ #
+ # @param [Proc] prepare_block
+ def init(&prepare_block)
+ new.tap(&prepare_block)
+ end
- def self.fabricate!(*args, &prepare_block)
- fabricate_via_api!(*args, &prepare_block)
- rescue NotImplementedError
- fabricate_via_browser_ui!(*args, &prepare_block)
- end
+ def fabricate!(*args, &prepare_block)
+ fabricate_via_api!(*args, &prepare_block)
+ rescue NotImplementedError
+ fabricate_via_browser_ui!(*args, &prepare_block)
+ end
- def self.fabricate_via_browser_ui!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ def fabricate_via_browser_ui!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
- current_url
+ current_url
+ end
end
- end
- def self.fabricate_via_api!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ def fabricate_via_api!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ raise NotImplementedError unless resource.api_support?
+
+ resource.eager_load_api_client!
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
+ end
+ end
+
+ def remove_via_api!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ resource.eager_load_api_client!
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
+ end
+ end
- raise NotImplementedError unless resource.api_support?
+ private
- resource.eager_load_api_client!
+ def do_fabricate!(resource:, prepare_block:, parents: [])
+ prepare_block.call(resource) if prepare_block
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
+ resource_web_url = yield
+ resource.web_url = resource_web_url
+
+ resource
+ end
+
+ def log_fabrication(method, resource, parents, args)
+ return yield unless Runtime::Env.debug?
+
+ start = Time.now
+ prefix = "==#{'=' * parents.size}>"
+ msg = [prefix]
+ msg << "Built a #{name}"
+ msg << "as a dependency of #{parents.last}" if parents.any?
+ msg << "via #{method}"
+
+ yield.tap do
+ msg << "in #{Time.now - start} seconds"
+ puts msg.join(' ')
+ puts if parents.empty?
+ end
+ end
+
+ # Define custom attribute
+ #
+ # @param [Symbol] name
+ # @return [void]
+ def attribute(name, &block)
+ (@attribute_names ||= []).push(name) # save added attributes
+
+ attr_writer(name)
+
+ define_method(name) do
+ instance_variable_get("@#{name}") || instance_variable_set("@#{name}", populate_attribute(name, block))
+ end
+ end
+
+ # Define multiple custom attributes
+ #
+ # @param [Array] names
+ # @return [void]
+ def attributes(*names)
+ names.each { |name| attribute(name) }
end
end
- def self.remove_via_api!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ # Override api reload! and update custom attributes from api_resource
+ #
+ api_reload = instance_method(:reload!)
+ define_method(:reload!) do
+ api_reload.bind_call(self)
+ return self unless api_resource
- resource.eager_load_api_client!
+ all_attributes.each do |attribute_name|
+ api_value = api_resource[attribute_name]
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
+ instance_variable_set("@#{attribute_name}", api_value) if api_value
end
+
+ self
end
+ attribute :web_url
+
def fabricate!(*_args)
raise NotImplementedError
end
def visit!
- Runtime::Logger.debug(%Q[Visiting #{self.class.name} at "#{web_url}"])
+ Runtime::Logger.debug(%(Visiting #{self.class.name} at "#{web_url}"))
# Just in case an async action is not yet complete
Support::WaitForRequests.wait_for_requests
@@ -78,14 +151,12 @@ module QA
Support::WaitForRequests.wait_for_requests
end
- def populate(*attributes)
- attributes.each(&method(:public_send))
+ def populate(*attribute_names)
+ attribute_names.each { |attribute_name| public_send(attribute_name) }
end
- def wait_until(max_duration: 60, sleep_interval: 0.1)
- QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval) do
- yield
- end
+ def wait_until(max_duration: 60, sleep_interval: 0.1, &block)
+ QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval, &block)
end
private
@@ -101,70 +172,27 @@ module QA
def attribute_value(name, block)
api_value = api_resource&.dig(name)
- if api_value && block
- log_having_both_api_result_and_block(name, api_value)
- end
+ log_having_both_api_result_and_block(name, api_value) if api_value && block
api_value || (block && instance_exec(&block))
end
- def log_having_both_api_result_and_block(name, api_value)
- QA::Runtime::Logger.info "<#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored."
+ # Get all defined attributes across all parents
+ #
+ # @return [Array<Symbol>]
+ def all_attributes
+ @all_attributes ||= self.class.ancestors
+ .select { |clazz| clazz <= QA::Resource::Base }
+ .map { |clazz| clazz.instance_variable_get(:@attribute_names) }
+ .flatten
+ .compact
end
- def self.do_fabricate!(resource:, prepare_block:, parents: [])
- prepare_block.call(resource) if prepare_block
-
- resource_web_url = yield
- resource.web_url = resource_web_url
-
- resource
- end
- private_class_method :do_fabricate!
-
- def self.log_fabrication(method, resource, parents, args)
- return yield unless Runtime::Env.debug?
-
- start = Time.now
- prefix = "==#{'=' * parents.size}>"
- msg = [prefix]
- msg << "Built a #{name}"
- msg << "as a dependency of #{parents.last}" if parents.any?
- msg << "via #{method}"
-
- yield.tap do
- msg << "in #{Time.now - start} seconds"
- puts msg.join(' ')
- puts if parents.empty?
- end
- end
- private_class_method :log_fabrication
-
- def self.evaluator
- @evaluator ||= Base::DSL.new(self)
- end
- private_class_method :evaluator
-
- class DSL
- def initialize(base)
- @base = base
- end
-
- def attribute(name, &block)
- @base.module_eval do
- attr_writer(name)
-
- define_method(name) do
- instance_variable_get("@#{name}") ||
- instance_variable_set(
- "@#{name}",
- populate_attribute(name, block))
- end
- end
- end
+ def log_having_both_api_result_and_block(name, api_value)
+ QA::Runtime::Logger.info(<<~MSG.strip)
+ <#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored.
+ MSG
end
-
- attribute :web_url
end
end
end