diff options
author | Rémy Coutable <remy@rymai.me> | 2018-09-27 18:43:44 +0200 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2018-10-02 18:36:16 +0200 |
commit | 98c7cbc8ce8402557d46799e7cabac59a2a5b238 (patch) | |
tree | 2630f01e941ca94f11178d30c3aa7462cf4d7605 | |
parent | 39265b5ab46001a27e8ba009dac1da9126547859 (diff) | |
download | gitlab-ce-qa-123.tar.gz |
Simplify API fabrication internal methodsqa-123
Also, raise an error when a product attribute doesn't have a value.
Signed-off-by: Rémy Coutable <remy@rymai.me>
-rw-r--r-- | qa/qa/factory/api_fabricator.rb | 32 | ||||
-rw-r--r-- | qa/qa/factory/base.rb | 56 | ||||
-rw-r--r-- | qa/qa/factory/product.rb | 37 | ||||
-rw-r--r-- | qa/qa/factory/resource/group.rb | 10 | ||||
-rw-r--r-- | qa/qa/factory/resource/project.rb | 14 | ||||
-rw-r--r-- | qa/qa/factory/resource/sandbox.rb | 26 | ||||
-rw-r--r-- | qa/qa/factory/resource/wiki.rb | 12 | ||||
-rw-r--r-- | qa/qa/runtime/env.rb | 2 | ||||
-rw-r--r-- | qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb | 17 | ||||
-rw-r--r-- | qa/spec/factory/api_fabricator_spec.rb | 61 | ||||
-rw-r--r-- | qa/spec/factory/base_spec.rb | 205 | ||||
-rw-r--r-- | qa/spec/factory/product_spec.rb | 60 | ||||
-rw-r--r-- | qa/spec/runtime/env_spec.rb | 66 |
13 files changed, 250 insertions, 348 deletions
diff --git a/qa/qa/factory/api_fabricator.rb b/qa/qa/factory/api_fabricator.rb index 85d6286207e..0c8f994c90f 100644 --- a/qa/qa/factory/api_fabricator.rb +++ b/qa/qa/factory/api_fabricator.rb @@ -2,11 +2,13 @@ require 'airborne' require 'active_support/core_ext/object/deep_dup' +require 'capybara/dsl' module QA module Factory module ApiFabricator include Airborne + include Capybara::DSL ResourceNotFoundError = Class.new(RuntimeError) ResourceFabricationFailedError = Class.new(RuntimeError) @@ -21,15 +23,7 @@ module QA alias_method :api_post_path, :api_get_path alias_method :api_post_body, :api_get_path - def api_support? - api_get_path && api_post_path && api_post_body - rescue NotImplementedError - false - end - def fabricate_via_api! - resource_web_url(api_get) - rescue ResourceNotFoundError resource_web_url(api_post) end @@ -37,41 +31,43 @@ module QA attr_writer :api_resource, :api_response + def api_support? + api_get_path && api_post_path && api_post_body + rescue NotImplementedError + false + end + def resource_web_url(resource) - unless resource.key?(:web_url) + resource.fetch(:web_url) do raise ResourceURLMissingError, "API resource for #{self.class.name} does not expose a `web_url` property: `#{resource}`." end - - resource[:web_url] end def api_get url = Runtime::API::Request.new(api_client, api_get_path).url response = get(url) - parsed_response = parse_body(response) unless response.code == 200 - raise ResourceNotFoundError, "Resource at #{url} could not be found (#{response.code}): `#{parsed_response}`." + raise ResourceNotFoundError, "Resource at #{url} could not be found (#{response.code}): `#{response}`." end - process_api_response(parsed_response) + process_api_response(parse_body(response)) end def api_post response = post( Runtime::API::Request.new(api_client, api_post_path).url, api_post_body) - parsed_response = parse_body(response) unless response.code == 201 - raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{parsed_response}`." + raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`." end - process_api_response(parsed_response) + process_api_response(parse_body(response)) end def api_client - @api_client ||= Runtime::API::Client.new(:gitlab, is_new_session: false) + @api_client ||= Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http')) end def parse_body(response) diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb index c3f154d1203..a254a831b66 100644 --- a/qa/qa/factory/base.rb +++ b/qa/qa/factory/base.rb @@ -18,62 +18,52 @@ module QA end def self.fabricate!(*args, &prepare_block) - do_fabricate!(prepare_block, *args) do |factory| - if factory.api_support? - log_fabrication(:do_fabricate_via_api, factory, *args) - else - log_fabrication(:do_fabricate_via_browser_ui, factory, *args) - end - end + fabricate_via_api!(*args, &prepare_block) + rescue NotImplementedError + fabricate_via_browser_ui!(*args, &prepare_block) end def self.fabricate_via_browser_ui!(*args, &prepare_block) - do_fabricate!(prepare_block, *args) do |factory| - log_fabrication(:do_fabricate_via_browser_ui, factory, *args) + do_fabricate!(prepare_block) do |factory| + log_fabrication(:browser_ui, factory) { factory.fabricate!(*args) } + + current_url end end def self.fabricate_via_api!(*args, &prepare_block) - do_fabricate!(prepare_block, *args) do |factory| - log_fabrication(:do_fabricate_via_api, factory, *args) + do_fabricate!(prepare_block) do |factory| + log_fabrication(:api, factory) { factory.fabricate_via_api! } end end - def self.do_fabricate!(prepare_block, *args) - new.tap do |factory| - prepare_block.call(factory) if prepare_block - - dependencies.each do |signature| - Factory::Dependency.new(factory, signature).build! - end + def self.do_fabricate!(prepare_block) + factory = new + prepare_block.call(factory) if prepare_block - resource_web_url = yield(factory) - - break Factory::Product.populate!(factory, resource_web_url) + dependencies.each do |signature| + Factory::Dependency.new(factory, signature).build! end - end - def self.do_fabricate_via_browser_ui(factory, *args) - factory.fabricate!(*args) - - current_url - end + resource_web_url = yield(factory) - def self.do_fabricate_via_api(factory, *_args) - factory.fabricate_via_api! + Factory::Product.populate!(factory, resource_web_url) end + private_class_method :do_fabricate! - def self.log_fabrication(method, factory, *args) + def self.log_fabrication(method, factory) start = Time.now - public_send(method, factory, *args).tap do + yield.tap do puts "Resource #{factory.class.name} built via #{method} in #{Time.now - start} seconds" if Runtime::Env.verbose? end end + private_class_method :log_fabrication def self.evaluator @evaluator ||= Factory::Base::DSL.new(self) end + private_class_method :evaluator class DSL attr_reader :dependencies, :attributes @@ -81,7 +71,7 @@ module QA def initialize(base) @base = base @dependencies = [] - @attributes = {} + @attributes = [] end def dependency(factory, as:, &block) @@ -96,7 +86,7 @@ module QA def product(attribute, &block) Product::Attribute.new(attribute, block).tap do |signature| - @attributes.store(attribute, signature) + @attributes << signature end end end diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb index dc30701a9d4..5ee2552eeb5 100644 --- a/qa/qa/factory/product.rb +++ b/qa/qa/factory/product.rb @@ -5,28 +5,47 @@ module QA class Product include Capybara::DSL - attr_reader :web_url + NoValueError = Class.new(RuntimeError) + + attr_reader :factory, :web_url Attribute = Struct.new(:name, :block) - def initialize(web_url) + def initialize(factory, web_url) + @factory = factory @web_url = web_url + + populate_attributes! end def visit! - visit @web_url + visit(web_url) end def self.populate!(factory, web_url) - new(web_url).tap do |product| - factory.class.attributes.each_value do |attribute| - product.instance_exec(factory, attribute.block) do |factory, block| - value = factory.api_resource&.dig(attribute.name) || block.call(factory) - product.define_singleton_method(attribute.name) { value } - end + new(factory, web_url) + end + + private + + def populate_attributes! + factory.class.attributes.each do |attribute| + instance_exec(factory, attribute.block) do |factory, block| + value = attribute_value(attribute, block) + + raise NoValueError, "No value was computed for product #{attribute.name} of factory #{factory.class.name}." unless value + + define_singleton_method(attribute.name) { value } end end end + + def attribute_value(attribute, block) + value = factory.api_resource&.dig(attribute.name) || (factory.respond_to?(attribute.name) && factory.public_send(attribute.name)) + value ||= block.call(factory) if block + + value + end end end end diff --git a/qa/qa/factory/resource/group.rb b/qa/qa/factory/resource/group.rb index b6703c202b5..2688328df92 100644 --- a/qa/qa/factory/resource/group.rb +++ b/qa/qa/factory/resource/group.rb @@ -6,7 +6,9 @@ module QA dependency Factory::Resource::Sandbox, as: :sandbox - product :id + product :id do + true # We don't retrieve the Group ID when using the Browser UI + end def initialize @path = Runtime::Namespace.name @@ -38,6 +40,12 @@ module QA end end + def fabricate_via_api! + resource_web_url(api_get) + rescue ResourceNotFoundError + super + end + def api_get_path "/groups/#{CGI.escape("#{sandbox.path}/#{path}")}" end diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb index 166ef00bf56..5b92afa9da2 100644 --- a/qa/qa/factory/resource/project.rb +++ b/qa/qa/factory/resource/project.rb @@ -11,9 +11,19 @@ module QA product :name - product :repository_ssh_location + product :repository_ssh_location do + Page::Project::Show.act do + choose_repository_clone_ssh + repository_location + end + end - product :repository_http_location + product :repository_http_location do + Page::Project::Show.act do + choose_repository_clone_http + repository_location + end + end def initialize @description = 'My awesome project' diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb index 6f2c3f22120..eaa83e1a0a3 100644 --- a/qa/qa/factory/resource/sandbox.rb +++ b/qa/qa/factory/resource/sandbox.rb @@ -6,27 +6,29 @@ module QA # creating it if it doesn't yet exist. # class Sandbox < Factory::Base - attr_reader :group_path + attr_reader :path - product :id + product :id do + true # We don't retrieve the Group ID when using the Browser UI + end product :path def initialize - @group_path = Runtime::Namespace.sandbox_name + @path = Runtime::Namespace.sandbox_name end def fabricate! Page::Menu::Main.act { go_to_groups } Page::Dashboard::Groups.perform do |page| - if page.has_group?(group_path) - page.go_to_group(group_path) + if page.has_group?(path) + page.go_to_group(path) else page.go_to_new_group Page::Group::New.perform do |group| - group.set_path(group_path) + group.set_path(path) group.set_description('GitLab QA Sandbox Group') group.set_visibility('Public') group.create @@ -35,8 +37,14 @@ module QA end end + def fabricate_via_api! + resource_web_url(api_get) + rescue ResourceNotFoundError + super + end + def api_get_path - "/groups/#{group_path}" + "/groups/#{path}" end def api_post_path @@ -45,8 +53,8 @@ module QA def api_post_body { - path: group_path, - name: group_path, + path: path, + name: path, visibility: 'public' } end diff --git a/qa/qa/factory/resource/wiki.rb b/qa/qa/factory/resource/wiki.rb index acd3f0da171..4a733432311 100644 --- a/qa/qa/factory/resource/wiki.rb +++ b/qa/qa/factory/resource/wiki.rb @@ -14,12 +14,12 @@ module QA Page::Menu::Side.perform { |menu_side| menu_side.click_wiki } - Page::Project::Wiki::New.perform do |page| - page.go_to_create_first_page - page.set_title(@title) - page.set_content(@content) - page.set_message(@message) - page.create_new_page + Page::Project::Wiki::New.perform do |wiki_new| + wiki_new.go_to_create_first_page + wiki_new.set_title(@title) + wiki_new.set_content(@content) + wiki_new.set_message(@message) + wiki_new.create_new_page end end end diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 0c3ed2d77f5..457dc1fe43b 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -40,7 +40,7 @@ module QA end def forker? - forker_username && forker_password + !!(forker_username && forker_password) end def forker_username diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb index f344edeec0c..c8ea558aed6 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb @@ -7,21 +7,12 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - group = Factory::Resource::Group.fabricate! - group.visit! - Page::Group::Show.act { go_to_new_project } - - name = "awesome-project-#{SecureRandom.hex(8)}" - - Page::Project::New.perform do |page| - page.choose_test_namespace - page.choose_name(name) - page.add_description('create awesome project test') - page.set_visibility('Public') - page.create_new_project + created_project = Factory::Resource::Project.fabricate_via_browser_ui! do |project| + project.name = 'awesome-project' + project.description = 'create awesome project test' end - expect(page).to have_content(name) + expect(page).to have_content(created_project.name) expect(page).to have_content( /Project \S?awesome-project\S+ was successfully created/ ) diff --git a/qa/spec/factory/api_fabricator_spec.rb b/qa/spec/factory/api_fabricator_spec.rb index 3fda5407549..8f8c29dd92d 100644 --- a/qa/spec/factory/api_fabricator_spec.rb +++ b/qa/spec/factory/api_fabricator_spec.rb @@ -27,26 +27,12 @@ describe QA::Factory::ApiFabricator do end end - subject { factory.tap { |f| f.include(described_class) }.new } - - describe '#api_support?' do - context 'when factory does not respond to api_get_path, api_post_path, and api_post_body' do - let(:factory) { factory_without_api_support } - - it 'returns false' do - expect(subject).not_to be_api_support - end - end - - context 'when factory responds to api_get_path, api_post_path, and api_post_body' do - let(:factory) { factory_with_api_support } - - it 'returns true' do - expect(subject).to be_api_support - end - end + before do + allow(subject).to receive(:current_url).and_return('') end + subject { factory.tap { |f| f.include(described_class) }.new } + describe '#fabricate_via_api!' do context 'when factory does not support fabrication via the API' do let(:factory) { factory_without_api_support } @@ -63,7 +49,7 @@ describe QA::Factory::ApiFabricator do let(:api_request) { spy('Runtime::API::Request') } let(:resource_web_url) { 'http://example.org/api/v4/foo' } let(:resource) { { id: 1, name: 'John Doe', web_url: resource_web_url } } - let(:raw_get) { double('Raw GET response', code: 200, body: resource.to_json) } + let(:raw_post) { double('Raw POST response', code: 201, body: resource.to_json) } before do stub_const('QA::Runtime::API::Client', api_client) @@ -71,44 +57,15 @@ describe QA::Factory::ApiFabricator do allow(api_client).to receive(:new).and_return(api_client_instance) allow(api_request).to receive(:new).and_return(double(url: resource_web_url)) - allow(subject).to receive(:get).with(resource_web_url).and_return(raw_get) - end - - context 'when resource already exists' do - it 'returns the resource URL' do - expect(api_request).to receive(:new).with(api_client_instance, subject.api_get_path).and_return(double(url: resource_web_url)) - expect(subject).to receive(:get).with(resource_web_url).and_return(raw_get) - - expect(subject.fabricate_via_api!).to eq(resource_web_url) - end - - it 'populates api_resource with the resource' do - subject.fabricate_via_api! - - expect(subject.api_resource).to eq(resource) - end - - context 'when the resource does not expose a `web_url` property' do - let(:resource) { { id: 1, name: 'John Doe' } } - - it 'raises a ResourceFabricationFailedError exception' do - expect { subject.fabricate_via_api! }.to raise_error(described_class::ResourceURLMissingError, "API resource for FooBarFactory does not expose a `web_url` property: `#{resource}`.") - expect(subject.api_resource).to eq(resource) - end - end end context 'when the resource does not exist' do - let(:raw_get) { double('Raw GET response', code: 404, body: { error: "404 not found." }.to_json) } - let(:raw_post) { double('Raw POST response', code: 201, body: resource.to_json) } - before do allow(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) end it 'returns the resource URL' do - expect(api_request).to receive(:new).with(api_client_instance, subject.api_get_path).and_return(double(url: resource_web_url)) - expect(subject).to receive(:get).with(resource_web_url).and_return(raw_get) + expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url)) expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) expect(subject.fabricate_via_api!).to eq(resource_web_url) @@ -125,11 +82,10 @@ describe QA::Factory::ApiFabricator do let(:raw_post) { double('Raw POST response', code: 400, body: post_response.to_json) } it 'raises a ResourceFabricationFailedError exception' do - expect(api_request).to receive(:new).with(api_client_instance, subject.api_get_path).and_return(double(url: resource_web_url)) - expect(subject).to receive(:get).with(resource_web_url).and_return(raw_get) + expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url)) expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) - expect { subject.fabricate_via_api! }.to raise_error(described_class::ResourceFabricationFailedError, "Fabrication of FooBarFactory using the API failed (400) with `#{post_response}`.") + expect { subject.fabricate_via_api! }.to raise_error(described_class::ResourceFabricationFailedError, "Fabrication of FooBarFactory using the API failed (400) with `#{raw_post}`.") expect(subject.api_resource).to be_nil end end @@ -165,6 +121,7 @@ describe QA::Factory::ApiFabricator do let(:transformed_resource) { { existing: 'foo', new: 'foobar', web_url: resource_web_url } } it 'transforms the resource' do + expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post) expect(subject).to receive(:transform_api_resource).with(resource).and_return(transformed_resource) subject.fabricate_via_api! diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb index 0457d878270..b9cf60e4cc0 100644 --- a/qa/spec/factory/base_spec.rb +++ b/qa/spec/factory/base_spec.rb @@ -11,124 +11,107 @@ describe QA::Factory::Base do subject { Class.new(described_class) } before do - allow(QA::Factory::Product).to receive(:new).with(product_location).and_return(product) - allow(QA::Factory::Product).to receive(:populate!).and_return(product) allow(subject).to receive(:current_url).and_return(product_location) allow(subject).to receive(:new).and_return(factory) + allow(QA::Factory::Product).to receive(:populate!).with(factory, product_location).and_return(product) end end - shared_examples 'API fabrication method' do |fabrication_method_called, actual_fabrication_method = nil| + shared_examples 'fabrication method' do |fabrication_method_called, actual_fabrication_method = nil| let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called } - include_context 'fabrication context' + it 'yields factory before calling factory method' do + allow(subject).to receive(:new).and_return(factory) - it_behaves_like 'fabrication method', fabrication_method_called, actual_fabrication_method + expect(factory).to receive(:something!).ordered + expect(factory).to receive(fabrication_method_used).ordered.and_return(product_location) - it 'instantiates the factory and calls factory method' do - expect(subject).to receive(:new).and_return(factory) + subject.public_send(fabrication_method_called) do |factory| + factory.something! + end + end - subject.public_send(fabrication_method_called) + it 'does not log the factory and build method when VERBOSE=false' do + stub_env('VERBOSE', 'false') + expect(factory).to receive(fabrication_method_used).and_return(product_location) - expect(factory).to have_received(fabrication_method_used) + expect { subject.public_send(fabrication_method_called, 'something') } + .not_to output(/Resource #{factory.class.name} built via/) + .to_stdout end + end - it 'returns fabrication product' do - result = subject.public_send(fabrication_method_called) + describe '.fabricate!' do + context 'when factory does not support fabrication via the API' do + before do + expect(described_class).to receive(:fabricate_via_api!).and_raise(NotImplementedError) + end + + it 'calls .fabricate_via_browser_ui!' do + expect(described_class).to receive(:fabricate_via_browser_ui!) - expect(result).to eq product + described_class.fabricate! + end end - it 'logs the factory and build method when VERBOSE=true' do - stub_env('VERBOSE', 'true') + context 'when factory supports fabrication via the API' do + it 'calls .fabricate_via_browser_ui!' do + expect(described_class).to receive(:fabricate_via_api!) - expect { subject.public_send(fabrication_method_called) } - .to output(/Resource #{factory.class.name} built via do_fabricate_via_api/) - .to_stdout + described_class.fabricate! + end end end - shared_examples 'Browser UI fabrication method' do |fabrication_method_called, actual_fabrication_method = nil| - let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called } - + describe '.fabricate_via_api!' do include_context 'fabrication context' - it_behaves_like 'fabrication method', fabrication_method_called, actual_fabrication_method - - it 'instantiates the factory and calls factory method' do - expect(subject).to receive(:new).and_return(factory) - - subject.public_send(fabrication_method_called, 'something') + it_behaves_like 'fabrication method', :fabricate_via_api! - expect(factory).to have_received(fabrication_method_used).with('something') - end + it 'instantiates the factory, calls factory method returns fabrication product' do + expect(factory).to receive(:fabricate_via_api!).and_return(product_location) - it 'returns fabrication product' do - result = subject.public_send(fabrication_method_called, 'something') + result = subject.fabricate_via_api! - expect(result).to eq product + expect(result).to eq(product) end it 'logs the factory and build method when VERBOSE=true' do stub_env('VERBOSE', 'true') + expect(factory).to receive(:fabricate_via_api!).and_return(product_location) - expect { subject.public_send(fabrication_method_called, 'something') } - .to output(/Resource #{factory.class.name} built via do_fabricate_via_browser_ui/) + expect { subject.fabricate_via_api! } + .to output(/Resource #{factory.class.name} built via api/) .to_stdout end end - shared_examples 'fabrication method' do |fabrication_method_called, actual_fabrication_method = nil| - let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called } - + describe '.fabricate_via_browser_ui!' do include_context 'fabrication context' - it 'yields factory before calling factory method' do - allow(subject).to receive(:new).and_return(factory) - - subject.public_send(fabrication_method_called) do |factory| - factory.something! - end + it_behaves_like 'fabrication method', :fabricate_via_browser_ui!, :fabricate! - expect(factory).to have_received(:something!).ordered - expect(factory).to have_received(fabrication_method_used).ordered - end - - it 'does not log the factory and build method when VERBOSE=false' do - stub_env('VERBOSE', 'false') + it 'instantiates the factory and calls factory method' do + subject.fabricate_via_browser_ui!('something') - expect { subject.public_send(fabrication_method_called, 'something') } - .not_to output(/Resource #{factory.class.name} built via/) - .to_stdout + expect(factory).to have_received(:fabricate!).with('something') end - end - describe '.fabricate!' do - context 'when factory does not support fabrication via the API' do - before do - allow(factory).to receive(:api_support?).and_return(false) - end + it 'returns fabrication product' do + result = subject.fabricate_via_browser_ui!('something') - it_behaves_like 'Browser UI fabrication method', :fabricate! + expect(result).to eq(product) end - context 'when factory supports fabrication via the API' do - before do - allow(factory).to receive(:api_support?).and_return(true) - end + it 'logs the factory and build method when VERBOSE=true' do + stub_env('VERBOSE', 'true') - it_behaves_like 'API fabrication method', :fabricate!, :fabricate_via_api! + expect { subject.fabricate_via_browser_ui!('something') } + .to output(/Resource #{factory.class.name} built via browser_ui/) + .to_stdout end end - describe '.fabricate_via_api!' do - it_behaves_like 'API fabrication method', :fabricate_via_api! - end - - describe '.fabricate_via_browser_ui!' do - it_behaves_like 'Browser UI fabrication method', :fabricate_via_browser_ui!, :fabricate! - end - describe '.dependency' do let(:dependency) { spy('dependency') } @@ -168,8 +151,7 @@ describe QA::Factory::Base do allow(subject).to receive(:new).and_return(instance) allow(subject).to receive(:current_url).and_return(product_location) allow(instance).to receive(:mydep).and_return(nil) - allow(QA::Factory::Product).to receive(:new) - allow(QA::Factory::Product).to receive(:populate!) + expect(QA::Factory::Product).to receive(:populate!) end it 'builds all dependencies first' do @@ -181,79 +163,22 @@ describe QA::Factory::Base do end describe '.product' do - context 'when the product is produced via the browser' 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') } + include_context 'fabrication context' - before do - allow(QA::Factory::Product).to receive(:new).and_return(product) - allow(product).to receive(:page).and_return(page) - allow(subject).to receive(:current_url).and_return(product_location) - allow(subject).to receive(:find_page).and_return(page) + subject do + Class.new(described_class) do + def fabricate! + "any" 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 + product :token end end - context 'when the product is producted via the API' do - subject do - Class.new(described_class) do - def fabricate! - "any" - end - - product :token - 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 - before do - allow(subject).to receive(:new).and_return(factory) - allow(factory).to receive(:class).and_return(subject) - allow(factory).to receive(:api_support?).and_return(true) - allow(factory).to receive(:api_resource).and_return({ token: 'resulting value' }) - allow(QA::Factory::Product).to receive(:new).and_return(product) - end - - it 'populates product after fabrication' do - subject.fabricate! - - expect(product.token).to eq 'resulting value' - end - end + it 'appends new product attribute' do + expect(subject.attributes).to be_one + expect(subject.attributes[0]).to be_a(QA::Factory::Product::Attribute) + expect(subject.attributes[0].name).to eq(:token) end end end diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb index 2f2d5568af7..43b1d93d769 100644 --- a/qa/spec/factory/product_spec.rb +++ b/qa/spec/factory/product_spec.rb @@ -1,51 +1,71 @@ describe QA::Factory::Product do let(:factory) do - QA::Factory::Base.new + Class.new(QA::Factory::Base) do + def foo + 'bar' + end + end.new end let(:product) { spy('product') } let(:product_location) { 'http://product_location' } - subject { described_class.new(product_location) } + subject { described_class.new(factory, product_location) } describe '.populate!' do before do - allow(QA::Factory::Base).to receive(:attributes).and_return(attributes) + expect(factory.class).to receive(:attributes).and_return(attributes) end - context 'when the product is produced via the browser' do + context 'when the product attribute is populated via a block' do let(:attributes) do - { test: QA::Factory::Product::Attribute.new(:test, proc { 'returned' }) } + [QA::Factory::Product::Attribute.new(:test, proc { 'returned' })] end it 'returns a fabrication product and defines factory attributes as its methods' do - expect(described_class).to receive(:new).with(product_location).and_return(product) + result = described_class.populate!(factory, product_location) + + expect(result).to be_a(described_class) + expect(result.test).to eq('returned') + end + end - result = described_class.populate!(factory, product_location) do |instance| - instance.something = 'string' - end + context 'when the product attribute is populated via the api' do + let(:attributes) do + [QA::Factory::Product::Attribute.new(:test)] + end + + it 'returns a fabrication product and defines factory attributes as its methods' do + expect(factory).to receive(:api_resource).and_return({ test: 'returned' }) - expect(result).to be product + result = described_class.populate!(factory, product_location) + + expect(result).to be_a(described_class) expect(result.test).to eq('returned') end end - context 'when the product is produced via the api' do + context 'when the product attribute is populated via a factory attribute' do let(:attributes) do - { test: QA::Factory::Product::Attribute.new(:test) } + [QA::Factory::Product::Attribute.new(:foo)] end it 'returns a fabrication product and defines factory attributes as its methods' do - allow(factory).to receive(:api_resource).and_return({ test: 'returned' }) + result = described_class.populate!(factory, product_location) - expect(described_class).to receive(:new).with(product_location).and_return(product) + expect(result).to be_a(described_class) + expect(result.foo).to eq('bar') + end + end - result = described_class.populate!(factory, product_location) do |instance| - instance.something = 'string' - end + context 'when the product attribute has no value' do + let(:attributes) do + [QA::Factory::Product::Attribute.new(:bar)] + end - expect(result).to be product - expect(result.test).to eq('returned') + it 'returns a fabrication product and defines factory attributes as its methods' do + expect { described_class.populate!(factory, product_location) } + .to raise_error(described_class::NoValueError, "No value was computed for product bar of factory #{factory.class.name}.") end end end @@ -53,8 +73,6 @@ describe QA::Factory::Product do 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' diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index c06e2dba8e7..e298795c6cf 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -1,28 +1,28 @@ describe QA::Runtime::Env do include Support::StubENV - describe '.verbose?' do + shared_examples 'boolean method' do |method, env_key| context 'when there is an env variable set' do it 'returns false when falsey values specified' do - stub_env('VERBOSE', 'false') - expect(described_class.verbose?).to be_falsey + stub_env(env_key, 'false') + expect(described_class.public_send(method)).to be_falsey - stub_env('VERBOSE', 'no') - expect(described_class.verbose?).to be_falsey + stub_env(env_key, 'no') + expect(described_class.public_send(method)).to be_falsey - stub_env('VERBOSE', '0') - expect(described_class.verbose?).to be_falsey + stub_env(env_key, '0') + expect(described_class.public_send(method)).to be_falsey end it 'returns true when anything else specified' do - stub_env('VERBOSE', 'true') - expect(described_class.verbose?).to be_truthy + stub_env(env_key, 'true') + expect(described_class.public_send(method)).to be_truthy - stub_env('VERBOSE', '1') - expect(described_class.verbose?).to be_truthy + stub_env(env_key, '1') + expect(described_class.public_send(method)).to be_truthy - stub_env('VERBOSE', 'anything') - expect(described_class.verbose?).to be_truthy + stub_env(env_key, 'anything') + expect(described_class.public_send(method)).to be_truthy end end @@ -34,37 +34,12 @@ describe QA::Runtime::Env do end end - 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.chrome_headless?).to be_falsey - - stub_env('CHROME_HEADLESS', 'no') - expect(described_class.chrome_headless?).to be_falsey - - stub_env('CHROME_HEADLESS', '0') - expect(described_class.chrome_headless?).to be_falsey - end - - it 'returns true when anything else specified' do - stub_env('CHROME_HEADLESS', 'true') - expect(described_class.chrome_headless?).to be_truthy - - stub_env('CHROME_HEADLESS', '1') - expect(described_class.chrome_headless?).to be_truthy - - stub_env('CHROME_HEADLESS', 'anything') - expect(described_class.chrome_headless?).to be_truthy - end - end + describe '.verbose?' do + it_behaves_like 'boolean method', :verbose?, 'VERBOSE' + end - context 'when there is no env variable set' do - it 'returns the default, true' do - stub_env('CHROME_HEADLESS', nil) - expect(described_class.chrome_headless?).to be_truthy - end - end + describe '.chrome_headless?' do + it_behaves_like 'boolean method', :chrome_headless?, 'CHROME_HEADLESS' end describe '.running_in_ci?' do @@ -132,6 +107,11 @@ describe QA::Runtime::Env do end describe '.forker?' do + before do + stub_env('GITLAB_FORKER_USERNAME', nil) + stub_env('GITLAB_FORKER_PASSWORD', nil) + end + it 'returns false if no forker credentials are defined' do expect(described_class).not_to be_forker end |