diff options
author | Rémy Coutable <remy@rymai.me> | 2017-12-21 09:39:31 +0000 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2017-12-21 09:39:31 +0000 |
commit | bc8e328fe4df7965e3ad80ee722cceb7c5b16261 (patch) | |
tree | 3d4b7682786a532005df732602dfd2067557fd1e | |
parent | 88f41bf61f5602aeca46c31d0b7ba76827543b29 (diff) | |
parent | 6df36c9d907d205ab977d5d2f81fb167e0dc969c (diff) | |
download | gitlab-ce-bc8e328fe4df7965e3ad80ee722cceb7c5b16261.tar.gz |
Merge branch 'qa/gb/add-dependencies-support-to-factories' into 'master'
Add support for defining explicit dependencies to QA factories
Closes gitlab-qa#122
See merge request gitlab-org/gitlab-ce!15979
-rw-r--r-- | qa/qa.rb | 2 | ||||
-rw-r--r-- | qa/qa/factory/base.rb | 27 | ||||
-rw-r--r-- | qa/qa/factory/dependency.rb | 38 | ||||
-rw-r--r-- | qa/qa/factory/product.rb | 26 | ||||
-rw-r--r-- | qa/qa/factory/repository/push.rb | 19 | ||||
-rw-r--r-- | qa/qa/factory/resource/group.rb | 22 | ||||
-rw-r--r-- | qa/qa/factory/resource/project.rb | 19 | ||||
-rw-r--r-- | qa/qa/factory/resource/sandbox.rb | 16 | ||||
-rw-r--r-- | qa/qa/page/group/show.rb | 2 | ||||
-rw-r--r-- | qa/qa/specs/features/repository/push_spec.rb | 15 | ||||
-rw-r--r-- | qa/spec/factory/base_spec.rb | 88 | ||||
-rw-r--r-- | qa/spec/factory/dependency_spec.rb | 59 | ||||
-rw-r--r-- | qa/spec/factory/product_spec.rb | 44 |
13 files changed, 327 insertions, 50 deletions
@@ -17,6 +17,8 @@ module QA # module Factory autoload :Base, 'qa/factory/base' + autoload :Dependency, 'qa/factory/dependency' + autoload :Product, 'qa/factory/product' module Resource autoload :Sandbox, 'qa/factory/resource/sandbox' diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb index 7b951a99b69..00851a7bece 100644 --- a/qa/qa/factory/base.rb +++ b/qa/qa/factory/base.rb @@ -1,15 +1,34 @@ module QA module Factory class Base + def fabricate!(*_args) + raise NotImplementedError + end + def self.fabricate!(*args) - new.tap do |factory| + Factory::Product.populate!(new) do |factory| yield factory if block_given? - return factory.fabricate!(*args) + + dependencies.each do |name, signature| + Factory::Dependency.new(name, factory, signature).build! + end + + factory.fabricate!(*args) end end - def fabricate!(*_args) - raise NotImplementedError + def self.dependencies + @dependencies ||= {} + end + + def self.dependency(factory, as:, &block) + as.tap do |name| + class_eval { attr_accessor name } + + Dependency::Signature.new(factory, block).tap do |signature| + dependencies.store(name, signature) + end + end end end end diff --git a/qa/qa/factory/dependency.rb b/qa/qa/factory/dependency.rb new file mode 100644 index 00000000000..d0e85a68237 --- /dev/null +++ b/qa/qa/factory/dependency.rb @@ -0,0 +1,38 @@ +module QA + 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).fabricate!.tap do |product| + @factory.public_send("#{@name}=", product) + end + end + + class Builder + def initialize(signature) + @factory = signature.factory + @block = signature.block + end + + def fabricate! + @factory.fabricate! do |factory| + @block&.call(factory) + end + end + end + end + end +end diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb new file mode 100644 index 00000000000..df35bbbb443 --- /dev/null +++ b/qa/qa/factory/product.rb @@ -0,0 +1,26 @@ +require 'capybara/dsl' + +module QA + module Factory + class Product + include Capybara::DSL + + def initialize(factory) + @factory = factory + @location = current_url + end + + def visit! + visit @location + end + + def self.populate!(factory) + raise ArgumentError unless block_given? + + yield factory + + new(factory) + end + end + end +end diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb index 1d5375d8c76..2f4de4173d4 100644 --- a/qa/qa/factory/repository/push.rb +++ b/qa/qa/factory/repository/push.rb @@ -1,16 +1,13 @@ -require "pry-byebug" - module QA module Factory module Repository class Push < Factory::Base - PAGE_REGEX_CHECK = - %r{\/#{Runtime::Namespace.sandbox_name}\/qa-test[^\/]+\/{1}[^\/]+\z}.freeze + attr_writer :file_name, :file_content, :commit_message, :branch_name - attr_writer :file_name, - :file_content, - :commit_message, - :branch_name + dependency Factory::Resource::Project, as: :project do |project| + project.name = 'project-with-code' + project.description = 'Project with repository' + end def initialize @file_name = 'file.txt' @@ -20,12 +17,10 @@ module QA end def fabricate! + project.visit! + Git::Repository.perform do |repository| repository.location = Page::Project::Show.act do - unless PAGE_REGEX_CHECK.match(current_path) - raise "To perform this scenario the current page should be project show." - end - choose_repository_clone_http repository_location end diff --git a/qa/qa/factory/resource/group.rb b/qa/qa/factory/resource/group.rb index a081cd94d39..9f13e26f35c 100644 --- a/qa/qa/factory/resource/group.rb +++ b/qa/qa/factory/resource/group.rb @@ -4,17 +4,29 @@ module QA class Group < Factory::Base attr_writer :path, :description + dependency Factory::Resource::Sandbox, as: :sandbox + def initialize @path = Runtime::Namespace.name @description = "QA test run at #{Runtime::Namespace.time}" end def fabricate! - Page::Group::New.perform do |group| - group.set_path(@path) - group.set_description(@description) - group.set_visibility('Private') - group.create + sandbox.visit! + + Page::Group::Show.perform do |page| + if page.has_subgroup?(@path) + page.go_to_subgroup(@path) + else + page.go_to_new_subgroup + + Page::Group::New.perform do |group| + group.set_path(@path) + group.set_description(@description) + group.set_visibility('Private') + group.create + end + end end end end diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb index 64fcfb084bb..07c2e3086d1 100644 --- a/qa/qa/factory/resource/project.rb +++ b/qa/qa/factory/resource/project.rb @@ -6,26 +6,17 @@ module QA class Project < Factory::Base attr_writer :description + dependency Factory::Resource::Group, as: :group + def name=(name) @name = "#{name}-#{SecureRandom.hex(8)}" + @description = 'My awesome project' end def fabricate! - Factory::Resource::Sandbox.fabricate! - - Page::Group::Show.perform do |page| - if page.has_subgroup?(Runtime::Namespace.name) - page.go_to_subgroup(Runtime::Namespace.name) - else - page.go_to_new_subgroup + group.visit! - Factory::Resource::Group.fabricate! do |group| - group.path = Runtime::Namespace.name - end - end - - page.go_to_new_project - end + Page::Group::Show.act { go_to_new_project } Page::Project::New.perform do |page| page.choose_test_namespace diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb index fd2177915c5..558da1c973b 100644 --- a/qa/qa/factory/resource/sandbox.rb +++ b/qa/qa/factory/resource/sandbox.rb @@ -6,18 +6,24 @@ module QA # creating it if it doesn't yet exist. # class Sandbox < Factory::Base + def initialize + @name = Runtime::Namespace.sandbox_name + end + def fabricate! Page::Main::Menu.act { go_to_groups } Page::Dashboard::Groups.perform do |page| - if page.has_group?(Runtime::Namespace.sandbox_name) - page.go_to_group(Runtime::Namespace.sandbox_name) + if page.has_group?(@name) + page.go_to_group(@name) else page.go_to_new_group - Resource::Group.fabricate! do |group| - group.path = Runtime::Namespace.sandbox_name - group.description = 'GitLab QA Sandbox' + Page::Group::New.perform do |group| + group.set_path(@name) + group.set_description('GitLab QA Sandbox') + group.set_visibility('Private') + group.create end end end diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb index 100e71ae157..0a16c07d64b 100644 --- a/qa/qa/page/group/show.rb +++ b/qa/qa/page/group/show.rb @@ -21,6 +21,7 @@ module QA find('.dropdown-toggle').click find("li[data-value='new-subgroup']").click end + find("input[data-action='new-subgroup']").click end @@ -29,6 +30,7 @@ module QA find('.dropdown-toggle').click find("li[data-value='new-project']").click end + find("input[data-action='new-project']").click end end diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/repository/push_spec.rb index e47c769b015..4f6ffe14c9f 100644 --- a/qa/qa/specs/features/repository/push_spec.rb +++ b/qa/qa/specs/features/repository/push_spec.rb @@ -5,15 +5,10 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - Factory::Resource::Project.fabricate! do |scenario| - scenario.name = 'project_with_code' - scenario.description = 'project with repository' - end - - Factory::Repository::Push.fabricate! do |scenario| - scenario.file_name = 'README.md' - scenario.file_content = '# This is test project' - scenario.commit_message = 'Add README.md' + Factory::Repository::Push.fabricate! do |push| + push.file_name = 'README.md' + push.file_content = '# This is a test project' + push.commit_message = 'Add README.md' end Page::Project::Show.act do @@ -22,7 +17,7 @@ module QA end expect(page).to have_content('README.md') - expect(page).to have_content('This is test project') + expect(page).to have_content('This is a test project') end end end diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb new file mode 100644 index 00000000000..a3ba0176819 --- /dev/null +++ b/qa/spec/factory/base_spec.rb @@ -0,0 +1,88 @@ +describe QA::Factory::Base do + describe '.fabricate!' do + subject { Class.new(described_class) } + let(:factory) { spy('factory') } + let(:product) { spy('product') } + + before do + allow(QA::Factory::Product).to receive(:new).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) + allow(factory).to receive(:fabricate!).and_return('something') + + 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! do |factory| + factory.something! + end + + 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 do |factory| + factory.something! + end + 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 + end + + describe 'building dependencies' 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(QA::Factory::Product).to receive(:new) + end + + it 'builds all dependencies first' do + expect(dependency).to receive(:fabricate!).once + + subject.fabricate! + end + end +end diff --git a/qa/spec/factory/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb new file mode 100644 index 00000000000..32405415126 --- /dev/null +++ b/qa/spec/factory/dependency_spec.rb @@ -0,0 +1,59 @@ +describe QA::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 + end + end +end diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb new file mode 100644 index 00000000000..3d9e86a641b --- /dev/null +++ b/qa/spec/factory/product_spec.rb @@ -0,0 +1,44 @@ +describe QA::Factory::Product do + let(:factory) { spy('factory') } + let(:product) { spy('product') } + + describe '.populate!' do + it 'instantiates and yields factory' do + expect(described_class).to receive(:new).with(factory) + + described_class.populate!(factory) do |instance| + instance.something = 'string' + end + + expect(factory).to have_received(:something=).with('string') + end + + it 'returns a fabrication product' do + expect(described_class).to receive(:new) + .with(factory).and_return(product) + + result = described_class.populate!(factory) do |instance| + instance.something = 'string' + end + + expect(result).to be product + end + + it 'raises unless block given' do + expect { described_class.populate!(factory) } + .to raise_error ArgumentError + 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(described_class.new(factory).visit!) + .to eq 'visited some url' + end + end +end |