A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://gitlab.com/gitlab-org/gitlab-qa/-/merge_requests/136.patch below:

From 3dd20ecd404794d301d6952cc2b663717cded1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 24 May 2018 17:17:04 +0200 Subject: [PATCH 1/7] Move the QA framework from GitLab to GitLab QA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .rubocop.yml | 3 + gitlab-qa.gemspec | 2 + lib/gitlab/qa.rb | 33 +++-- lib/gitlab/qa/component/gitlab.rb | 2 +- lib/gitlab/qa/component/ldap.rb | 2 +- lib/gitlab/qa/component/specs.rb | 2 +- lib/gitlab/qa/framework.rb | 26 ++++ lib/gitlab/qa/framework/factory/base.rb | 64 +++++++++ lib/gitlab/qa/framework/factory/dependency.rb | 43 ++++++ lib/gitlab/qa/framework/factory/product.rb | 34 +++++ lib/gitlab/qa/framework/page/base.rb | 128 ++++++++++++++++++ lib/gitlab/qa/framework/page/element.rb | 36 +++++ lib/gitlab/qa/framework/page/validator.rb | 56 ++++++++ lib/gitlab/qa/framework/page/view.rb | 59 ++++++++ lib/gitlab/qa/framework/scenario/actable.rb | 27 ++++ lib/gitlab/qa/framework/scenario/template.rb | 20 +++ lib/gitlab/qa/scenario/actable.rb | 25 ---- lib/gitlab/qa/scenario/template.rb | 18 --- lib/gitlab/qa/scenario/test/instance/any.rb | 2 +- lib/gitlab/qa/scenario/test/instance/image.rb | 2 +- .../qa/scenario/test/instance/staging.rb | 2 +- .../qa/scenario/test/integration/geo.rb | 2 +- .../qa/scenario/test/integration/ldap.rb | 2 +- .../scenario/test/integration/mattermost.rb | 2 +- lib/gitlab/qa/scenario/test/omnibus/image.rb | 2 +- lib/gitlab/qa/scenario/test/omnibus/update.rb | 2 +- .../qa/scenario/test/omnibus/upgrade.rb | 2 +- lib/gitlab/qa/scenario/test/sanity/version.rb | 2 +- spec/gitlab/qa/framework/factory/base_spec.rb | 127 +++++++++++++++++ .../qa/framework/factory/dependency_spec.rb | 72 ++++++++++ .../qa/framework/factory/product_spec.rb | 39 ++++++ spec/gitlab/qa/framework/page/base_spec.rb | 62 +++++++++ spec/gitlab/qa/framework/page/element_spec.rb | 51 +++++++ .../qa/framework/page/validator_spec.rb | 85 ++++++++++++ spec/gitlab/qa/framework/page/view_spec.rb | 70 ++++++++++ .../{ => framework}/scenario/actable_spec.rb | 4 +- 36 files changed, 1037 insertions(+), 73 deletions(-) create mode 100644 lib/gitlab/qa/framework.rb create mode 100644 lib/gitlab/qa/framework/factory/base.rb create mode 100644 lib/gitlab/qa/framework/factory/dependency.rb create mode 100644 lib/gitlab/qa/framework/factory/product.rb create mode 100644 lib/gitlab/qa/framework/page/base.rb create mode 100644 lib/gitlab/qa/framework/page/element.rb create mode 100644 lib/gitlab/qa/framework/page/validator.rb create mode 100644 lib/gitlab/qa/framework/page/view.rb create mode 100644 lib/gitlab/qa/framework/scenario/actable.rb create mode 100644 lib/gitlab/qa/framework/scenario/template.rb delete mode 100644 lib/gitlab/qa/scenario/actable.rb delete mode 100644 lib/gitlab/qa/scenario/template.rb create mode 100644 spec/gitlab/qa/framework/factory/base_spec.rb create mode 100644 spec/gitlab/qa/framework/factory/dependency_spec.rb create mode 100644 spec/gitlab/qa/framework/factory/product_spec.rb create mode 100644 spec/gitlab/qa/framework/page/base_spec.rb create mode 100644 spec/gitlab/qa/framework/page/element_spec.rb create mode 100644 spec/gitlab/qa/framework/page/validator_spec.rb create mode 100644 spec/gitlab/qa/framework/page/view_spec.rb rename spec/gitlab/qa/{ => framework}/scenario/actable_spec.rb (88%) diff --git a/.rubocop.yml b/.rubocop.yml index c4839d51a..bad20ee41 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -28,3 +28,6 @@ Style/ModuleFunction: Style/SignalException: Enabled: false + +GitlabSecurity/PublicSend: + Enabled: false diff --git a/gitlab-qa.gemspec b/gitlab-qa.gemspec index ffae94576..e2977b7e0 100644 --- a/gitlab-qa.gemspec +++ b/gitlab-qa.gemspec @@ -18,6 +18,8 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] + spec.add_runtime_dependency 'capybara', '~> 2.16' + # Some dependencies are pinned, to prevent new cops from breaking the CI pipelines spec.add_development_dependency 'gitlab-styles', '2.2.0' spec.add_development_dependency 'pry', '~> 0.11' diff --git a/lib/gitlab/qa.rb b/lib/gitlab/qa.rb index c15630f2e..1469d980a 100644 --- a/lib/gitlab/qa.rb +++ b/lib/gitlab/qa.rb @@ -1,8 +1,21 @@ -$LOAD_PATH << File.expand_path(__dir__) +lib = File.expand_path(__dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) module Gitlab module QA - autoload :Release, 'qa/release' + module Component + autoload :Gitlab, 'qa/component/gitlab' + autoload :LDAP, 'qa/component/ldap' + autoload :Specs, 'qa/component/specs' + autoload :Staging, 'qa/component/staging' + end + + module Docker + autoload :Command, 'qa/docker/command' + autoload :Engine, 'qa/docker/engine' + autoload :Shellout, 'qa/docker/shellout' + autoload :Volumes, 'qa/docker/volumes' + end module Runtime autoload :Env, 'qa/runtime/env' @@ -37,18 +50,8 @@ module Gitlab end end - module Component - autoload :Gitlab, 'qa/component/gitlab' - autoload :LDAP, 'qa/component/ldap' - autoload :Specs, 'qa/component/specs' - autoload :Staging, 'qa/component/staging' - end - - module Docker - autoload :Command, 'qa/docker/command' - autoload :Engine, 'qa/docker/engine' - autoload :Shellout, 'qa/docker/shellout' - autoload :Volumes, 'qa/docker/volumes' - end + autoload :Release, 'qa/release' end end + +require 'qa/framework' diff --git a/lib/gitlab/qa/component/gitlab.rb b/lib/gitlab/qa/component/gitlab.rb index f0b36ddd4..a9b26a564 100644 --- a/lib/gitlab/qa/component/gitlab.rb +++ b/lib/gitlab/qa/component/gitlab.rb @@ -8,7 +8,7 @@ module Gitlab module Component class Gitlab extend Forwardable - include Scenario::Actable + include Framework::Scenario::Actable attr_reader :release, :docker attr_accessor :volumes, :network, :environment diff --git a/lib/gitlab/qa/component/ldap.rb b/lib/gitlab/qa/component/ldap.rb index 942cfefec..461c0c005 100644 --- a/lib/gitlab/qa/component/ldap.rb +++ b/lib/gitlab/qa/component/ldap.rb @@ -16,7 +16,7 @@ module Gitlab module QA module Component class LDAP - include Scenario::Actable + include Framework::Scenario::Actable LDAP_IMAGE = 'osixia/openldap'.freeze LDAP_IMAGE_TAG = 'latest'.freeze diff --git a/lib/gitlab/qa/component/specs.rb b/lib/gitlab/qa/component/specs.rb index f331deb5c..6915fbb57 100644 --- a/lib/gitlab/qa/component/specs.rb +++ b/lib/gitlab/qa/component/specs.rb @@ -5,7 +5,7 @@ module Gitlab # This class represents GitLab QA specs image that is implemented in # the `qa/` directory located in GitLab CE / EE repositories. # - class Specs < Scenario::Template + class Specs < Framework::Scenario::Template attr_accessor :suite, :release, :network, :args def initialize diff --git a/lib/gitlab/qa/framework.rb b/lib/gitlab/qa/framework.rb new file mode 100644 index 000000000..161d6fdf3 --- /dev/null +++ b/lib/gitlab/qa/framework.rb @@ -0,0 +1,26 @@ +lib = File.expand_path('../', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +module Gitlab + module QA + module Framework + module Factory + autoload :Base, 'qa/framework/factory/base' + autoload :Dependency, 'qa/framework/factory/dependency' + autoload :Product, 'qa/framework/factory/product' + end + + module Page + autoload :Base, 'qa/framework/page/base' + autoload :Element, 'qa/framework/page/element' + autoload :Validator, 'qa/framework/page/validator' + autoload :View, 'qa/framework/page/view' + end + + module Scenario + autoload :Actable, 'qa/framework/scenario/actable' + autoload :Template, 'qa/framework/scenario/template' + end + end + end +end diff --git a/lib/gitlab/qa/framework/factory/base.rb b/lib/gitlab/qa/framework/factory/base.rb new file mode 100644 index 000000000..8207128e6 --- /dev/null +++ b/lib/gitlab/qa/framework/factory/base.rb @@ -0,0 +1,64 @@ +require 'forwardable' + +module Gitlab + module QA + module Framework + module Factory + class Base + extend SingleForwardable + + def_delegators :evaluator, :dependency, :dependencies + def_delegators :evaluator, :product, :attributes + + def fabricate!(*_args) + raise NotImplementedError + end + + def self.fabricate!(*args) + new.tap do |factory| + yield factory if block_given? + + dependencies.each do |name, signature| + Factory::Dependency.new(name, factory, signature).build! + end + + factory.fabricate!(*args) + + break Factory::Product.populate!(factory) + end + end + + def self.evaluator + @evaluator ||= Factory::Base::DSL.new(self) + end + + class DSL + attr_reader :dependencies, :attributes + + def initialize(base) + @base = base + @dependencies = {} + @attributes = {} + end + + def dependency(factory, as:, &block) + as.tap do |name| + @base.class_eval { attr_accessor name } + + Dependency::Signature.new(factory, block).tap do |signature| + @dependencies.store(name, signature) + end + end + end + + def product(attribute, &block) + Product::Attribute.new(attribute, block).tap do |signature| + @attributes.store(attribute, signature) + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/factory/dependency.rb b/lib/gitlab/qa/framework/factory/dependency.rb new file mode 100644 index 000000000..31cc41307 --- /dev/null +++ b/lib/gitlab/qa/framework/factory/dependency.rb @@ -0,0 +1,43 @@ +module Gitlab + module QA + module Framework + module Factory + class Dependency + Signature = Struct.new(:factory, :block) + + def initialize(name, factory, signature) + @name = name + @factory = factory + @signature = signature + end + + def overridden? + !!@factory.public_send(@name) + end + + def build! + return if overridden? + + Builder.new(@signature, @factory).fabricate!.tap do |product| + @factory.public_send("#{@name}=", product) + end + end + + class Builder + def initialize(signature, caller_factory) + @factory = signature.factory + @block = signature.block + @caller_factory = caller_factory + end + + def fabricate! + @factory.fabricate! do |factory| + @block&.call(factory, @caller_factory) + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/factory/product.rb b/lib/gitlab/qa/framework/factory/product.rb new file mode 100644 index 000000000..88ebb0440 --- /dev/null +++ b/lib/gitlab/qa/framework/factory/product.rb @@ -0,0 +1,34 @@ +require 'capybara/dsl' + +module Gitlab + module QA + module Framework + module Factory + class Product + include Capybara::DSL + + Attribute = Struct.new(:name, :block) + + def initialize + @location = current_url + end + + def visit! + visit @location + end + + def self.populate!(factory) + new.tap do |product| + factory.class.attributes.each_value do |attribute| + product.instance_exec(factory, attribute.block) do |factory, block| + value = block.call(factory) + product.define_singleton_method(attribute.name) { value } + end + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/page/base.rb b/lib/gitlab/qa/framework/page/base.rb new file mode 100644 index 000000000..a9e31329c --- /dev/null +++ b/lib/gitlab/qa/framework/page/base.rb @@ -0,0 +1,128 @@ +require 'capybara/dsl' + +module Gitlab + module QA + module Framework + module Page + class Base + include Capybara::DSL + include Gitlab::QA::Framework::Scenario::Actable + extend SingleForwardable + + def_delegators :evaluator, :view, :views + + def refresh + visit current_url + end + + def wait(max: 60, time: 1, reload: true) + start = Time.now + + while Time.now - start < max + result = yield + return result if result + + sleep(time) + + refresh if reload + end + + false + end + + def scroll_to(selector, text: nil) + page.execute_script <<~JS + var elements = Array.from(document.querySelectorAll('#{selector}')); + var text = '#{text}'; + + if (text.length > 0) { + elements.find(e => e.textContent === text).scrollIntoView(); + } else { + elements[0].scrollIntoView(); + } + JS + + page.within(selector) { yield } if block_given? + end + + # Returns true if successfully GETs the given URL + # Useful because `page.status_code` is unsupported by our driver, and + # we don't have access to the `response` to use `have_http_status`. + def asset_exists?(url) + page.execute_script <<~JS + xhr = new XMLHttpRequest(); + xhr.open('GET', '#{url}', true); + xhr.send(); + JS + + return false unless wait(time: 0.5, max: 60, reload: false) do + page.evaluate_script('xhr.readyState == XMLHttpRequest.DONE') + end + + page.evaluate_script('xhr.status') == 200 + end + + def find_element(name) + find(element_selector_css(name)) + end + + def all_elements(name) + all(element_selector_css(name)) + end + + def click_element(name) + find_element(name).click + end + + def fill_element(name, content) + find_element(name).set(content) + end + + def within_element(name) + page.within(element_selector_css(name)) do + yield + end + end + + def element_selector_css(name) + Page::Element.new(name).selector_css + end + + def self.path + raise NotImplementedError + end + + def self.evaluator + @evaluator ||= Page::Base::DSL.new + end + + def self.errors + if views.empty? + return ["Page class does not have views / elements defined!"] + end + + views.map(&:errors).flatten + end + + def self.elements + views.map(&:elements).flatten + end + + class DSL + attr_reader :views + + def initialize + @views = [] + end + + def view(path, &block) + Page::View.evaluate(&block).tap do |view| + @views.push(Page::View.new(path, view.elements)) + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/page/element.rb b/lib/gitlab/qa/framework/page/element.rb new file mode 100644 index 000000000..8457dc202 --- /dev/null +++ b/lib/gitlab/qa/framework/page/element.rb @@ -0,0 +1,36 @@ +module Gitlab + module QA + module Framework + module Page + class Element + attr_reader :name + + def initialize(name, pattern = nil) + @name = name + @pattern = pattern || selector + end + + def selector + "qa-#{@name.to_s.tr('_', '-')}" + end + + def selector_css + ".#{selector}" + end + + def expression + if @pattern.is_a?(String) + @_regexp ||= Regexp.new(Regexp.escape(@pattern)) + else + @pattern + end + end + + def matches?(line) + !!(line =~ expression) + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/page/validator.rb b/lib/gitlab/qa/framework/page/validator.rb new file mode 100644 index 000000000..8d69f5a44 --- /dev/null +++ b/lib/gitlab/qa/framework/page/validator.rb @@ -0,0 +1,56 @@ +module Gitlab + module QA + module Framework + module Page + class Validator + ValidationError = Class.new(StandardError) + + Error = Struct.new(:page, :message) do + def to_s + "Error: #{page} - #{message}" + end + end + + def initialize(constant) + @module = constant + end + + def constants + @consts ||= @module.constants.map do |const| + @module.const_get(const) + end + end + + def descendants + @descendants ||= constants.map do |const| + case const + when Class + const if const < ::Gitlab::QA::Framework::Page::Base + when Module + Page::Validator.new(const).descendants + end + end + + @descendants.flatten.compact + end + + def errors + [].tap do |errors| + descendants.each do |page| + page.errors.each do |message| + errors.push(Error.new(page.name, message)) + end + end + end + end + + def validate! + return if errors.none? + + raise ValidationError, 'Page views / elements validation error!' + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/page/view.rb b/lib/gitlab/qa/framework/page/view.rb new file mode 100644 index 000000000..29c08ac99 --- /dev/null +++ b/lib/gitlab/qa/framework/page/view.rb @@ -0,0 +1,59 @@ +module Gitlab + module QA + module Framework + module Page + class View + attr_reader :path, :elements + + def initialize(path, elements) + @path = path + @elements = elements + end + + def pathname + @pathname ||= Pathname.new(File.join(__dir__, '../../../../../', @path)) + .cleanpath.expand_path + end + + def errors + unless pathname.readable? + return ["Missing view partial `#{pathname}`!"] + end + + ## + # Reduce required elements by streaming view and making assertions on + # elements' existence. + # + @missing ||= @elements.dup.tap do |elements| + File.foreach(pathname.to_s) do |line| + elements.reject! { |element| element.matches?(line) } + end + end + + @missing.map do |missing| + "Missing element `#{missing.name}` in `#{pathname}` view partial!" + end + end + + def self.evaluate(&block) + Page::View::DSL.new.tap do |evaluator| + evaluator.instance_exec(&block) if block_given? + end + end + + class DSL + attr_reader :elements + + def initialize + @elements = [] + end + + def element(name, pattern = nil) + @elements.push(Page::Element.new(name, pattern)) + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/actable.rb b/lib/gitlab/qa/framework/scenario/actable.rb new file mode 100644 index 000000000..b021fc008 --- /dev/null +++ b/lib/gitlab/qa/framework/scenario/actable.rb @@ -0,0 +1,27 @@ +module Gitlab + module QA + module Framework + module Scenario + module Actable + def act(*args, &block) + instance_exec(*args, &block) + end + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def perform + yield new if block_given? + end + + def act(*args, &block) + new.act(*args, &block) + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/template.rb b/lib/gitlab/qa/framework/scenario/template.rb new file mode 100644 index 000000000..44b567b18 --- /dev/null +++ b/lib/gitlab/qa/framework/scenario/template.rb @@ -0,0 +1,20 @@ +module Gitlab + module QA + module Framework + module Scenario + class Template + def self.perform(*args) + new.tap do |scenario| + yield scenario if block_given? + return scenario.perform(*args) + end + end + + def perform(*_args) + raise NotImplementedError + end + end + end + end + end +end diff --git a/lib/gitlab/qa/scenario/actable.rb b/lib/gitlab/qa/scenario/actable.rb deleted file mode 100644 index 23a67b09b..000000000 --- a/lib/gitlab/qa/scenario/actable.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Gitlab - module QA - module Scenario - module Actable - def act(*args, &block) - instance_exec(*args, &block) - end - - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def perform - yield new if block_given? - end - - def act(*args, &block) - new.act(*args, &block) - end - end - end - end - end -end diff --git a/lib/gitlab/qa/scenario/template.rb b/lib/gitlab/qa/scenario/template.rb deleted file mode 100644 index 506004ad1..000000000 --- a/lib/gitlab/qa/scenario/template.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module QA - module Scenario - class Template - def self.perform(*args) - new.tap do |scenario| - yield scenario if block_given? - return scenario.perform(*args) - end - end - - def perform(*_args) - raise NotImplementedError - end - end - end - end -end diff --git a/lib/gitlab/qa/scenario/test/instance/any.rb b/lib/gitlab/qa/scenario/test/instance/any.rb index af7ab3a6d..0576019c8 100644 --- a/lib/gitlab/qa/scenario/test/instance/any.rb +++ b/lib/gitlab/qa/scenario/test/instance/any.rb @@ -7,7 +7,7 @@ module Gitlab # Run test suite against any GitLab instance, # including staging and on-premises installation. # - class Any < Scenario::Template + class Any < Framework::Scenario::Template def perform(edition, tag, address) release = Release.new(edition).tap do |r| r.tag = tag diff --git a/lib/gitlab/qa/scenario/test/instance/image.rb b/lib/gitlab/qa/scenario/test/instance/image.rb index 1ddf1677f..e8b8e56a3 100644 --- a/lib/gitlab/qa/scenario/test/instance/image.rb +++ b/lib/gitlab/qa/scenario/test/instance/image.rb @@ -3,7 +3,7 @@ module Gitlab module Scenario module Test module Instance - class Image < Scenario::Template + class Image < Framework::Scenario::Template attr_writer :volumes def initialize diff --git a/lib/gitlab/qa/scenario/test/instance/staging.rb b/lib/gitlab/qa/scenario/test/instance/staging.rb index 2b57e3b3d..c223cc261 100644 --- a/lib/gitlab/qa/scenario/test/instance/staging.rb +++ b/lib/gitlab/qa/scenario/test/instance/staging.rb @@ -6,7 +6,7 @@ module Gitlab ## # Run test suite against staging.gitlab.com # - class Staging < Scenario::Template + class Staging < Framework::Scenario::Template def perform(*) Runtime::Env.require_no_license! diff --git a/lib/gitlab/qa/scenario/test/integration/geo.rb b/lib/gitlab/qa/scenario/test/integration/geo.rb index 407b9f649..13b267869 100644 --- a/lib/gitlab/qa/scenario/test/integration/geo.rb +++ b/lib/gitlab/qa/scenario/test/integration/geo.rb @@ -3,7 +3,7 @@ module Gitlab module Scenario module Test module Integration - class Geo < Scenario::Template + class Geo < Framework::Scenario::Template ## # rubocop:disable Lint/MissingCopEnableDirective # rubocop:disable Metrics/MethodLength diff --git a/lib/gitlab/qa/scenario/test/integration/ldap.rb b/lib/gitlab/qa/scenario/test/integration/ldap.rb index 09de8d8ae..539d8cf6e 100644 --- a/lib/gitlab/qa/scenario/test/integration/ldap.rb +++ b/lib/gitlab/qa/scenario/test/integration/ldap.rb @@ -5,7 +5,7 @@ module Gitlab module Scenario module Test module Integration - class LDAP < Scenario::Template + class LDAP < Framework::Scenario::Template # rubocop:disable Metrics/AbcSize def perform(release) Component::Gitlab.perform do |gitlab| diff --git a/lib/gitlab/qa/scenario/test/integration/mattermost.rb b/lib/gitlab/qa/scenario/test/integration/mattermost.rb index b9685c624..19bdb7fb8 100644 --- a/lib/gitlab/qa/scenario/test/integration/mattermost.rb +++ b/lib/gitlab/qa/scenario/test/integration/mattermost.rb @@ -3,7 +3,7 @@ module Gitlab module Scenario module Test module Integration - class Mattermost < Scenario::Template + class Mattermost < Framework::Scenario::Template def perform(release) Component::Gitlab.perform do |gitlab| gitlab.release = release diff --git a/lib/gitlab/qa/scenario/test/omnibus/image.rb b/lib/gitlab/qa/scenario/test/omnibus/image.rb index ccc7a1b70..38f232d69 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/image.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/image.rb @@ -3,7 +3,7 @@ module Gitlab module Scenario module Test module Omnibus - class Image < Scenario::Template + class Image < Framework::Scenario::Template def perform(release) Component::Gitlab.perform do |gitlab| gitlab.release = release diff --git a/lib/gitlab/qa/scenario/test/omnibus/update.rb b/lib/gitlab/qa/scenario/test/omnibus/update.rb index 938bb14cf..05378ea88 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/update.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/update.rb @@ -6,7 +6,7 @@ module Gitlab module Scenario module Test module Omnibus - class Update < Scenario::Template + class Update < Framework::Scenario::Template def perform(next_release) next_release = Release.new(next_release) diff --git a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb index e1a0ceeb8..3a3c1b7cb 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb @@ -6,7 +6,7 @@ module Gitlab module Scenario module Test module Omnibus - class Upgrade < Scenario::Template + class Upgrade < Framework::Scenario::Template def perform(image = 'CE') ce_release = Release.new(image) diff --git a/lib/gitlab/qa/scenario/test/sanity/version.rb b/lib/gitlab/qa/scenario/test/sanity/version.rb index d4a08ff24..95ddcedea 100644 --- a/lib/gitlab/qa/scenario/test/sanity/version.rb +++ b/lib/gitlab/qa/scenario/test/sanity/version.rb @@ -11,7 +11,7 @@ module Gitlab # the window defined by `HOURS_AGO`. We perform a single API call, # so `COMMITS` needs to be a large enough value that we expect all # the commits in the time window will fit. - class Version < Scenario::Template + class Version < Framework::Scenario::Template HOURS_AGO = 24 COMMITS = 10_000 diff --git a/spec/gitlab/qa/framework/factory/base_spec.rb b/spec/gitlab/qa/framework/factory/base_spec.rb new file mode 100644 index 000000000..e1a37aca1 --- /dev/null +++ b/spec/gitlab/qa/framework/factory/base_spec.rb @@ -0,0 +1,127 @@ +describe Gitlab::QA::Framework::Factory::Base do + let(:factory) { spy('factory') } + let(:product) { spy('product') } + + describe '.fabricate!' do + subject { Class.new(described_class) } + + before do + allow(Gitlab::QA::Framework::Factory::Product).to receive(:new).and_return(product) + allow(Gitlab::QA::Framework::Factory::Product).to receive(:populate!).and_return(product) + end + + it 'instantiates the factory and calls factory method' do + expect(subject).to receive(:new).and_return(factory) + + subject.fabricate!('something') + + expect(factory).to have_received(:fabricate!).with('something') + end + + it 'returns fabrication product' do + allow(subject).to receive(:new).and_return(factory) + + result = subject.fabricate!('something') + + expect(result).to eq product + end + + it 'yields factory before calling factory method' do + allow(subject).to receive(:new).and_return(factory) + + subject.fabricate!(&:something!) + + expect(factory).to have_received(:something!).ordered + expect(factory).to have_received(:fabricate!).ordered + end + end + + describe '.dependency' do + let(:dependency) { spy('dependency') } + + before do + stub_const('Some::MyDependency', dependency) + end + + subject do + Class.new(described_class) do + dependency(Some::MyDependency, as: :mydep, &:something!) + end + end + + it 'appends a new dependency and accessors' do + expect(subject.dependencies).to be_one + end + + it 'defines dependency accessors' do + expect(subject.new).to respond_to :mydep, :mydep= + end + + describe 'dependencies fabrication' do + let(:dependency) { double('dependency') } + let(:instance) { spy('instance') } + + subject do + Class.new(described_class) do + dependency Some::MyDependency, as: :mydep + end + end + + before do + stub_const('Some::MyDependency', dependency) + + allow(subject).to receive(:new).and_return(instance) + allow(instance).to receive(:mydep).and_return(nil) + allow(Gitlab::QA::Framework::Factory::Product).to receive(:new) + allow(Gitlab::QA::Framework::Factory::Product).to receive(:populate!) + end + + it 'builds all dependencies first' do + expect(dependency).to receive(:fabricate!).once + + subject.fabricate! + end + end + end + + describe '.product' do + subject do + Class.new(described_class) do + def fabricate! + "any" + end + + # Defined only to be stubbed + def self.find_page; end + + product :token do + find_page.do_something_on_page! + 'resulting value' + end + end + end + + it 'appends new product attribute' do + expect(subject.attributes).to be_one + expect(subject.attributes).to have_key(:token) + end + + describe 'populating fabrication product with data' do + let(:page) { spy('page') } + + before do + allow(factory).to receive(:class).and_return(subject) + allow(Gitlab::QA::Framework::Factory::Product).to receive(:new).and_return(product) + allow(product).to receive(:page).and_return(page) + allow(subject).to receive(:find_page).and_return(page) + end + + it 'populates product after fabrication' do + subject.fabricate! + + expect(product.token).to eq 'resulting value' + expect(page).to have_received(:do_something_on_page!) + end + end + end +end diff --git a/spec/gitlab/qa/framework/factory/dependency_spec.rb b/spec/gitlab/qa/framework/factory/dependency_spec.rb new file mode 100644 index 000000000..b2e59c292 --- /dev/null +++ b/spec/gitlab/qa/framework/factory/dependency_spec.rb @@ -0,0 +1,72 @@ +describe Gitlab::QA::Framework::Factory::Dependency do + let(:dependency) { spy('dependency') } + let(:factory) { spy('factory') } + let(:block) { spy('block') } + + let(:signature) do + double('signature', factory: dependency, block: block) + end + + subject do + described_class.new(:mydep, factory, signature) + end + + describe '#overridden?' do + it 'returns true if factory has overridden dependency' do + allow(factory).to receive(:mydep).and_return('something') + + expect(subject).to be_overridden + end + + it 'returns false if dependency has not been overridden' do + allow(factory).to receive(:mydep).and_return(nil) + + expect(subject).not_to be_overridden + end + end + + describe '#build!' do + context 'when dependency has been overridden' do + before do + allow(subject).to receive(:overridden?).and_return(true) + end + + it 'does not fabricate dependency' do + subject.build! + + expect(dependency).not_to have_received(:fabricate!) + end + end + + context 'when dependency has not been overridden' do + before do + allow(subject).to receive(:overridden?).and_return(false) + end + + it 'fabricates dependency' do + subject.build! + + expect(dependency).to have_received(:fabricate!) + end + + it 'sets product in the factory' do + subject.build! + + expect(factory).to have_received(:mydep=).with(dependency) + end + + context 'when receives a caller factory as block argument' do + let(:dependency) { Gitlab::QA::Framework::Factory::Base } + + it 'calls given block with dependency factory and caller factory' do + allow_any_instance_of(Gitlab::QA::Framework::Factory::Base).to receive(:fabricate!).and_return(factory) + allow(Gitlab::QA::Framework::Factory::Product).to receive(:populate!).and_return(spy('any')) + + subject.build! + + expect(block).to have_received(:call).with(an_instance_of(Gitlab::QA::Framework::Factory::Base), factory) + end + end + end + end +end diff --git a/spec/gitlab/qa/framework/factory/product_spec.rb b/spec/gitlab/qa/framework/factory/product_spec.rb new file mode 100644 index 000000000..8c74fced8 --- /dev/null +++ b/spec/gitlab/qa/framework/factory/product_spec.rb @@ -0,0 +1,39 @@ +describe Gitlab::QA::Framework::Factory::Product do + let(:factory) do + Gitlab::QA::Framework::Factory::Base.new + end + + let(:attributes) do + { test: Gitlab::QA::Framework::Factory::Product::Attribute.new(:test, proc { 'returned' }) } + end + + let(:product) { spy('product') } + + before do + allow(Gitlab::QA::Framework::Factory::Base).to receive(:attributes).and_return(attributes) + end + + describe '.populate!' do + it 'returns a fabrication product and define factory attributes as its methods' do + expect(described_class).to receive(:new).and_return(product) + + result = described_class.populate!(factory) do |instance| + instance.something = 'string' + end + + expect(result).to be product + expect(result.test).to eq('returned') + end + end + + describe '.visit!' do + it 'makes it possible to visit fabrication product' do + allow_any_instance_of(described_class) + .to receive(:current_url).and_return('some url') + allow_any_instance_of(described_class) + .to receive(:visit).and_return('visited some url') + + expect(subject.visit!).to eq 'visited some url' + end + end +end diff --git a/spec/gitlab/qa/framework/page/base_spec.rb b/spec/gitlab/qa/framework/page/base_spec.rb new file mode 100644 index 000000000..56933cab8 --- /dev/null +++ b/spec/gitlab/qa/framework/page/base_spec.rb @@ -0,0 +1,62 @@ +describe Gitlab::QA::Framework::Page::Base do + describe 'page helpers' do + it 'exposes helpful page helpers' do + expect(subject).to respond_to :refresh, :wait, :scroll_to + end + end + + describe '.view', 'DSL for defining view partials' do + subject do + Class.new(described_class) do + view 'path/to/some/view.html.haml' do + element :something, 'string pattern' + element :something_else, /regexp pattern/ + end + + view 'path/to/some/_partial.html.haml' do + element :another_element, 'string pattern' + end + end + end + + it 'makes it possible to define page views' do + expect(subject.views.size).to eq 2 + expect(subject.views).to all(be_an_instance_of(Gitlab::QA::Framework::Page::View)) + end + + it 'populates views objects with data about elements' do + expect(subject.elements.size).to eq 3 + expect(subject.elements).to all(be_an_instance_of(Gitlab::QA::Framework::Page::Element)) + expect(subject.elements.map(&:name)) + .to eq [:something, :something_else, :another_element] + end + end + + describe '.errors' do + let(:view) { double('view') } + + context 'when page has views and elements defined' do + before do + allow(described_class).to receive(:views) + .and_return([view]) + + allow(view).to receive(:errors).and_return(['some error']) + end + + it 'iterates views composite and returns errors' do + expect(described_class.errors).to eq ['some error'] + end + end + + context 'when page has no views and elements defined' do + before do + allow(described_class).to receive(:views).and_return([]) + end + + it 'appends an error about missing views / elements block' do + expect(described_class.errors) + .to include 'Page class does not have views / elements defined!' + end + end + end +end diff --git a/spec/gitlab/qa/framework/page/element_spec.rb b/spec/gitlab/qa/framework/page/element_spec.rb new file mode 100644 index 000000000..c22e39bc4 --- /dev/null +++ b/spec/gitlab/qa/framework/page/element_spec.rb @@ -0,0 +1,51 @@ +describe Gitlab::QA::Framework::Page::Element do + describe '#selector' do + it 'transforms element name into QA-specific selector' do + expect(described_class.new(:sign_in_button).selector) + .to eq 'qa-sign-in-button' + end + end + + describe '#selector_css' do + it 'transforms element name into QA-specific clickable css selector' do + expect(described_class.new(:sign_in_button).selector_css) + .to eq '.qa-sign-in-button' + end + end + + context 'when pattern is an expression' do + subject { described_class.new(:something, /button 'Sign in'/) } + + it 'matches when there is a match' do + expect(subject.matches?("button 'Sign in'")).to be true + end + + it 'does not match if pattern is not present' do + expect(subject.matches?("button 'Sign out'")).to be false + end + end + + context 'when pattern is a string' do + subject { described_class.new(:something, 'button') } + + it 'matches when there is match' do + expect(subject.matches?('some button in the view')).to be true + end + + it 'does not match if pattern is not present' do + expect(subject.matches?('text_field :name')).to be false + end + end + + context 'when pattern is not provided' do + subject { described_class.new(:some_name) } + + it 'matches when QA specific selector is present' do + expect(subject.matches?('some qa-some-name selector')).to be true + end + + it 'does not match if QA selector is not there' do + expect(subject.matches?('some_name selector')).to be false + end + end +end diff --git a/spec/gitlab/qa/framework/page/validator_spec.rb b/spec/gitlab/qa/framework/page/validator_spec.rb new file mode 100644 index 000000000..1b30990a2 --- /dev/null +++ b/spec/gitlab/qa/framework/page/validator_spec.rb @@ -0,0 +1,85 @@ +describe Gitlab::QA::Framework::Page::Validator do + before do + stub_const('Gitlab::QA::Framework::Test', Module.new) + stub_const('Gitlab::QA::Framework::Test::APage', Class.new(Gitlab::QA::Framework::Page::Base) { view('lib/gitlab/qa.rb') { element :button, 'module Gitlab' } }) + stub_const('Gitlab::QA::Framework::Test::AModule', Module.new) + stub_const('Gitlab::QA::Framework::Test::AModule::APage', Class.new(Gitlab::QA::Framework::Page::Base) { view('lib/gitlab/qa.rb') { element :button, 'module Gitlab' } }) + end + describe '#constants' do + subject do + described_class.new(Gitlab::QA::Framework::Test) + end + + it 'returns all constants that are module children' do + expect(subject.constants) + .to include Gitlab::QA::Framework::Test::APage, Gitlab::QA::Framework::Test::AModule + end + end + + describe '#descendants' do + subject do + described_class.new(Gitlab::QA::Framework::Test) + end + + it 'recursively returns all descendants that are page objects' do + expect(subject.descendants) + .to include Gitlab::QA::Framework::Test::APage, Gitlab::QA::Framework::Test::AModule::APage + end + + it 'does not return modules that aggregate page objects' do + expect(subject.descendants) + .not_to include Gitlab::QA::Framework::Test::AModule + end + end + + context 'when checking validation errors' do + let(:view) { spy('view') } + + before do + allow(Gitlab::QA::Framework::Test::AModule::APage) + .to receive(:views).and_return([view]) + end + + subject do + described_class.new(Gitlab::QA::Framework::Test) + end + + context 'when there are no validation errors' do + before do + allow(view).to receive(:errors).and_return([]) + end + + describe '#errors' do + it 'does not return errors' do + expect(subject.errors).to be_empty + end + end + + describe '#validate!' do + it 'does not raise error' do + expect { subject.validate! }.not_to raise_error + end + end + end + + context 'when there are validation errors' do + before do + allow(view).to receive(:errors) + .and_return(['some error', 'another error']) + end + + describe '#errors' do + it 'returns errors' do + expect(subject.errors.count).to eq 2 + end + end + + describe '#validate!' do + it 'raises validation error' do + expect { subject.validate! } + .to raise_error described_class::ValidationError + end + end + end + end +end diff --git a/spec/gitlab/qa/framework/page/view_spec.rb b/spec/gitlab/qa/framework/page/view_spec.rb new file mode 100644 index 000000000..089fa763e --- /dev/null +++ b/spec/gitlab/qa/framework/page/view_spec.rb @@ -0,0 +1,70 @@ +describe Gitlab::QA::Framework::Page::View do + let(:element) do + double('element', name: :something, pattern: /some element/) + end + + subject { described_class.new('some/file.html', [element]) } + + describe '.evaluate' do + it 'evaluates a block and returns a DSL object' do + results = described_class.evaluate do + element :something, 'my pattern' + element :something_else, /another pattern/ + end + + expect(results.elements.size).to eq 2 + end + end + + describe '#pathname' do + it 'returns an absolute and clean path to the view' do + expect(subject.pathname.to_s).not_to include 'qa/page/' + expect(subject.pathname.to_s).to include 'some/file.html' + end + end + + describe '#errors' do + context 'when view partial is present' do + before do + allow(subject.pathname).to receive(:readable?) + .and_return(true) + end + + context 'when pattern is found' do + before do + allow(File).to receive(:foreach) + .and_yield('some element').once + allow(element).to receive(:matches?) + .with('some element').and_return(true) + end + + it 'walks through the view and asserts on elements existence' do + expect(subject.errors).to be_empty + end + end + + context 'when pattern has not been found' do + before do + allow(File).to receive(:foreach) + .and_yield('some element').once + allow(element).to receive(:matches?) + .with('some element').and_return(false) + end + + it 'returns an array of errors related to missing elements' do + expect(subject.errors).not_to be_empty + expect(subject.errors.first) + .to match %r{Missing element `.*` in `.*/some/file.html` view} + end + end + end + + context 'when view partial has not been found' do + it 'returns an error when it is not able to find the partial' do + expect(subject.errors).to be_one + expect(subject.errors.first) + .to match %r{Missing view partial `.*/some/file.html`!} + end + end + end +end diff --git a/spec/gitlab/qa/scenario/actable_spec.rb b/spec/gitlab/qa/framework/scenario/actable_spec.rb similarity index 88% rename from spec/gitlab/qa/scenario/actable_spec.rb rename to spec/gitlab/qa/framework/scenario/actable_spec.rb index caecfde26..fb8429e58 100644 --- a/spec/gitlab/qa/scenario/actable_spec.rb +++ b/spec/gitlab/qa/framework/scenario/actable_spec.rb @@ -1,7 +1,7 @@ -describe Gitlab::QA::Scenario::Actable do +describe Gitlab::QA::Framework::Scenario::Actable do subject do Class.new do - include Gitlab::QA::Scenario::Actable + include Gitlab::QA::Framework::Scenario::Actable attr_accessor :something -- GitLab From f53f5d38d78bb6aa7b74712decf230e0ffda9bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 25 May 2018 19:06:11 +0200 Subject: [PATCH 2/7] Move QA::Docker::Shellout to the framework as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/qa.rb | 1 - lib/gitlab/qa/docker/command.rb | 2 +- lib/gitlab/qa/docker/engine.rb | 2 +- lib/gitlab/qa/docker/shellout.rb | 40 --------------------- lib/gitlab/qa/framework.rb | 4 +++ lib/gitlab/qa/framework/docker/shellout.rb | 42 ++++++++++++++++++++++ spec/gitlab/qa/docker/command_spec.rb | 2 +- spec/gitlab/qa/docker/engine_spec.rb | 2 +- 8 files changed, 50 insertions(+), 45 deletions(-) delete mode 100644 lib/gitlab/qa/docker/shellout.rb create mode 100644 lib/gitlab/qa/framework/docker/shellout.rb diff --git a/lib/gitlab/qa.rb b/lib/gitlab/qa.rb index 1469d980a..405f02128 100644 --- a/lib/gitlab/qa.rb +++ b/lib/gitlab/qa.rb @@ -13,7 +13,6 @@ module Gitlab module Docker autoload :Command, 'qa/docker/command' autoload :Engine, 'qa/docker/engine' - autoload :Shellout, 'qa/docker/shellout' autoload :Volumes, 'qa/docker/volumes' end diff --git a/lib/gitlab/qa/docker/command.rb b/lib/gitlab/qa/docker/command.rb index a3f2a1059..b81ddb3eb 100644 --- a/lib/gitlab/qa/docker/command.rb +++ b/lib/gitlab/qa/docker/command.rb @@ -33,7 +33,7 @@ module Gitlab end def execute!(&block) - Docker::Shellout.new(self).execute!(&block) + Framework::Docker::Shellout.new(self).execute!(&block) end def self.execute(cmd, &block) diff --git a/lib/gitlab/qa/docker/engine.rb b/lib/gitlab/qa/docker/engine.rb index 763519551..cbc5a5b7e 100644 --- a/lib/gitlab/qa/docker/engine.rb +++ b/lib/gitlab/qa/docker/engine.rb @@ -46,7 +46,7 @@ module Gitlab def network_exists?(name) Docker::Command.execute("network inspect #{name}") - rescue Docker::Shellout::StatusError + rescue Framework::Docker::Shellout::StatusError false else true diff --git a/lib/gitlab/qa/docker/shellout.rb b/lib/gitlab/qa/docker/shellout.rb deleted file mode 100644 index fa623fe1b..000000000 --- a/lib/gitlab/qa/docker/shellout.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'open3' - -module Gitlab - module QA - module Docker - class Shellout - StatusError = Class.new(StandardError) - - def initialize(command) - @command = command - @output = [] - - puts "Docker shell command: `#{@command}`" - end - - def execute! - raise StatusError, 'Command already executed' if @output.any? - - Open3.popen2e(@command.to_s) do |_in, out, wait| - out.each do |line| - @output.push(line) - - if block_given? - yield line, wait - else - puts line - end - end - - if wait.value.exited? && wait.value.exitstatus.nonzero? - raise StatusError, "Docker command `#{@command}` failed!" - end - end - - @output.join.chomp - end - end - end - end -end diff --git a/lib/gitlab/qa/framework.rb b/lib/gitlab/qa/framework.rb index 161d6fdf3..b6c24685d 100644 --- a/lib/gitlab/qa/framework.rb +++ b/lib/gitlab/qa/framework.rb @@ -4,6 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) module Gitlab module QA module Framework + module Docker + autoload :Shellout, 'qa/framework/docker/shellout' + end + module Factory autoload :Base, 'qa/framework/factory/base' autoload :Dependency, 'qa/framework/factory/dependency' diff --git a/lib/gitlab/qa/framework/docker/shellout.rb b/lib/gitlab/qa/framework/docker/shellout.rb new file mode 100644 index 000000000..6005cc281 --- /dev/null +++ b/lib/gitlab/qa/framework/docker/shellout.rb @@ -0,0 +1,42 @@ +require 'open3' + +module Gitlab + module QA + module Framework + module Docker + class Shellout + StatusError = Class.new(StandardError) + + def initialize(command) + @command = command + @output = [] + + puts "Docker shell command: `#{@command}`" + end + + def execute! + raise StatusError, 'Command already executed' if @output.any? + + Open3.popen2e(@command.to_s) do |_in, out, wait| + out.each do |line| + @output.push(line) + + if block_given? + yield line, wait + else + puts line + end + end + + if wait.value.exited? && wait.value.exitstatus.nonzero? + raise StatusError, "Docker command `#{@command}` failed!" + end + end + + @output.join.chomp + end + end + end + end + end +end diff --git a/spec/gitlab/qa/docker/command_spec.rb b/spec/gitlab/qa/docker/command_spec.rb index 1698897ee..7cb287cf8 100644 --- a/spec/gitlab/qa/docker/command_spec.rb +++ b/spec/gitlab/qa/docker/command_spec.rb @@ -2,7 +2,7 @@ describe Gitlab::QA::Docker::Command do let(:docker) { spy('docker') } before do - stub_const('Gitlab::QA::Docker::Shellout', docker) + stub_const('Gitlab::QA::Framework::Docker::Shellout', docker) end describe '#<<' do diff --git a/spec/gitlab/qa/docker/engine_spec.rb b/spec/gitlab/qa/docker/engine_spec.rb index df2d40ccb..af925891f 100644 --- a/spec/gitlab/qa/docker/engine_spec.rb +++ b/spec/gitlab/qa/docker/engine_spec.rb @@ -2,7 +2,7 @@ describe Gitlab::QA::Docker::Engine do let(:docker) { spy('docker') } before do - stub_const('Gitlab::QA::Docker::Shellout', docker) + stub_const('Gitlab::QA::Framework::Docker::Shellout', docker) end describe '#pull' do -- GitLab From 1ab1bbb5758acc8cdaf29e3b4065bbdbe84d016e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 28 May 2018 18:30:22 +0200 Subject: [PATCH 3/7] Migrate Scenario::Bootable and Runtime::Scenario to the framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- bin/qa | 2 +- lib/gitlab/qa.rb | 2 + lib/gitlab/qa/component/specs.rb | 4 +- lib/gitlab/qa/docker/engine.rb | 2 + lib/gitlab/qa/framework.rb | 5 ++ lib/gitlab/qa/framework/runtime/scenario.rb | 43 +++++++++++++++ lib/gitlab/qa/framework/scenario/actable.rb | 16 +++--- lib/gitlab/qa/framework/scenario/bootable.rb | 52 +++++++++++++++++++ lib/gitlab/qa/framework/scenario/template.rb | 14 ++--- lib/gitlab/qa/scenario/test/instance/any.rb | 6 ++- lib/gitlab/qa/scenario/test/instance/image.rb | 6 ++- .../qa/scenario/test/instance/staging.rb | 6 ++- .../qa/scenario/test/integration/geo.rb | 6 ++- .../qa/scenario/test/integration/ldap.rb | 6 ++- .../scenario/test/integration/mattermost.rb | 6 ++- lib/gitlab/qa/scenario/test/omnibus/image.rb | 6 ++- lib/gitlab/qa/scenario/test/omnibus/update.rb | 6 ++- .../qa/scenario/test/omnibus/upgrade.rb | 6 ++- lib/gitlab/qa/scenario/test/sanity/version.rb | 6 ++- .../qa/framework/runtime/scenario_spec.rb | 34 ++++++++++++ .../qa/framework/scenario/actable_spec.rb | 28 ++++++++++ .../qa/framework/scenario/bootable_spec.rb | 27 ++++++++++ .../qa/framework/scenario/template_spec.rb | 22 ++++++++ spec/spec_helper.rb | 4 ++ 24 files changed, 278 insertions(+), 37 deletions(-) create mode 100644 lib/gitlab/qa/framework/runtime/scenario.rb create mode 100644 lib/gitlab/qa/framework/scenario/bootable.rb create mode 100644 spec/gitlab/qa/framework/runtime/scenario_spec.rb create mode 100644 spec/gitlab/qa/framework/scenario/bootable_spec.rb create mode 100644 spec/gitlab/qa/framework/scenario/template_spec.rb diff --git a/bin/qa b/bin/qa index 158290481..90297cd67 100755 --- a/bin/qa +++ b/bin/qa @@ -5,4 +5,4 @@ require 'gitlab/qa' Gitlab::QA::Scenario .const_get(ARGV.shift) - .perform(*ARGV) + .launch!(ARGV) diff --git a/lib/gitlab/qa.rb b/lib/gitlab/qa.rb index 405f02128..5d98adf12 100644 --- a/lib/gitlab/qa.rb +++ b/lib/gitlab/qa.rb @@ -18,10 +18,12 @@ module Gitlab module Runtime autoload :Env, 'qa/runtime/env' + autoload :Scenario, 'qa/runtime/scenario' end module Scenario autoload :Actable, 'qa/scenario/actable' + autoload :Bootable, 'qa/scenario/bootable' autoload :Template, 'qa/scenario/template' module Test diff --git a/lib/gitlab/qa/component/specs.rb b/lib/gitlab/qa/component/specs.rb index 6915fbb57..73c5cfc72 100644 --- a/lib/gitlab/qa/component/specs.rb +++ b/lib/gitlab/qa/component/specs.rb @@ -5,7 +5,9 @@ module Gitlab # This class represents GitLab QA specs image that is implemented in # the `qa/` directory located in GitLab CE / EE repositories. # - class Specs < Framework::Scenario::Template + class Specs + include Framework::Scenario::Actable + attr_accessor :suite, :release, :network, :args def initialize diff --git a/lib/gitlab/qa/docker/engine.rb b/lib/gitlab/qa/docker/engine.rb index cbc5a5b7e..364e9ddbc 100644 --- a/lib/gitlab/qa/docker/engine.rb +++ b/lib/gitlab/qa/docker/engine.rb @@ -2,6 +2,8 @@ module Gitlab module QA module Docker class Engine + include Gitlab::QA::Framework::Scenario::Bootable + DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost' def hostname diff --git a/lib/gitlab/qa/framework.rb b/lib/gitlab/qa/framework.rb index b6c24685d..5342774fd 100644 --- a/lib/gitlab/qa/framework.rb +++ b/lib/gitlab/qa/framework.rb @@ -21,8 +21,13 @@ module Gitlab autoload :View, 'qa/framework/page/view' end + module Runtime + autoload :Scenario, 'qa/framework/runtime/scenario' + end + module Scenario autoload :Actable, 'qa/framework/scenario/actable' + autoload :Bootable, 'qa/framework/scenario/bootable' autoload :Template, 'qa/framework/scenario/template' end end diff --git a/lib/gitlab/qa/framework/runtime/scenario.rb b/lib/gitlab/qa/framework/runtime/scenario.rb new file mode 100644 index 000000000..744035975 --- /dev/null +++ b/lib/gitlab/qa/framework/runtime/scenario.rb @@ -0,0 +1,43 @@ +module Gitlab + module QA + module Framework + module Runtime + ## + # Singleton approach to global test scenario arguments. + # + module Scenario + extend self + + def attributes + @attributes ||= {} + end + + def define(attribute, value, **opts) + attribute_sym = attribute.to_sym + attributes.store(attribute_sym, value) + + return if respond_to?(attribute) + + define_singleton_method(attribute) do + attributes[attribute_sym].tap do |val| + if opts[:type] != :flag && val.to_s.empty? + raise ArgumentError, "Empty `#{attribute}` attribute!" + end + end + end + end + + def clear_attributes + @attributes = {} + end + + def method_missing(name, *args) + return yield if block_given? + + raise ArgumentError, "Scenario attribute `#{name}` not defined!" + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/actable.rb b/lib/gitlab/qa/framework/scenario/actable.rb index b021fc008..88afb3971 100644 --- a/lib/gitlab/qa/framework/scenario/actable.rb +++ b/lib/gitlab/qa/framework/scenario/actable.rb @@ -3,17 +3,21 @@ module Gitlab module Framework module Scenario module Actable - def act(*args, &block) - instance_exec(*args, &block) - end - def self.included(base) base.extend(ClassMethods) end + def act(*args, &block) + instance_exec(*args, &block) + end + module ClassMethods - def perform - yield new if block_given? + def perform(*args) + new.tap do |actor| + block_result = yield actor if block_given? + actor.perform(*args) if actor.respond_to?(:perform) + return block_result + end end def act(*args, &block) diff --git a/lib/gitlab/qa/framework/scenario/bootable.rb b/lib/gitlab/qa/framework/scenario/bootable.rb new file mode 100644 index 000000000..f702eb462 --- /dev/null +++ b/lib/gitlab/qa/framework/scenario/bootable.rb @@ -0,0 +1,52 @@ +require 'optparse' + +module Gitlab + module QA + module Framework + module Scenario + module Bootable + Option = Struct.new(:name, :arg, :type, :default, :desc) + DEFAULT_NOT_PASSED = Object.new.freeze + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def launch!(argv = []) + return self.perform(*argv) unless has_attributes? + + arguments = OptionParser.new do |parser| + options.to_a.each do |opt| + if opt.default != DEFAULT_NOT_PASSED + Runtime::Scenario.define(opt.name, opt.default, type: opt.type) + end + + parser.on(opt.arg, opt.desc) do |value| + Runtime::Scenario.define(opt.name, value, type: opt.type) + end + end + end + + arguments.parse!(argv) + + self.perform(Runtime::Scenario.attributes, *arguments.default_argv) + end + + def attribute(name, arg, type: String, default: DEFAULT_NOT_PASSED, desc: '') + options.push(Option.new(name, arg, type, default, desc)) + end + + def options + @options ||= [] + end + + def has_attributes? + options.any? + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/template.rb b/lib/gitlab/qa/framework/scenario/template.rb index 44b567b18..6040e3c34 100644 --- a/lib/gitlab/qa/framework/scenario/template.rb +++ b/lib/gitlab/qa/framework/scenario/template.rb @@ -2,16 +2,12 @@ module Gitlab module QA module Framework module Scenario - class Template - def self.perform(*args) - new.tap do |scenario| - yield scenario if block_given? - return scenario.perform(*args) - end - end + module Template + def self.included(base) + base.include Gitlab::QA::Framework::Scenario::Actable + base.include Gitlab::QA::Framework::Scenario::Bootable - def perform(*_args) - raise NotImplementedError + base.attribute :skip_pull?, '--skip-pull', type: :flag, default: false end end end diff --git a/lib/gitlab/qa/scenario/test/instance/any.rb b/lib/gitlab/qa/scenario/test/instance/any.rb index 0576019c8..0dea6a18a 100644 --- a/lib/gitlab/qa/scenario/test/instance/any.rb +++ b/lib/gitlab/qa/scenario/test/instance/any.rb @@ -7,8 +7,10 @@ module Gitlab # Run test suite against any GitLab instance, # including staging and on-premises installation. # - class Any < Framework::Scenario::Template - def perform(edition, tag, address) + class Any + include Gitlab::QA::Framework::Scenario::Template + + def perform(options, edition, tag, address) release = Release.new(edition).tap do |r| r.tag = tag end diff --git a/lib/gitlab/qa/scenario/test/instance/image.rb b/lib/gitlab/qa/scenario/test/instance/image.rb index e8b8e56a3..062d761c2 100644 --- a/lib/gitlab/qa/scenario/test/instance/image.rb +++ b/lib/gitlab/qa/scenario/test/instance/image.rb @@ -3,14 +3,16 @@ module Gitlab module Scenario module Test module Instance - class Image < Framework::Scenario::Template + class Image + include Gitlab::QA::Framework::Scenario::Template + attr_writer :volumes def initialize @volumes = {} end - def perform(release) + def perform(options, release) Component::Gitlab.perform do |gitlab| gitlab.release = release gitlab.volumes = @volumes diff --git a/lib/gitlab/qa/scenario/test/instance/staging.rb b/lib/gitlab/qa/scenario/test/instance/staging.rb index c223cc261..6177e1cd1 100644 --- a/lib/gitlab/qa/scenario/test/instance/staging.rb +++ b/lib/gitlab/qa/scenario/test/instance/staging.rb @@ -6,8 +6,10 @@ module Gitlab ## # Run test suite against staging.gitlab.com # - class Staging < Framework::Scenario::Template - def perform(*) + class Staging + include Gitlab::QA::Framework::Scenario::Template + + def perform(options) Runtime::Env.require_no_license! release = Component::Staging.release diff --git a/lib/gitlab/qa/scenario/test/integration/geo.rb b/lib/gitlab/qa/scenario/test/integration/geo.rb index 13b267869..6af5202f2 100644 --- a/lib/gitlab/qa/scenario/test/integration/geo.rb +++ b/lib/gitlab/qa/scenario/test/integration/geo.rb @@ -3,13 +3,15 @@ module Gitlab module Scenario module Test module Integration - class Geo < Framework::Scenario::Template + class Geo + include Gitlab::QA::Framework::Scenario::Template + ## # rubocop:disable Lint/MissingCopEnableDirective # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/AbcSize # - def perform(release) + def perform(options, release) release = Release.new(release) raise ArgumentError, 'Geo is EE only!' unless release.ee? diff --git a/lib/gitlab/qa/scenario/test/integration/ldap.rb b/lib/gitlab/qa/scenario/test/integration/ldap.rb index 539d8cf6e..4be1ab7ac 100644 --- a/lib/gitlab/qa/scenario/test/integration/ldap.rb +++ b/lib/gitlab/qa/scenario/test/integration/ldap.rb @@ -5,9 +5,11 @@ module Gitlab module Scenario module Test module Integration - class LDAP < Framework::Scenario::Template + class LDAP + include Gitlab::QA::Framework::Scenario::Template + # rubocop:disable Metrics/AbcSize - def perform(release) + def perform(options, release) Component::Gitlab.perform do |gitlab| gitlab.release = release gitlab.name = 'gitlab-ldap' diff --git a/lib/gitlab/qa/scenario/test/integration/mattermost.rb b/lib/gitlab/qa/scenario/test/integration/mattermost.rb index 19bdb7fb8..15b765604 100644 --- a/lib/gitlab/qa/scenario/test/integration/mattermost.rb +++ b/lib/gitlab/qa/scenario/test/integration/mattermost.rb @@ -3,8 +3,10 @@ module Gitlab module Scenario module Test module Integration - class Mattermost < Framework::Scenario::Template - def perform(release) + class Mattermost + include Gitlab::QA::Framework::Scenario::Template + + def perform(options, release) Component::Gitlab.perform do |gitlab| gitlab.release = release gitlab.network = 'test' diff --git a/lib/gitlab/qa/scenario/test/omnibus/image.rb b/lib/gitlab/qa/scenario/test/omnibus/image.rb index 38f232d69..f6201ca6c 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/image.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/image.rb @@ -3,8 +3,10 @@ module Gitlab module Scenario module Test module Omnibus - class Image < Framework::Scenario::Template - def perform(release) + class Image + include Gitlab::QA::Framework::Scenario::Template + + def perform(options, release) Component::Gitlab.perform do |gitlab| gitlab.release = release gitlab.network = 'bridge' diff --git a/lib/gitlab/qa/scenario/test/omnibus/update.rb b/lib/gitlab/qa/scenario/test/omnibus/update.rb index 05378ea88..86b6f97bd 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/update.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/update.rb @@ -6,8 +6,10 @@ module Gitlab module Scenario module Test module Omnibus - class Update < Framework::Scenario::Template - def perform(next_release) + class Update + include Gitlab::QA::Framework::Scenario::Template + + def perform(options, next_release) next_release = Release.new(next_release) Docker::Volumes.new.with_temporary_volumes do |volumes| diff --git a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb index 3a3c1b7cb..9ce1a4c0a 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb @@ -6,8 +6,10 @@ module Gitlab module Scenario module Test module Omnibus - class Upgrade < Framework::Scenario::Template - def perform(image = 'CE') + class Upgrade + include Gitlab::QA::Framework::Scenario::Template + + def perform(options, image = 'CE') ce_release = Release.new(image) if ce_release.ee? diff --git a/lib/gitlab/qa/scenario/test/sanity/version.rb b/lib/gitlab/qa/scenario/test/sanity/version.rb index 95ddcedea..c1a15bc67 100644 --- a/lib/gitlab/qa/scenario/test/sanity/version.rb +++ b/lib/gitlab/qa/scenario/test/sanity/version.rb @@ -11,11 +11,13 @@ module Gitlab # the window defined by `HOURS_AGO`. We perform a single API call, # so `COMMITS` needs to be a large enough value that we expect all # the commits in the time window will fit. - class Version < Framework::Scenario::Template + class Version + include Gitlab::QA::Framework::Scenario::Template + HOURS_AGO = 24 COMMITS = 10_000 - def perform(release) + def perform(options, release) version = Component::Gitlab.perform do |gitlab| gitlab.release = release gitlab.act do diff --git a/spec/gitlab/qa/framework/runtime/scenario_spec.rb b/spec/gitlab/qa/framework/runtime/scenario_spec.rb new file mode 100644 index 000000000..d4382b8e4 --- /dev/null +++ b/spec/gitlab/qa/framework/runtime/scenario_spec.rb @@ -0,0 +1,34 @@ +describe Gitlab::QA::Framework::Runtime::Scenario do + subject do + Module.new.extend(described_class) + end + + it 'makes it possible to define global scenario attributes' do + subject.define(:my_attribute, 'some-value') + subject.define(:another_attribute, '42') + subject.define(:last_attribute, true, type: :flag) + + expect(subject.my_attribute).to eq 'some-value' + expect(subject.another_attribute).to eq '42' + expect(subject.last_attribute).to eq(true) + expect(subject.attributes) + .to eq(my_attribute: 'some-value', another_attribute: '42', last_attribute: true) + end + + it 'raises error when attribute is not known' do + expect { subject.invalid_accessor } + .to raise_error ArgumentError, /invalid_accessor/ + end + + it 'raises error when attribute is empty' do + subject.define(:empty_attribute, '') + + expect { subject.empty_attribute } + .to raise_error ArgumentError, /empty_attribute/ + end + + it 'fallbacks to given block return value when attribute is not known' do + expect(subject.invalid_accessor { 'hello world' }) + .to eq('hello world') + end +end diff --git a/spec/gitlab/qa/framework/scenario/actable_spec.rb b/spec/gitlab/qa/framework/scenario/actable_spec.rb index fb8429e58..ae00e6c0c 100644 --- a/spec/gitlab/qa/framework/scenario/actable_spec.rb +++ b/spec/gitlab/qa/framework/scenario/actable_spec.rb @@ -43,5 +43,33 @@ describe Gitlab::QA::Framework::Scenario::Actable do expect(result).to eq 'something' end + + context 'when class implements #perform' do + subject do + Class.new do + include Gitlab::QA::Framework::Scenario::Actable + + attr_reader :thing1, :thing2 + + def do_something(arg = nil) + @thing1 = "something" + end + + def perform(arg = nil) + @thing2 = "someone" + end + end + end + + it 'calls #perform' do + result = subject.perform do |object| + object.do_something + object + end + + expect(result.thing1).to eq 'something' + expect(result.thing2).to eq 'someone' + end + end end end diff --git a/spec/gitlab/qa/framework/scenario/bootable_spec.rb b/spec/gitlab/qa/framework/scenario/bootable_spec.rb new file mode 100644 index 000000000..c3578c092 --- /dev/null +++ b/spec/gitlab/qa/framework/scenario/bootable_spec.rb @@ -0,0 +1,27 @@ +describe Gitlab::QA::Framework::Scenario::Bootable do + subject do + Class.new + .include(Gitlab::QA::Framework::Scenario::Actable) + .include(described_class) + end + + it 'makes it possible to define the scenario attribute' do + subject.class_eval do + attribute :something, '--something SOMETHING', desc: 'Some attribute' + attribute :another, '--another ANOTHER', desc: 'Some other attribute' + attribute :bool, '--bool', type: :flag, desc: 'A boolean attribute' + attribute :attr_with_default, '--foo', default: 'hello world' + end + + expect(subject).to receive(:perform) + .with(something: 'test', another: '42', bool: true, attr_with_default: 'hello world') + + subject.launch!(%w[--another 42 --something test --bool]) + end + + it 'does not require attributes to be defined' do + expect(subject).to receive(:perform).with('some', 'argv') + + subject.launch!(%w[some argv]) + end +end diff --git a/spec/gitlab/qa/framework/scenario/template_spec.rb b/spec/gitlab/qa/framework/scenario/template_spec.rb new file mode 100644 index 000000000..015ec6f93 --- /dev/null +++ b/spec/gitlab/qa/framework/scenario/template_spec.rb @@ -0,0 +1,22 @@ +describe Gitlab::QA::Framework::Scenario::Template do + subject do + Class.new.include(described_class) + end + + it { expect(subject).to include(Gitlab::QA::Framework::Scenario::Actable) } + it { expect(subject).to include(Gitlab::QA::Framework::Scenario::Bootable) } + + describe '.skip_pull?' do + it 'defaults to false' do + subject.launch! + + expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(false) + end + + it 'can be set to true' do + subject.launch!(['--skip-pull']) + + expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(true) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 50e36e9f6..e178ce9f8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,4 +16,8 @@ RSpec.configure do |config| config.profile_examples = 10 config.order = :random Kernel.srand config.seed + + config.before do + Gitlab::QA::Framework::Runtime::Scenario.clear_attributes + end end -- GitLab From e51ca14177768387d1a44243908f579e40854820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 28 May 2018 18:34:18 +0200 Subject: [PATCH 4/7] Add a --skip-pull global option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/qa/docker/engine.rb | 2 ++ spec/gitlab/qa/docker/engine_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/gitlab/qa/docker/engine.rb b/lib/gitlab/qa/docker/engine.rb index 364e9ddbc..005ccaf18 100644 --- a/lib/gitlab/qa/docker/engine.rb +++ b/lib/gitlab/qa/docker/engine.rb @@ -11,6 +11,8 @@ module Gitlab end def pull(image, tag) + return if Gitlab::QA::Framework::Runtime::Scenario.skip_pull? { false } + Docker::Command.execute("pull #{image}:#{tag}") end diff --git a/spec/gitlab/qa/docker/engine_spec.rb b/spec/gitlab/qa/docker/engine_spec.rb index af925891f..67b377dda 100644 --- a/spec/gitlab/qa/docker/engine_spec.rb +++ b/spec/gitlab/qa/docker/engine_spec.rb @@ -12,6 +12,18 @@ describe Gitlab::QA::Docker::Engine do expect(docker).to have_received(:new) .with(eq('docker pull gitlab/gitlab-ce:nightly')) end + + context 'when Gitlab::QA::Framework::Runtime::Scenario.skip_pull? is true' do + before do + Gitlab::QA::Framework::Runtime::Scenario.define(:skip_pull?, true) + end + + it 'does not pull doicker image' do + subject.pull('gitlab/gitlab-ce', 'nightly') + + expect(docker).not_to have_received(:new) + end + end end describe '#run' do -- GitLab From 3030b4d189d21594cea480152a96a5b9616e1b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 29 May 2018 19:44:49 +0200 Subject: [PATCH 5/7] Move Docker classes to the framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/qa.rb | 15 +--- lib/gitlab/qa/component/gitlab.rb | 9 ++- lib/gitlab/qa/component/ldap.rb | 2 +- lib/gitlab/qa/component/specs.rb | 2 +- lib/gitlab/qa/docker/command.rb | 45 ------------ lib/gitlab/qa/docker/engine.rb | 69 ------------------- lib/gitlab/qa/docker/volumes.rb | 25 ------- lib/gitlab/qa/framework.rb | 3 + lib/gitlab/qa/framework/docker/command.rb | 47 +++++++++++++ lib/gitlab/qa/framework/docker/engine.rb | 67 ++++++++++++++++++ lib/gitlab/qa/framework/docker/volumes.rb | 24 +++++++ lib/gitlab/qa/framework/scenario/template.rb | 2 - lib/gitlab/qa/scenario/test/instance/any.rb | 2 +- lib/gitlab/qa/scenario/test/instance/image.rb | 2 +- .../qa/scenario/test/instance/staging.rb | 2 +- .../qa/scenario/test/integration/geo.rb | 2 +- .../qa/scenario/test/integration/ldap.rb | 2 +- .../scenario/test/integration/mattermost.rb | 2 +- lib/gitlab/qa/scenario/test/omnibus/image.rb | 2 +- lib/gitlab/qa/scenario/test/omnibus/update.rb | 5 +- .../qa/scenario/test/omnibus/upgrade.rb | 6 +- lib/gitlab/qa/scenario/test/sanity/version.rb | 1 + lib/gitlab/qa/scenario/test/template.rb | 19 +++++ spec/gitlab/qa/component/gitlab_spec.rb | 2 +- spec/gitlab/qa/component/specs_spec.rb | 2 +- .../qa/{ => framework}/docker/command_spec.rb | 2 +- .../qa/{ => framework}/docker/engine_spec.rb | 14 +--- .../qa/framework/scenario/template_spec.rb | 14 ---- spec/gitlab/qa/scenario/test/template_spec.rb | 19 +++++ 29 files changed, 210 insertions(+), 198 deletions(-) delete mode 100644 lib/gitlab/qa/docker/command.rb delete mode 100644 lib/gitlab/qa/docker/engine.rb delete mode 100644 lib/gitlab/qa/docker/volumes.rb create mode 100644 lib/gitlab/qa/framework/docker/command.rb create mode 100644 lib/gitlab/qa/framework/docker/engine.rb create mode 100644 lib/gitlab/qa/framework/docker/volumes.rb create mode 100644 lib/gitlab/qa/scenario/test/template.rb rename spec/gitlab/qa/{ => framework}/docker/command_spec.rb (95%) rename spec/gitlab/qa/{ => framework}/docker/engine_spec.rb (71%) create mode 100644 spec/gitlab/qa/scenario/test/template_spec.rb diff --git a/lib/gitlab/qa.rb b/lib/gitlab/qa.rb index 5d98adf12..a0319e685 100644 --- a/lib/gitlab/qa.rb +++ b/lib/gitlab/qa.rb @@ -1,5 +1,4 @@ -lib = File.expand_path(__dir__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +$LOAD_PATH.unshift(File.expand_path(__dir__)).uniq! module Gitlab module QA @@ -10,23 +9,15 @@ module Gitlab autoload :Staging, 'qa/component/staging' end - module Docker - autoload :Command, 'qa/docker/command' - autoload :Engine, 'qa/docker/engine' - autoload :Volumes, 'qa/docker/volumes' - end - module Runtime autoload :Env, 'qa/runtime/env' autoload :Scenario, 'qa/runtime/scenario' end module Scenario - autoload :Actable, 'qa/scenario/actable' - autoload :Bootable, 'qa/scenario/bootable' - autoload :Template, 'qa/scenario/template' - module Test + autoload :Template, 'qa/scenario/test/template' + module Instance autoload :Any, 'qa/scenario/test/instance/any' autoload :Image, 'qa/scenario/test/instance/image' diff --git a/lib/gitlab/qa/component/gitlab.rb b/lib/gitlab/qa/component/gitlab.rb index a9b26a564..82b322a55 100644 --- a/lib/gitlab/qa/component/gitlab.rb +++ b/lib/gitlab/qa/component/gitlab.rb @@ -14,10 +14,15 @@ module Gitlab attr_accessor :volumes, :network, :environment attr_writer :name + VOLUMES = { + 'config' => '/etc/gitlab', + 'data' => '/var/opt/gitlab' + }.freeze + def_delegators :release, :tag, :image, :edition def initialize - @docker = Docker::Engine.new + @docker = Framework::Docker::Engine.new @environment = {} @volumes = {} @network_aliases = [] @@ -148,7 +153,7 @@ module Gitlab class Availability def initialize(name) - @docker = Docker::Engine.new + @docker = Framework::Docker::Engine.new host = @docker.hostname port = @docker.port(name, 80).split(':').last diff --git a/lib/gitlab/qa/component/ldap.rb b/lib/gitlab/qa/component/ldap.rb index 461c0c005..da3eedd46 100644 --- a/lib/gitlab/qa/component/ldap.rb +++ b/lib/gitlab/qa/component/ldap.rb @@ -38,7 +38,7 @@ module Gitlab attr_writer :name def initialize - @docker = Docker::Engine.new + @docker = Framework::Docker::Engine.new @environment = {} @volumes = {} @network_aliases = [] diff --git a/lib/gitlab/qa/component/specs.rb b/lib/gitlab/qa/component/specs.rb index 73c5cfc72..1c97da115 100644 --- a/lib/gitlab/qa/component/specs.rb +++ b/lib/gitlab/qa/component/specs.rb @@ -11,7 +11,7 @@ module Gitlab attr_accessor :suite, :release, :network, :args def initialize - @docker = Docker::Engine.new + @docker = Framework::Docker::Engine.new end def perform # rubocop:disable Metrics/AbcSize diff --git a/lib/gitlab/qa/docker/command.rb b/lib/gitlab/qa/docker/command.rb deleted file mode 100644 index b81ddb3eb..000000000 --- a/lib/gitlab/qa/docker/command.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Gitlab - module QA - module Docker - class Command - attr_reader :args - - def initialize(cmd = nil) - @args = Array(cmd) - end - - def <<(*args) - tap { @args.concat(args) } - end - - def volume(from, to, opt = :z) - tap { @args.push("--volume #{from}:#{to}:#{opt}") } - end - - def name(identity) - tap { @args.push("--name #{identity}") } - end - - def env(name, value) - tap { @args.push(%(--env #{name}="#{value}")) } - end - - def to_s - "docker #{@args.join(' ')}" - end - - def ==(other) - to_s == other.to_s - end - - def execute!(&block) - Framework::Docker::Shellout.new(self).execute!(&block) - end - - def self.execute(cmd, &block) - new(cmd).execute!(&block) - end - end - end - end -end diff --git a/lib/gitlab/qa/docker/engine.rb b/lib/gitlab/qa/docker/engine.rb deleted file mode 100644 index 005ccaf18..000000000 --- a/lib/gitlab/qa/docker/engine.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Gitlab - module QA - module Docker - class Engine - include Gitlab::QA::Framework::Scenario::Bootable - - DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost' - - def hostname - URI(DOCKER_HOST).host - end - - def pull(image, tag) - return if Gitlab::QA::Framework::Runtime::Scenario.skip_pull? { false } - - Docker::Command.execute("pull #{image}:#{tag}") - end - - def run(image, tag, *args) - Docker::Command.new('run').tap do |command| - yield command if block_given? - - command << "#{image}:#{tag}" - command << args if args.any? - - command.execute! - end - end - - def read_file(image, tag, path, &block) - cat_file = "run --rm --entrypoint /bin/cat #{image}:#{tag} #{path}" - Docker::Command.execute(cat_file, &block) - end - - def attach(name, &block) - Docker::Command.execute("attach --sig-proxy=false #{name}", &block) - end - - def restart(name) - Docker::Command.execute("restart #{name}") - end - - def stop(name) - Docker::Command.execute("stop #{name}") - end - - def remove(name) - Docker::Command.execute("rm -f #{name}") - end - - def network_exists?(name) - Docker::Command.execute("network inspect #{name}") - rescue Framework::Docker::Shellout::StatusError - false - else - true - end - - def network_create(name) - Docker::Command.execute("network create #{name}") - end - - def port(name, port) - Docker::Command.execute("port #{name} #{port}/tcp") - end - end - end - end -end diff --git a/lib/gitlab/qa/docker/volumes.rb b/lib/gitlab/qa/docker/volumes.rb deleted file mode 100644 index be9e411df..000000000 --- a/lib/gitlab/qa/docker/volumes.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'tmpdir' - -module Gitlab - module QA - module Docker - class Volumes - VOLUMES = { 'config' => '/etc/gitlab', - 'data' => '/var/opt/gitlab' }.freeze - - def initialize(volumes = VOLUMES) - @volumes = volumes - end - - def with_temporary_volumes - # macOS's tmpdir is a symlink /var/folders -> /private/var/folders - # but Docker on macOS exposes /private and disallow exposing /var/ - # so we need to get the real tmpdir path - Dir.mktmpdir('gitlab-qa-', File.realpath(Dir.tmpdir)).tap do |dir| - yield Hash[@volumes.map { |k, v| ["#{dir}/#{k}", v] }] - end - end - end - end - end -end diff --git a/lib/gitlab/qa/framework.rb b/lib/gitlab/qa/framework.rb index 5342774fd..eab095f2b 100644 --- a/lib/gitlab/qa/framework.rb +++ b/lib/gitlab/qa/framework.rb @@ -5,7 +5,10 @@ module Gitlab module QA module Framework module Docker + autoload :Command, 'qa/framework/docker/command' + autoload :Engine, 'qa/framework/docker/engine' autoload :Shellout, 'qa/framework/docker/shellout' + autoload :Volumes, 'qa/framework/docker/volumes' end module Factory diff --git a/lib/gitlab/qa/framework/docker/command.rb b/lib/gitlab/qa/framework/docker/command.rb new file mode 100644 index 000000000..0db876a0e --- /dev/null +++ b/lib/gitlab/qa/framework/docker/command.rb @@ -0,0 +1,47 @@ +module Gitlab + module QA + module Framework + module Docker + class Command + attr_reader :args + + def initialize(cmd = nil) + @args = Array(cmd) + end + + def <<(*args) + tap { @args.concat(args) } + end + + def volume(from, to, opt = :z) + tap { @args.push("--volume #{from}:#{to}:#{opt}") } + end + + def name(identity) + tap { @args.push("--name #{identity}") } + end + + def env(name, value) + tap { @args.push(%(--env #{name}="#{value}")) } + end + + def to_s + "docker #{@args.join(' ')}" + end + + def ==(other) + to_s == other.to_s + end + + def execute!(&block) + Shellout.new(self).execute!(&block) + end + + def self.execute(cmd, &block) + new(cmd).execute!(&block) + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/docker/engine.rb b/lib/gitlab/qa/framework/docker/engine.rb new file mode 100644 index 000000000..22d3e1b0b --- /dev/null +++ b/lib/gitlab/qa/framework/docker/engine.rb @@ -0,0 +1,67 @@ +module Gitlab + module QA + module Framework + module Docker + class Engine + DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost' + + def hostname + URI(DOCKER_HOST).host + end + + def pull(image, tag) + Command.execute("pull #{image}:#{tag}") + end + + def run(image, tag, *args) + Command.new('run').tap do |command| + yield command if block_given? + + command << "#{image}:#{tag}" + command << args if args.any? + + command.execute! + end + end + + def read_file(image, tag, path, &block) + cat_file = "run --rm --entrypoint /bin/cat #{image}:#{tag} #{path}" + Command.execute(cat_file, &block) + end + + def attach(name, &block) + Command.execute("attach --sig-proxy=false #{name}", &block) + end + + def restart(name) + Command.execute("restart #{name}") + end + + def stop(name) + Command.execute("stop #{name}") + end + + def remove(name) + Command.execute("rm -f #{name}") + end + + def network_exists?(name) + Command.execute("network inspect #{name}") + rescue Shellout::StatusError + false + else + true + end + + def network_create(name) + Command.execute("network create #{name}") + end + + def port(name, port) + Command.execute("port #{name} #{port}/tcp") + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/docker/volumes.rb b/lib/gitlab/qa/framework/docker/volumes.rb new file mode 100644 index 000000000..491e77420 --- /dev/null +++ b/lib/gitlab/qa/framework/docker/volumes.rb @@ -0,0 +1,24 @@ +require 'tmpdir' + +module Gitlab + module QA + module Framework + module Docker + class Volumes + def initialize(volumes) + @volumes = volumes + end + + def with_temporary_volumes + # macOS's tmpdir is a symlink /var/folders -> /private/var/folders + # but Docker on macOS exposes /private and disallow exposing /var/ + # so we need to get the real tmpdir path + Dir.mktmpdir('qa-framework-', File.realpath(Dir.tmpdir)).tap do |dir| + yield Hash[@volumes.map { |k, v| ["#{dir}/#{k}", v] }] + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/template.rb b/lib/gitlab/qa/framework/scenario/template.rb index 6040e3c34..6c3a70aec 100644 --- a/lib/gitlab/qa/framework/scenario/template.rb +++ b/lib/gitlab/qa/framework/scenario/template.rb @@ -6,8 +6,6 @@ module Gitlab def self.included(base) base.include Gitlab::QA::Framework::Scenario::Actable base.include Gitlab::QA::Framework::Scenario::Bootable - - base.attribute :skip_pull?, '--skip-pull', type: :flag, default: false end end end diff --git a/lib/gitlab/qa/scenario/test/instance/any.rb b/lib/gitlab/qa/scenario/test/instance/any.rb index 0dea6a18a..25d420cc8 100644 --- a/lib/gitlab/qa/scenario/test/instance/any.rb +++ b/lib/gitlab/qa/scenario/test/instance/any.rb @@ -8,7 +8,7 @@ module Gitlab # including staging and on-premises installation. # class Any - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options, edition, tag, address) release = Release.new(edition).tap do |r| diff --git a/lib/gitlab/qa/scenario/test/instance/image.rb b/lib/gitlab/qa/scenario/test/instance/image.rb index 062d761c2..21638b78c 100644 --- a/lib/gitlab/qa/scenario/test/instance/image.rb +++ b/lib/gitlab/qa/scenario/test/instance/image.rb @@ -4,7 +4,7 @@ module Gitlab module Test module Instance class Image - include Gitlab::QA::Framework::Scenario::Template + include Template attr_writer :volumes diff --git a/lib/gitlab/qa/scenario/test/instance/staging.rb b/lib/gitlab/qa/scenario/test/instance/staging.rb index 6177e1cd1..0cd1c1dc0 100644 --- a/lib/gitlab/qa/scenario/test/instance/staging.rb +++ b/lib/gitlab/qa/scenario/test/instance/staging.rb @@ -7,7 +7,7 @@ module Gitlab # Run test suite against staging.gitlab.com # class Staging - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options) Runtime::Env.require_no_license! diff --git a/lib/gitlab/qa/scenario/test/integration/geo.rb b/lib/gitlab/qa/scenario/test/integration/geo.rb index 6af5202f2..204d66bef 100644 --- a/lib/gitlab/qa/scenario/test/integration/geo.rb +++ b/lib/gitlab/qa/scenario/test/integration/geo.rb @@ -4,7 +4,7 @@ module Gitlab module Test module Integration class Geo - include Gitlab::QA::Framework::Scenario::Template + include Template ## # rubocop:disable Lint/MissingCopEnableDirective diff --git a/lib/gitlab/qa/scenario/test/integration/ldap.rb b/lib/gitlab/qa/scenario/test/integration/ldap.rb index 4be1ab7ac..db630e8ab 100644 --- a/lib/gitlab/qa/scenario/test/integration/ldap.rb +++ b/lib/gitlab/qa/scenario/test/integration/ldap.rb @@ -6,7 +6,7 @@ module Gitlab module Test module Integration class LDAP - include Gitlab::QA::Framework::Scenario::Template + include Template # rubocop:disable Metrics/AbcSize def perform(options, release) diff --git a/lib/gitlab/qa/scenario/test/integration/mattermost.rb b/lib/gitlab/qa/scenario/test/integration/mattermost.rb index 15b765604..df4ad196e 100644 --- a/lib/gitlab/qa/scenario/test/integration/mattermost.rb +++ b/lib/gitlab/qa/scenario/test/integration/mattermost.rb @@ -4,7 +4,7 @@ module Gitlab module Test module Integration class Mattermost - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options, release) Component::Gitlab.perform do |gitlab| diff --git a/lib/gitlab/qa/scenario/test/omnibus/image.rb b/lib/gitlab/qa/scenario/test/omnibus/image.rb index f6201ca6c..b1c41b61e 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/image.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/image.rb @@ -4,7 +4,7 @@ module Gitlab module Test module Omnibus class Image - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options, release) Component::Gitlab.perform do |gitlab| diff --git a/lib/gitlab/qa/scenario/test/omnibus/update.rb b/lib/gitlab/qa/scenario/test/omnibus/update.rb index 86b6f97bd..6dd81862b 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/update.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/update.rb @@ -7,12 +7,13 @@ module Gitlab module Test module Omnibus class Update - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options, next_release) next_release = Release.new(next_release) + volumes = Framework::Docker::Volumes.new(Component::Gitlab::VOLUMES) - Docker::Volumes.new.with_temporary_volumes do |volumes| + volumes.with_temporary_volumes do |volumes| Scenario::Test::Instance::Image .perform(next_release.previous_stable) do |scenario| scenario.volumes = volumes diff --git a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb index 9ce1a4c0a..d6b2fb1c8 100644 --- a/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb +++ b/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb @@ -7,7 +7,7 @@ module Gitlab module Test module Omnibus class Upgrade - include Gitlab::QA::Framework::Scenario::Template + include Template def perform(options, image = 'CE') ce_release = Release.new(image) @@ -16,7 +16,9 @@ module Gitlab raise ArgumentError, 'Only CE can be upgraded to EE!' end - Docker::Volumes.new.with_temporary_volumes do |volumes| + volumes = Framework::Docker::Volumes.new(Component::Gitlab::VOLUMES) + + volumes.with_temporary_volumes do |volumes| Scenario::Test::Instance::Image .perform(ce_release) do |scenario| scenario.volumes = volumes diff --git a/lib/gitlab/qa/scenario/test/sanity/version.rb b/lib/gitlab/qa/scenario/test/sanity/version.rb index c1a15bc67..32f17ef33 100644 --- a/lib/gitlab/qa/scenario/test/sanity/version.rb +++ b/lib/gitlab/qa/scenario/test/sanity/version.rb @@ -13,6 +13,7 @@ module Gitlab # the commits in the time window will fit. class Version include Gitlab::QA::Framework::Scenario::Template + include Scenario::CommonOptions HOURS_AGO = 24 COMMITS = 10_000 diff --git a/lib/gitlab/qa/scenario/test/template.rb b/lib/gitlab/qa/scenario/test/template.rb new file mode 100644 index 000000000..3425cd213 --- /dev/null +++ b/lib/gitlab/qa/scenario/test/template.rb @@ -0,0 +1,19 @@ +require 'json' +require 'net/http' +require 'cgi' + +module Gitlab + module QA + module Scenario + module Test + module Template + def self.included(base) + base.include Gitlab::QA::Framework::Scenario::Actable + base.include Gitlab::QA::Framework::Scenario::Bootable + base.attribute :skip_pull?, '--skip-pull', type: :flag, default: false + end + end + end + end + end +end diff --git a/spec/gitlab/qa/component/gitlab_spec.rb b/spec/gitlab/qa/component/gitlab_spec.rb index 09f6ea94e..3db4fcebd 100644 --- a/spec/gitlab/qa/component/gitlab_spec.rb +++ b/spec/gitlab/qa/component/gitlab_spec.rb @@ -88,7 +88,7 @@ describe Gitlab::QA::Component::Gitlab do let(:docker) { spy('docker command') } before do - stub_const('Gitlab::QA::Docker::Command', docker) + stub_const('Gitlab::QA::Framework::Docker::Command', docker) allow(subject).to receive(:ensure_configured!) end diff --git a/spec/gitlab/qa/component/specs_spec.rb b/spec/gitlab/qa/component/specs_spec.rb index 9e80b8105..66a9c100f 100644 --- a/spec/gitlab/qa/component/specs_spec.rb +++ b/spec/gitlab/qa/component/specs_spec.rb @@ -2,7 +2,7 @@ describe Gitlab::QA::Component::Specs do let(:docker) { spy('docker command') } before do - stub_const('Gitlab::QA::Docker::Command', docker) + stub_const('Gitlab::QA::Framework::Docker::Command', docker) end describe '#perform' do diff --git a/spec/gitlab/qa/docker/command_spec.rb b/spec/gitlab/qa/framework/docker/command_spec.rb similarity index 95% rename from spec/gitlab/qa/docker/command_spec.rb rename to spec/gitlab/qa/framework/docker/command_spec.rb index 7cb287cf8..63a7723c2 100644 --- a/spec/gitlab/qa/docker/command_spec.rb +++ b/spec/gitlab/qa/framework/docker/command_spec.rb @@ -1,4 +1,4 @@ -describe Gitlab::QA::Docker::Command do +describe Gitlab::QA::Framework::Docker::Command do let(:docker) { spy('docker') } before do diff --git a/spec/gitlab/qa/docker/engine_spec.rb b/spec/gitlab/qa/framework/docker/engine_spec.rb similarity index 71% rename from spec/gitlab/qa/docker/engine_spec.rb rename to spec/gitlab/qa/framework/docker/engine_spec.rb index 67b377dda..e8f107a1f 100644 --- a/spec/gitlab/qa/docker/engine_spec.rb +++ b/spec/gitlab/qa/framework/docker/engine_spec.rb @@ -1,4 +1,4 @@ -describe Gitlab::QA::Docker::Engine do +describe Gitlab::QA::Framework::Docker::Engine do let(:docker) { spy('docker') } before do @@ -12,18 +12,6 @@ describe Gitlab::QA::Docker::Engine do expect(docker).to have_received(:new) .with(eq('docker pull gitlab/gitlab-ce:nightly')) end - - context 'when Gitlab::QA::Framework::Runtime::Scenario.skip_pull? is true' do - before do - Gitlab::QA::Framework::Runtime::Scenario.define(:skip_pull?, true) - end - - it 'does not pull doicker image' do - subject.pull('gitlab/gitlab-ce', 'nightly') - - expect(docker).not_to have_received(:new) - end - end end describe '#run' do diff --git a/spec/gitlab/qa/framework/scenario/template_spec.rb b/spec/gitlab/qa/framework/scenario/template_spec.rb index 015ec6f93..58fddadc0 100644 --- a/spec/gitlab/qa/framework/scenario/template_spec.rb +++ b/spec/gitlab/qa/framework/scenario/template_spec.rb @@ -5,18 +5,4 @@ describe Gitlab::QA::Framework::Scenario::Template do it { expect(subject).to include(Gitlab::QA::Framework::Scenario::Actable) } it { expect(subject).to include(Gitlab::QA::Framework::Scenario::Bootable) } - - describe '.skip_pull?' do - it 'defaults to false' do - subject.launch! - - expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(false) - end - - it 'can be set to true' do - subject.launch!(['--skip-pull']) - - expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(true) - end - end end diff --git a/spec/gitlab/qa/scenario/test/template_spec.rb b/spec/gitlab/qa/scenario/test/template_spec.rb new file mode 100644 index 000000000..60e5512d8 --- /dev/null +++ b/spec/gitlab/qa/scenario/test/template_spec.rb @@ -0,0 +1,19 @@ +describe Gitlab::QA::Scenario::Test::Template do + subject do + Class.new.include(described_class) + end + + describe '.skip_pull?' do + it 'defaults to false' do + subject.launch! + + expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(false) + end + + it 'can be set to true' do + subject.launch!(['--skip-pull']) + + expect(Gitlab::QA::Framework::Runtime::Scenario.skip_pull?).to be(true) + end + end +end -- GitLab From 7b353511e035c7216e11d5fde7ce727b56eb99a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 30 May 2018 13:25:11 +0200 Subject: [PATCH 6/7] Move more Runtime classes, Scenario::Taggable and Scenario::Runner to the framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- gitlab-qa.gemspec | 5 +- lib/gitlab/qa/framework.rb | 14 ++- lib/gitlab/qa/framework/docker/command.rb | 2 +- lib/gitlab/qa/framework/runtime/address.rb | 24 ++++ lib/gitlab/qa/framework/runtime/browser.rb | 110 ++++++++++++++++++ lib/gitlab/qa/framework/runtime/env.rb | 20 ++++ lib/gitlab/qa/framework/runtime/scenario.rb | 6 +- lib/gitlab/qa/framework/runtime/session.rb | 52 +++++++++ lib/gitlab/qa/framework/scenario/bootable.rb | 33 +++--- lib/gitlab/qa/framework/scenario/runner.rb | 34 ++++++ lib/gitlab/qa/framework/scenario/taggable.rb | 23 ++++ .../framework/{docker => utils}/shellout.rb | 6 +- .../qa/framework/docker/command_spec.rb | 2 +- .../gitlab/qa/framework/docker/engine_spec.rb | 2 +- spec/gitlab/qa/framework/runtime/env_spec.rb | 58 +++++++++ .../qa/framework/runtime/scenario_spec.rb | 5 - spec/spec_helper.rb | 2 + spec/support/stub_env.rb | 38 ++++++ 18 files changed, 403 insertions(+), 33 deletions(-) create mode 100644 lib/gitlab/qa/framework/runtime/address.rb create mode 100644 lib/gitlab/qa/framework/runtime/browser.rb create mode 100644 lib/gitlab/qa/framework/runtime/env.rb create mode 100644 lib/gitlab/qa/framework/runtime/session.rb create mode 100644 lib/gitlab/qa/framework/scenario/runner.rb create mode 100644 lib/gitlab/qa/framework/scenario/taggable.rb rename lib/gitlab/qa/framework/{docker => utils}/shellout.rb (84%) create mode 100644 spec/gitlab/qa/framework/runtime/env_spec.rb create mode 100644 spec/support/stub_env.rb diff --git a/gitlab-qa.gemspec b/gitlab-qa.gemspec index e2977b7e0..ab2725624 100644 --- a/gitlab-qa.gemspec +++ b/gitlab-qa.gemspec @@ -1,5 +1,4 @@ -lib = File.expand_path('../lib', __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__)).uniq! require 'gitlab/qa/version' Gem::Specification.new do |spec| @@ -19,6 +18,8 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_runtime_dependency 'capybara', '~> 2.16' + spec.add_runtime_dependency 'capybara-screenshot', '~> 1.0.18' + spec.add_runtime_dependency 'selenium-webdriver', '~> 3.8.0' # Some dependencies are pinned, to prevent new cops from breaking the CI pipelines spec.add_development_dependency 'gitlab-styles', '2.2.0' diff --git a/lib/gitlab/qa/framework.rb b/lib/gitlab/qa/framework.rb index eab095f2b..56bfdf0a4 100644 --- a/lib/gitlab/qa/framework.rb +++ b/lib/gitlab/qa/framework.rb @@ -1,5 +1,4 @@ -lib = File.expand_path('../', __dir__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +$LOAD_PATH.unshift(File.expand_path('../', __dir__)).uniq! module Gitlab module QA @@ -7,7 +6,6 @@ module Gitlab module Docker autoload :Command, 'qa/framework/docker/command' autoload :Engine, 'qa/framework/docker/engine' - autoload :Shellout, 'qa/framework/docker/shellout' autoload :Volumes, 'qa/framework/docker/volumes' end @@ -25,14 +23,24 @@ module Gitlab end module Runtime + autoload :Address, 'qa/framework/runtime/address' + autoload :Browser, 'qa/framework/runtime/browser' + autoload :Env, 'qa/framework/runtime/env' autoload :Scenario, 'qa/framework/runtime/scenario' + autoload :Session, 'qa/framework/runtime/session' end module Scenario autoload :Actable, 'qa/framework/scenario/actable' autoload :Bootable, 'qa/framework/scenario/bootable' + autoload :Runner, 'qa/framework/scenario/runner' + autoload :Taggable, 'qa/framework/scenario/taggable' autoload :Template, 'qa/framework/scenario/template' end + + module Utils + autoload :Shellout, 'qa/framework/utils/shellout' + end end end end diff --git a/lib/gitlab/qa/framework/docker/command.rb b/lib/gitlab/qa/framework/docker/command.rb index 0db876a0e..471fb02c5 100644 --- a/lib/gitlab/qa/framework/docker/command.rb +++ b/lib/gitlab/qa/framework/docker/command.rb @@ -34,7 +34,7 @@ module Gitlab end def execute!(&block) - Shellout.new(self).execute!(&block) + Framework::Utils::Shellout.new(self).execute!(&block) end def self.execute(cmd, &block) diff --git a/lib/gitlab/qa/framework/runtime/address.rb b/lib/gitlab/qa/framework/runtime/address.rb new file mode 100644 index 000000000..462d9e64f --- /dev/null +++ b/lib/gitlab/qa/framework/runtime/address.rb @@ -0,0 +1,24 @@ +module Gitlab + module QA + module Framework + module Runtime + class Address + attr_reader :address + + def initialize(instance, page = nil) + @instance = instance + @address = host + (page.is_a?(String) ? page : page&.path) + end + + def host + if @instance.is_a?(Symbol) + Scenario.send("#{@instance}_address") + else + @instance.to_s + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/runtime/browser.rb b/lib/gitlab/qa/framework/runtime/browser.rb new file mode 100644 index 000000000..e0cc5f706 --- /dev/null +++ b/lib/gitlab/qa/framework/runtime/browser.rb @@ -0,0 +1,110 @@ +require 'rspec/core' +require 'capybara/rspec' +require 'capybara-screenshot/rspec' +require 'selenium-webdriver' + +module Gitlab + module QA + module Framework + module Runtime + class Browser + include Gitlab::QA::Framework::Scenario::Actable + + def initialize + self.class.configure! + end + + ## + # Visit a page that belongs to a GitLab instance under given address. + # + # Example: + # + # visit(:gitlab, Page::Main::Login) + # visit('http://gitlab.example/users/sign_in') + # + # In case of an address that is a symbol we will try to guess address + # based on `Runtime::Scenario#something_address`. + # + def visit(address, page = nil, &block) + Session.new(address, page).perform(&block) + end + + def self.visit(address, page = nil, &block) + new.visit(address, page, &block) + end + + def self.configure! + return if Capybara.drivers.include?(:chrome) + + Capybara.register_driver :chrome do |app| + options = Selenium::WebDriver::Chrome::Options.new + configure_defaults!(options) + configure_headless!(options) + configure_ci!(options) + + Capybara::Selenium::Driver.new( + app, + browser: :chrome, + desired_capabilities: capabilities, + options: options + ) + end + + # Keep only the screenshots generated from the last failing test suite + Capybara::Screenshot.prune_strategy = :keep_last_run + + # From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326 + Capybara::Screenshot.register_driver(:chrome) do |driver, path| + driver.browser.save_screenshot(path) + end + + Capybara.configure do |config| + config.default_driver = :chrome + config.javascript_driver = :chrome + config.default_max_wait_time = 10 + # https://github.com/mattheworiordan/capybara-screenshot/issues/164 + config.save_path = File.expand_path('../../tmp', __dir__) + end + end + + def self.capabilities + Selenium::WebDriver::Remote::Capabilities.chrome( + # This enables access to logs with `page.driver.manage.get_log(:browser)` + loggingPrefs: { + browser: "ALL", + client: "ALL", + driver: "ALL", + server: "ALL" + } + ) + end + + def self.configure_defaults!(options) + options.add_argument("window-size=1240,1680") + + # Chrome won't work properly in a Docker container in sandbox mode + options.add_argument("no-sandbox") + end + + def self.configure_headless!(options) + return unless Env.chrome_headless? + + # Run headless by default unless CHROME_HEADLESS is false + options.add_argument("headless") + + # Chrome documentation says this flag is needed for now + # https://developers.google.com/web/updates/2017/04/headless-chrome#cli + options.add_argument("disable-gpu") + end + + def self.configure_ci!(options) + return unless Env.running_in_ci? + + # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252 + options.add_argument("disable-dev-shm-usage") + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/runtime/env.rb b/lib/gitlab/qa/framework/runtime/env.rb new file mode 100644 index 000000000..a15a9206a --- /dev/null +++ b/lib/gitlab/qa/framework/runtime/env.rb @@ -0,0 +1,20 @@ +module Gitlab + module QA + module Framework + module Runtime + module Env + extend self + + # set to 'false' to have Chrome run visibly instead of headless + def chrome_headless? + (ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0 + end + + def running_in_ci? + ENV['CI'] || ENV['CI_SERVER'] + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/runtime/scenario.rb b/lib/gitlab/qa/framework/runtime/scenario.rb index 744035975..ac1b2add6 100644 --- a/lib/gitlab/qa/framework/runtime/scenario.rb +++ b/lib/gitlab/qa/framework/runtime/scenario.rb @@ -31,9 +31,11 @@ module Gitlab @attributes = {} end - def method_missing(name, *args) - return yield if block_given? + def respond_to_missing?(name, *) + super + end + def method_missing(name, *) # rubocop:disable Style/MethodMissing raise ArgumentError, "Scenario attribute `#{name}` not defined!" end end diff --git a/lib/gitlab/qa/framework/runtime/session.rb b/lib/gitlab/qa/framework/runtime/session.rb new file mode 100644 index 000000000..6f1b61c05 --- /dev/null +++ b/lib/gitlab/qa/framework/runtime/session.rb @@ -0,0 +1,52 @@ +require 'rspec/core' +require 'capybara/rspec' +require 'capybara-screenshot/rspec' +require 'selenium-webdriver' + +module Gitlab + module QA + module Framework + module Runtime + class Session + include Capybara::DSL + + def initialize(instance, page = nil) + @session_address = Address.new(instance, page) + end + + def url + @session_address.address + end + + def perform(&block) + visit(url) + + yield if block_given? + rescue StandardError + raise if block.nil? + + # RSpec examples will take care of screenshots on their own + # + unless block.binding.receiver.is_a?(RSpec::Core::ExampleGroup) + screenshot_and_save_page + end + + raise + ensure + clear! if block_given? + end + + ## + # Selenium allows to reset session cookies for current domain only. + # + # See gitlab-org/gitlab-qa#102 + # + def clear! + visit(url) + reset_session! + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/bootable.rb b/lib/gitlab/qa/framework/scenario/bootable.rb index f702eb462..a7d16a095 100644 --- a/lib/gitlab/qa/framework/scenario/bootable.rb +++ b/lib/gitlab/qa/framework/scenario/bootable.rb @@ -14,23 +14,11 @@ module Gitlab module ClassMethods def launch!(argv = []) - return self.perform(*argv) unless has_attributes? + return perform(*argv) unless has_attributes? - arguments = OptionParser.new do |parser| - options.to_a.each do |opt| - if opt.default != DEFAULT_NOT_PASSED - Runtime::Scenario.define(opt.name, opt.default, type: opt.type) - end - - parser.on(opt.arg, opt.desc) do |value| - Runtime::Scenario.define(opt.name, value, type: opt.type) - end - end - end - - arguments.parse!(argv) + options_parser.parse!(argv) - self.perform(Runtime::Scenario.attributes, *arguments.default_argv) + perform(Runtime::Scenario.attributes, *options_parser.default_argv) end def attribute(name, arg, type: String, default: DEFAULT_NOT_PASSED, desc: '') @@ -44,6 +32,21 @@ module Gitlab def has_attributes? options.any? end + + def options_parser + @options_parser ||= + OptionParser.new do |parser| + options.to_a.each do |opt| + if opt.default != DEFAULT_NOT_PASSED + Runtime::Scenario.define(opt.name, opt.default, type: opt.type) + end + + parser.on(opt.arg, opt.desc) do |value| + Runtime::Scenario.define(opt.name, value, type: opt.type) + end + end + end + end end end end diff --git a/lib/gitlab/qa/framework/scenario/runner.rb b/lib/gitlab/qa/framework/scenario/runner.rb new file mode 100644 index 000000000..db2029986 --- /dev/null +++ b/lib/gitlab/qa/framework/scenario/runner.rb @@ -0,0 +1,34 @@ +require 'rspec/core' + +module Gitlab + module QA + module Framework + module Scenario + class Runner + include Gitlab::QA::Framework::Scenario::Actable + + attr_accessor :tty, :tags, :options + + def initialize + @tty = false + @tags = [] + @options = [File.expand_path('../../features', __dir__)] + end + + def perform + args = [] + args.push('--tty') if tty + tags.to_a.each { |tag| args.push(['-t', tag.to_s]) } + args.push(options) + + Framework::Runtime::Browser.configure! + + RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status| + abort if status.nonzero? + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/scenario/taggable.rb b/lib/gitlab/qa/framework/scenario/taggable.rb new file mode 100644 index 000000000..56765dd95 --- /dev/null +++ b/lib/gitlab/qa/framework/scenario/taggable.rb @@ -0,0 +1,23 @@ +module Gitlab + module QA + module Framework + module Scenario + module Taggable + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def tags(*tags) + @tags = tags # rubocop:disable Gitlab/ModuleWithInstanceVariables + end + + def focus + @tags.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables + end + end + end + end + end + end +end diff --git a/lib/gitlab/qa/framework/docker/shellout.rb b/lib/gitlab/qa/framework/utils/shellout.rb similarity index 84% rename from lib/gitlab/qa/framework/docker/shellout.rb rename to lib/gitlab/qa/framework/utils/shellout.rb index 6005cc281..e0c41254a 100644 --- a/lib/gitlab/qa/framework/docker/shellout.rb +++ b/lib/gitlab/qa/framework/utils/shellout.rb @@ -3,7 +3,7 @@ require 'open3' module Gitlab module QA module Framework - module Docker + module Utils class Shellout StatusError = Class.new(StandardError) @@ -11,7 +11,7 @@ module Gitlab @command = command @output = [] - puts "Docker shell command: `#{@command}`" + puts "Command: `#{@command}`" end def execute! @@ -29,7 +29,7 @@ module Gitlab end if wait.value.exited? && wait.value.exitstatus.nonzero? - raise StatusError, "Docker command `#{@command}` failed!" + raise StatusError, "Command `#{@command}` failed!" end end diff --git a/spec/gitlab/qa/framework/docker/command_spec.rb b/spec/gitlab/qa/framework/docker/command_spec.rb index 63a7723c2..a8c8c13f9 100644 --- a/spec/gitlab/qa/framework/docker/command_spec.rb +++ b/spec/gitlab/qa/framework/docker/command_spec.rb @@ -2,7 +2,7 @@ describe Gitlab::QA::Framework::Docker::Command do let(:docker) { spy('docker') } before do - stub_const('Gitlab::QA::Framework::Docker::Shellout', docker) + stub_const('Gitlab::QA::Framework::Utils::Shellout', docker) end describe '#<<' do diff --git a/spec/gitlab/qa/framework/docker/engine_spec.rb b/spec/gitlab/qa/framework/docker/engine_spec.rb index e8f107a1f..abe6ed0fc 100644 --- a/spec/gitlab/qa/framework/docker/engine_spec.rb +++ b/spec/gitlab/qa/framework/docker/engine_spec.rb @@ -2,7 +2,7 @@ describe Gitlab::QA::Framework::Docker::Engine do let(:docker) { spy('docker') } before do - stub_const('Gitlab::QA::Framework::Docker::Shellout', docker) + stub_const('Gitlab::QA::Framework::Utils::Shellout', docker) end describe '#pull' do diff --git a/spec/gitlab/qa/framework/runtime/env_spec.rb b/spec/gitlab/qa/framework/runtime/env_spec.rb new file mode 100644 index 000000000..4f260ff7d --- /dev/null +++ b/spec/gitlab/qa/framework/runtime/env_spec.rb @@ -0,0 +1,58 @@ +describe Gitlab::QA::Framework::Runtime::Env do + include Support::StubENV + + describe '.chrome_headless?' do + context 'when there is an env variable set' do + it 'returns false when falsey values specified' do + stub_env('CHROME_HEADLESS', 'false') + expect(described_class).not_to be_chrome_headless + + stub_env('CHROME_HEADLESS', 'no') + expect(described_class).not_to be_chrome_headless + + stub_env('CHROME_HEADLESS', '0') + expect(described_class).not_to be_chrome_headless + end + + it 'returns true when anything else specified' do + stub_env('CHROME_HEADLESS', 'true') + expect(described_class).to be_chrome_headless + + stub_env('CHROME_HEADLESS', '1') + expect(described_class).to be_chrome_headless + + stub_env('CHROME_HEADLESS', 'anything') + expect(described_class).to be_chrome_headless + end + end + + context 'when there is no env variable set' do + it 'returns the default, true' do + stub_env('CHROME_HEADLESS', nil) + expect(described_class).to be_chrome_headless + end + end + end + + describe '.running_in_ci?' do + context 'when there is an env variable set' do + it 'returns true if CI' do + stub_env('CI', 'anything') + expect(described_class).to be_running_in_ci + end + + it 'returns true if CI_SERVER' do + stub_env('CI_SERVER', 'anything') + expect(described_class).to be_running_in_ci + end + end + + context 'when there is no env variable set' do + it 'returns true' do + stub_env('CI', nil) + stub_env('CI_SERVER', nil) + expect(described_class).not_to be_running_in_ci + end + end + end +end diff --git a/spec/gitlab/qa/framework/runtime/scenario_spec.rb b/spec/gitlab/qa/framework/runtime/scenario_spec.rb index d4382b8e4..6d489d88c 100644 --- a/spec/gitlab/qa/framework/runtime/scenario_spec.rb +++ b/spec/gitlab/qa/framework/runtime/scenario_spec.rb @@ -26,9 +26,4 @@ describe Gitlab::QA::Framework::Runtime::Scenario do expect { subject.empty_attribute } .to raise_error ArgumentError, /empty_attribute/ end - - it 'fallbacks to given block return value when attribute is not known' do - expect(subject.invalid_accessor { 'hello world' }) - .to eq('hello world') - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e178ce9f8..fa437f787 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ require 'gitlab/qa' +Dir[File.expand_path('./support/**/*.rb', __dir__)].each { |f| require f } + RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true diff --git a/spec/support/stub_env.rb b/spec/support/stub_env.rb new file mode 100644 index 000000000..c4bb8df6c --- /dev/null +++ b/spec/support/stub_env.rb @@ -0,0 +1,38 @@ +# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb +module Support + module StubENV + def stub_env(key_or_hash, value = nil) + init_stub unless env_stubbed? + + if key_or_hash.is_a? Hash + key_or_hash.each { |k, v| add_stubbed_value(k, v) } + else + add_stubbed_value key_or_hash, value + end + end + + private + + STUBBED_KEY = '__STUBBED__'.freeze + + def add_stubbed_value(key, value) # rubocop:disable Metrics/AbcSize + allow(ENV).to receive(:[]).with(key).and_return(value) + allow(ENV).to receive(:key?).with(key).and_return(true) + allow(ENV).to receive(:fetch).with(key).and_return(value) + allow(ENV).to receive(:fetch).with(key, anything) do |_, default_val| + value || default_val + end + end + + def env_stubbed? + ENV[STUBBED_KEY] + end + + def init_stub + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:key?).and_call_original + allow(ENV).to receive(:fetch).and_call_original + add_stubbed_value(STUBBED_KEY, true) + end + end +end -- GitLab From 4385ab06bd09093605f1630b273d76154b5172c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 30 May 2018 13:32:58 +0200 Subject: [PATCH 7/7] Rename Runtime::Env to Runtime::Settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/qa.rb | 2 +- lib/gitlab/qa/component/gitlab.rb | 2 +- lib/gitlab/qa/component/ldap.rb | 4 ++-- lib/gitlab/qa/component/specs.rb | 4 ++-- lib/gitlab/qa/component/staging.rb | 4 ++-- lib/gitlab/qa/runtime/{env.rb => settings.rb} | 2 +- lib/gitlab/qa/scenario/test/instance/staging.rb | 2 +- lib/gitlab/qa/scenario/test/integration/geo.rb | 2 +- spec/gitlab/qa/component/gitlab_spec.rb | 2 +- spec/gitlab/qa/runtime/{env_spec.rb => settings_spec.rb} | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) rename lib/gitlab/qa/runtime/{env.rb => settings.rb} (99%) rename spec/gitlab/qa/runtime/{env_spec.rb => settings_spec.rb} (98%) diff --git a/lib/gitlab/qa.rb b/lib/gitlab/qa.rb index a0319e685..b1a26d631 100644 --- a/lib/gitlab/qa.rb +++ b/lib/gitlab/qa.rb @@ -10,8 +10,8 @@ module Gitlab end module Runtime - autoload :Env, 'qa/runtime/env' autoload :Scenario, 'qa/runtime/scenario' + autoload :Settings, 'qa/runtime/settings' end module Scenario diff --git a/lib/gitlab/qa/component/gitlab.rb b/lib/gitlab/qa/component/gitlab.rb index 82b322a55..065149544 100644 --- a/lib/gitlab/qa/component/gitlab.rb +++ b/lib/gitlab/qa/component/gitlab.rb @@ -88,7 +88,7 @@ module Gitlab command.volume(to, from, 'Z') end - File.join(Runtime::Env.logs_dir, name).tap do |logs_dir| + File.join(Runtime::Settings.logs_dir, name).tap do |logs_dir| command.volume(logs_dir, '/var/log/gitlab', 'Z') end diff --git a/lib/gitlab/qa/component/ldap.rb b/lib/gitlab/qa/component/ldap.rb index da3eedd46..0c85400dd 100644 --- a/lib/gitlab/qa/component/ldap.rb +++ b/lib/gitlab/qa/component/ldap.rb @@ -150,8 +150,8 @@ module Gitlab end def set_gitlab_credentials - ::Gitlab::QA::Runtime::Env.ldap_username = username - ::Gitlab::QA::Runtime::Env.ldap_password = password + ::Gitlab::QA::Runtime::Settings.ldap_username = username + ::Gitlab::QA::Runtime::Settings.ldap_password = password end end end diff --git a/lib/gitlab/qa/component/specs.rb b/lib/gitlab/qa/component/specs.rb index 1c97da115..5b4817d77 100644 --- a/lib/gitlab/qa/component/specs.rb +++ b/lib/gitlab/qa/component/specs.rb @@ -22,13 +22,13 @@ module Gitlab @docker.run(release.qa_image, release.tag, suite, *args) do |command| command << "-t --rm --net=#{network || 'bridge'}" - variables = Runtime::Env.variables + variables = Runtime::Settings.variables variables.each do |key, value| command.env(key, value) end command.volume('/var/run/docker.sock', '/var/run/docker.sock') - command.volume(Runtime::Env.screenshots_dir, '/home/qa/tmp') + command.volume(Runtime::Settings.screenshots_dir, '/home/qa/tmp') command.name("gitlab-specs-#{Time.now.to_i}") end end diff --git a/lib/gitlab/qa/component/staging.rb b/lib/gitlab/qa/component/staging.rb index fe3a7ff14..8107d0da1 100644 --- a/lib/gitlab/qa/component/staging.rb +++ b/lib/gitlab/qa/component/staging.rb @@ -50,10 +50,10 @@ module Gitlab private def request - Runtime::Env.require_qa_access_token! + Runtime::Settings.require_qa_access_token! @request ||= Net::HTTP::Get.new(@uri.path).tap do |req| - req['PRIVATE-TOKEN'] = Runtime::Env.qa_access_token + req['PRIVATE-TOKEN'] = Runtime::Settings.qa_access_token end end end diff --git a/lib/gitlab/qa/runtime/env.rb b/lib/gitlab/qa/runtime/settings.rb similarity index 99% rename from lib/gitlab/qa/runtime/env.rb rename to lib/gitlab/qa/runtime/settings.rb index 8ea4ca93c..de5ae86b0 100644 --- a/lib/gitlab/qa/runtime/env.rb +++ b/lib/gitlab/qa/runtime/settings.rb @@ -1,7 +1,7 @@ module Gitlab module QA module Runtime - module Env + module Settings extend self ENV_VARIABLES = { diff --git a/lib/gitlab/qa/scenario/test/instance/staging.rb b/lib/gitlab/qa/scenario/test/instance/staging.rb index 0cd1c1dc0..2a06cad2d 100644 --- a/lib/gitlab/qa/scenario/test/instance/staging.rb +++ b/lib/gitlab/qa/scenario/test/instance/staging.rb @@ -10,7 +10,7 @@ module Gitlab include Template def perform(options) - Runtime::Env.require_no_license! + Runtime::Settings.require_no_license! release = Component::Staging.release diff --git a/lib/gitlab/qa/scenario/test/integration/geo.rb b/lib/gitlab/qa/scenario/test/integration/geo.rb index 204d66bef..284563577 100644 --- a/lib/gitlab/qa/scenario/test/integration/geo.rb +++ b/lib/gitlab/qa/scenario/test/integration/geo.rb @@ -16,7 +16,7 @@ module Gitlab raise ArgumentError, 'Geo is EE only!' unless release.ee? - Runtime::Env.require_license! + Runtime::Settings.require_license! Component::Gitlab.perform do |primary| primary.release = release diff --git a/spec/gitlab/qa/component/gitlab_spec.rb b/spec/gitlab/qa/component/gitlab_spec.rb index 3db4fcebd..aa1097f32 100644 --- a/spec/gitlab/qa/component/gitlab_spec.rb +++ b/spec/gitlab/qa/component/gitlab_spec.rb @@ -120,7 +120,7 @@ describe Gitlab::QA::Component::Gitlab do end it 'bind-mounds volume with logs in an appropriate directory' do - allow(Gitlab::QA::Runtime::Env) + allow(Gitlab::QA::Runtime::Settings) .to receive(:logs_dir) .and_return('/tmp/gitlab-qa/logs') diff --git a/spec/gitlab/qa/runtime/env_spec.rb b/spec/gitlab/qa/runtime/settings_spec.rb similarity index 98% rename from spec/gitlab/qa/runtime/env_spec.rb rename to spec/gitlab/qa/runtime/settings_spec.rb index d12c9c55d..22dd44f0e 100644 --- a/spec/gitlab/qa/runtime/env_spec.rb +++ b/spec/gitlab/qa/runtime/settings_spec.rb @@ -1,4 +1,4 @@ -describe Gitlab::QA::Runtime::Env do +describe Gitlab::QA::Runtime::Settings do describe '.screenshots_dir' do context 'when there is an env variable set' do before do -- GitLab

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4