diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb index 00851a7bece..bd66b74a164 100644 --- a/qa/qa/factory/base.rb +++ b/qa/qa/factory/base.rb @@ -1,12 +1,19 @@ +require 'forwardable' + module QA 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) - Factory::Product.populate!(new) do |factory| + new.tap do |factory| yield factory if block_given? dependencies.each do |name, signature| @@ -14,19 +21,37 @@ module QA end factory.fabricate!(*args) + + return Factory::Product.populate!(self) end end - def self.dependencies - @dependencies ||= {} + def self.evaluator + @evaluator ||= Factory::Base::DSL.new(self) end - def self.dependency(factory, as:, &block) - as.tap do |name| - class_eval { attr_accessor name } + class DSL + attr_reader :dependencies, :attributes - Dependency::Signature.new(factory, block).tap do |signature| - dependencies.store(name, signature) + 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 diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb index df35bbbb443..d004e642f9b 100644 --- a/qa/qa/factory/product.rb +++ b/qa/qa/factory/product.rb @@ -5,8 +5,9 @@ module QA class Product include Capybara::DSL - def initialize(factory) - @factory = factory + Attribute = Struct.new(:name, :block) + + def initialize @location = current_url end @@ -15,11 +16,13 @@ module QA end def self.populate!(factory) - raise ArgumentError unless block_given? - - yield factory - - new(factory) + new.tap do |product| + factory.attributes.each_value do |attribute| + product.instance_exec(&attribute.block).tap do |value| + product.define_singleton_method(attribute.name) { value } + end + end + end end end end diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb index 07c2e3086d1..7df2dc6618c 100644 --- a/qa/qa/factory/resource/project.rb +++ b/qa/qa/factory/resource/project.rb @@ -13,6 +13,10 @@ module QA @description = 'My awesome project' end + product :name do + Page::Project::Show.act { project_name } + end + def fabricate! group.visit! diff --git a/qa/qa/specs/features/project/create_spec.rb b/qa/qa/specs/features/project/create_spec.rb index 61c19378ae0..b1c07249892 100644 --- a/qa/qa/specs/features/project/create_spec.rb +++ b/qa/qa/specs/features/project/create_spec.rb @@ -4,11 +4,13 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - Factory::Resource::Project.fabricate! do |project| + created_project = Factory::Resource::Project.fabricate! do |project| project.name = 'awesome-project' project.description = 'create awesome project test' end + expect(created_project.name).to match /^awesome-project-\h{16}$/ + expect(page).to have_content( /Project \S?awesome-project\S+ was successfully created/ ) diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb index a3ba0176819..90dd58e20fd 100644 --- a/qa/spec/factory/base_spec.rb +++ b/qa/spec/factory/base_spec.rb @@ -1,8 +1,9 @@ describe QA::Factory::Base do + let(:factory) { spy('factory') } + let(:product) { spy('product') } + 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) @@ -59,30 +60,63 @@ describe QA::Factory::Base do 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(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 'building dependencies' do - let(:dependency) { double('dependency') } - let(:instance) { spy('instance') } - + describe '.product' do subject do Class.new(described_class) do - dependency Some::MyDependency, as: :mydep + product :token do + page.do_something_on_page! + 'resulting value' + end 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) + it 'appends new product attribute' do + expect(subject.attributes).to be_one + expect(subject.attributes).to have_key(:token) end - it 'builds all dependencies first' do - expect(dependency).to receive(:fabricate!).once + describe 'populating fabrication product with data' do + let(:page) { spy('page') } - subject.fabricate! + before do + allow(subject).to receive(:new).and_return(factory) + allow(QA::Factory::Product).to receive(:new).and_return(product) + allow(product).to receive(:page).and_return(page) + end + + it 'populates product after fabrication' do + subject.fabricate! + + expect(page).to have_received(:do_something_on_page!) + expect(product.token).to eq 'resulting value' + end end end end diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb index 3d9e86a641b..fdfb1ec90cc 100644 --- a/qa/spec/factory/product_spec.rb +++ b/qa/spec/factory/product_spec.rb @@ -3,19 +3,8 @@ describe QA::Factory::Product do 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) + expect(described_class).to receive(:new).and_return(product) result = described_class.populate!(factory) do |instance| instance.something = 'string' @@ -23,11 +12,6 @@ describe QA::Factory::Product do 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 @@ -37,8 +21,7 @@ describe QA::Factory::Product do 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' + expect(subject.visit!).to eq 'visited some url' end end end