Commit bc8e328f authored by Rémy Coutable's avatar Rémy Coutable

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
parents 88f41bf6 6df36c9d
...@@ -17,6 +17,8 @@ module QA ...@@ -17,6 +17,8 @@ module QA
# #
module Factory module Factory
autoload :Base, 'qa/factory/base' autoload :Base, 'qa/factory/base'
autoload :Dependency, 'qa/factory/dependency'
autoload :Product, 'qa/factory/product'
module Resource module Resource
autoload :Sandbox, 'qa/factory/resource/sandbox' autoload :Sandbox, 'qa/factory/resource/sandbox'
......
module QA module QA
module Factory module Factory
class Base class Base
def fabricate!(*_args)
raise NotImplementedError
end
def self.fabricate!(*args) def self.fabricate!(*args)
new.tap do |factory| Factory::Product.populate!(new) do |factory|
yield factory if block_given? 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
end end
def fabricate!(*_args) def self.dependencies
raise NotImplementedError @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 end
end end
......
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
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
require "pry-byebug"
module QA module QA
module Factory module Factory
module Repository module Repository
class Push < Factory::Base class Push < Factory::Base
PAGE_REGEX_CHECK = attr_writer :file_name, :file_content, :commit_message, :branch_name
%r{\/#{Runtime::Namespace.sandbox_name}\/qa-test[^\/]+\/{1}[^\/]+\z}.freeze
attr_writer :file_name, dependency Factory::Resource::Project, as: :project do |project|
:file_content, project.name = 'project-with-code'
:commit_message, project.description = 'Project with repository'
:branch_name end
def initialize def initialize
@file_name = 'file.txt' @file_name = 'file.txt'
...@@ -20,12 +17,10 @@ module QA ...@@ -20,12 +17,10 @@ module QA
end end
def fabricate! def fabricate!
project.visit!
Git::Repository.perform do |repository| Git::Repository.perform do |repository|
repository.location = Page::Project::Show.act do 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 choose_repository_clone_http
repository_location repository_location
end end
......
...@@ -4,12 +4,22 @@ module QA ...@@ -4,12 +4,22 @@ module QA
class Group < Factory::Base class Group < Factory::Base
attr_writer :path, :description attr_writer :path, :description
dependency Factory::Resource::Sandbox, as: :sandbox
def initialize def initialize
@path = Runtime::Namespace.name @path = Runtime::Namespace.name
@description = "QA test run at #{Runtime::Namespace.time}" @description = "QA test run at #{Runtime::Namespace.time}"
end end
def fabricate! def fabricate!
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| Page::Group::New.perform do |group|
group.set_path(@path) group.set_path(@path)
group.set_description(@description) group.set_description(@description)
...@@ -20,4 +30,6 @@ module QA ...@@ -20,4 +30,6 @@ module QA
end end
end end
end end
end
end
end end
...@@ -6,26 +6,17 @@ module QA ...@@ -6,26 +6,17 @@ module QA
class Project < Factory::Base class Project < Factory::Base
attr_writer :description attr_writer :description
dependency Factory::Resource::Group, as: :group
def name=(name) def name=(name)
@name = "#{name}-#{SecureRandom.hex(8)}" @name = "#{name}-#{SecureRandom.hex(8)}"
@description = 'My awesome project'
end end
def fabricate! def fabricate!
Factory::Resource::Sandbox.fabricate! group.visit!
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
Factory::Resource::Group.fabricate! do |group|
group.path = Runtime::Namespace.name
end
end
page.go_to_new_project Page::Group::Show.act { go_to_new_project }
end
Page::Project::New.perform do |page| Page::Project::New.perform do |page|
page.choose_test_namespace page.choose_test_namespace
......
...@@ -6,18 +6,24 @@ module QA ...@@ -6,18 +6,24 @@ module QA
# creating it if it doesn't yet exist. # creating it if it doesn't yet exist.
# #
class Sandbox < Factory::Base class Sandbox < Factory::Base
def initialize
@name = Runtime::Namespace.sandbox_name
end
def fabricate! def fabricate!
Page::Main::Menu.act { go_to_groups } Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page| Page::Dashboard::Groups.perform do |page|
if page.has_group?(Runtime::Namespace.sandbox_name) if page.has_group?(@name)
page.go_to_group(Runtime::Namespace.sandbox_name) page.go_to_group(@name)
else else
page.go_to_new_group page.go_to_new_group
Resource::Group.fabricate! do |group| Page::Group::New.perform do |group|
group.path = Runtime::Namespace.sandbox_name group.set_path(@name)
group.description = 'GitLab QA Sandbox' group.set_description('GitLab QA Sandbox')
group.set_visibility('Private')
group.create
end end
end end
end end
......
...@@ -21,6 +21,7 @@ module QA ...@@ -21,6 +21,7 @@ module QA
find('.dropdown-toggle').click find('.dropdown-toggle').click
find("li[data-value='new-subgroup']").click find("li[data-value='new-subgroup']").click
end end
find("input[data-action='new-subgroup']").click find("input[data-action='new-subgroup']").click
end end
...@@ -29,6 +30,7 @@ module QA ...@@ -29,6 +30,7 @@ module QA
find('.dropdown-toggle').click find('.dropdown-toggle').click
find("li[data-value='new-project']").click find("li[data-value='new-project']").click
end end
find("input[data-action='new-project']").click find("input[data-action='new-project']").click
end end
end end
......
...@@ -5,15 +5,10 @@ module QA ...@@ -5,15 +5,10 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Project.fabricate! do |scenario| Factory::Repository::Push.fabricate! do |push|
scenario.name = 'project_with_code' push.file_name = 'README.md'
scenario.description = 'project with repository' push.file_content = '# This is a test project'
end push.commit_message = 'Add README.md'
Factory::Repository::Push.fabricate! do |scenario|
scenario.file_name = 'README.md'
scenario.file_content = '# This is test project'
scenario.commit_message = 'Add README.md'
end end
Page::Project::Show.act do Page::Project::Show.act do
...@@ -22,7 +17,7 @@ module QA ...@@ -22,7 +17,7 @@ module QA
end end
expect(page).to have_content('README.md') 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 end
end end
......
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
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
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment