Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2018-10-17 21:08:20 +0300
committerDouglas Barbosa Alexandre <dbalexandre@gmail.com>2018-10-17 21:08:20 +0300
commitb6f2f738c73b1dfe66be61e1b37ca21fa698cf1c (patch)
treeb6b520d12c2051a6a1cdaa5741f48f6583e0cce8 /qa/spec/factory
parentab9cf561c230f1b6ec630215a9a9def53e14d764 (diff)
First iteration to allow creating QA resources using the API
Diffstat (limited to 'qa/spec/factory')
-rw-r--r--qa/spec/factory/api_fabricator_spec.rb161
-rw-r--r--qa/spec/factory/base_spec.rb149
-rw-r--r--qa/spec/factory/dependency_spec.rb23
-rw-r--r--qa/spec/factory/product_spec.rb74
4 files changed, 336 insertions, 71 deletions
diff --git a/qa/spec/factory/api_fabricator_spec.rb b/qa/spec/factory/api_fabricator_spec.rb
new file mode 100644
index 00000000000..e5fbc064911
--- /dev/null
+++ b/qa/spec/factory/api_fabricator_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+describe QA::Factory::ApiFabricator do
+ let(:factory_without_api_support) do
+ Class.new do
+ def self.name
+ 'FooBarFactory'
+ end
+ end
+ end
+
+ let(:factory_with_api_support) do
+ Class.new do
+ def self.name
+ 'FooBarFactory'
+ end
+
+ def api_get_path
+ '/foo'
+ end
+
+ def api_post_path
+ '/bar'
+ end
+
+ def api_post_body
+ { name: 'John Doe' }
+ end
+ end
+ end
+
+ before do
+ allow(subject).to receive(:current_url).and_return('')
+ end
+
+ subject { factory.tap { |f| f.include(described_class) }.new }
+
+ describe '#api_support?' do
+ let(:api_client) { spy('Runtime::API::Client') }
+ let(:api_client_instance) { double('API Client') }
+
+ context 'when factory does not support fabrication via the API' do
+ let(:factory) { factory_without_api_support }
+
+ it 'returns false' do
+ expect(subject).not_to be_api_support
+ end
+ end
+
+ context 'when factory supports fabrication via the API' do
+ let(:factory) { factory_with_api_support }
+
+ it 'returns false' do
+ expect(subject).to be_api_support
+ end
+ end
+ end
+
+ describe '#fabricate_via_api!' do
+ let(:api_client) { spy('Runtime::API::Client') }
+ let(:api_client_instance) { double('API Client') }
+
+ before do
+ stub_const('QA::Runtime::API::Client', api_client)
+
+ allow(api_client).to receive(:new).and_return(api_client_instance)
+ allow(api_client_instance).to receive(:personal_access_token).and_return('foo')
+ end
+
+ context 'when factory does not support fabrication via the API' do
+ let(:factory) { factory_without_api_support }
+
+ it 'raises a NotImplementedError exception' do
+ expect { subject.fabricate_via_api! }.to raise_error(NotImplementedError, "Factory FooBarFactory does not support fabrication via the API!")
+ end
+ end
+
+ context 'when factory supports fabrication via the API' do
+ let(:factory) { factory_with_api_support }
+ 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_post) { double('Raw POST response', code: 201, body: resource.to_json) }
+
+ before do
+ stub_const('QA::Runtime::API::Request', api_request)
+
+ allow(api_request).to receive(:new).and_return(double(url: resource_web_url))
+ end
+
+ context 'when creating a resource' do
+ 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_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)
+ end
+
+ it 'populates api_resource with the resource' do
+ subject.fabricate_via_api!
+
+ expect(subject.api_resource).to eq(resource)
+ end
+
+ context 'when the POST fails' do
+ let(:post_response) { { error: "Name already taken." } }
+ 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_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 `#{raw_post}`.")
+ expect(subject.api_resource).to be_nil
+ end
+ end
+ end
+
+ context '#transform_api_resource' do
+ let(:factory) do
+ Class.new do
+ def self.name
+ 'FooBarFactory'
+ end
+
+ def api_get_path
+ '/foo'
+ end
+
+ def api_post_path
+ '/bar'
+ end
+
+ def api_post_body
+ { name: 'John Doe' }
+ end
+
+ def transform_api_resource(resource)
+ resource[:new] = 'foobar'
+ resource
+ end
+ end
+ end
+
+ let(:resource) { { existing: 'foo', web_url: resource_web_url } }
+ 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!
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb
index 04e04886699..184802a7903 100644
--- a/qa/spec/factory/base_spec.rb
+++ b/qa/spec/factory/base_spec.rb
@@ -1,40 +1,117 @@
+# frozen_string_literal: true
+
describe QA::Factory::Base do
+ include Support::StubENV
+
let(:factory) { spy('factory') }
let(:product) { spy('product') }
+ let(:product_location) { 'http://product_location' }
- describe '.fabricate!' do
- subject { Class.new(described_class) }
+ shared_context 'fabrication context' do
+ subject do
+ Class.new(described_class) do
+ def self.name
+ 'MyFactory'
+ end
+ end
+ end
before do
- allow(QA::Factory::Product).to receive(:new).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
- it 'instantiates the factory and calls factory method' do
- expect(subject).to receive(:new).and_return(factory)
+ shared_examples 'fabrication method' do |fabrication_method_called, actual_fabrication_method = nil|
+ let(:fabrication_method_used) { actual_fabrication_method || fabrication_method_called }
- subject.fabricate!('something')
+ it 'yields factory before calling factory method' do
+ expect(factory).to receive(:something!).ordered
+ expect(factory).to receive(fabrication_method_used).ordered.and_return(product_location)
- expect(factory).to have_received(:fabricate!).with('something')
+ subject.public_send(fabrication_method_called, factory: factory) do |factory|
+ factory.something!
+ end
end
- it 'returns fabrication product' do
- allow(subject).to receive(:new).and_return(factory)
+ 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)
- result = subject.fabricate!('something')
+ expect { subject.public_send(fabrication_method_called, 'something', factory: factory) }
+ .not_to output.to_stdout
+ end
+ end
+
+ 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
- expect(result).to eq product
+ it 'calls .fabricate_via_browser_ui!' do
+ expect(described_class).to receive(:fabricate_via_browser_ui!)
+
+ described_class.fabricate!
+ end
end
- it 'yields factory before calling factory method' do
- allow(subject).to receive(:new).and_return(factory)
+ context 'when factory supports fabrication via the API' do
+ it 'calls .fabricate_via_browser_ui!' do
+ expect(described_class).to receive(:fabricate_via_api!)
- subject.fabricate! do |factory|
- factory.something!
+ described_class.fabricate!
end
+ end
+ end
+
+ describe '.fabricate_via_api!' do
+ include_context 'fabrication context'
+
+ it_behaves_like 'fabrication method', :fabricate_via_api!
+
+ it 'instantiates the factory, calls factory method returns fabrication product' do
+ expect(factory).to receive(:fabricate_via_api!).and_return(product_location)
- expect(factory).to have_received(:something!).ordered
- expect(factory).to have_received(:fabricate!).ordered
+ result = subject.fabricate_via_api!(factory: factory, parents: [])
+
+ 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.fabricate_via_api!(factory: factory, parents: []) }
+ .to output(/==> Built a MyFactory via api with args \[\] in [\d\w\.\-]+/)
+ .to_stdout
+ end
+ end
+
+ describe '.fabricate_via_browser_ui!' do
+ include_context 'fabrication context'
+
+ it_behaves_like 'fabrication method', :fabricate_via_browser_ui!, :fabricate!
+
+ it 'instantiates the factory and calls factory method' do
+ subject.fabricate_via_browser_ui!('something', factory: factory, parents: [])
+
+ expect(factory).to have_received(:fabricate!).with('something')
+ end
+
+ it 'returns fabrication product' do
+ result = subject.fabricate_via_browser_ui!('something', factory: factory, parents: [])
+
+ expect(result).to eq(product)
+ end
+
+ it 'logs the factory and build method when VERBOSE=true' do
+ stub_env('VERBOSE', 'true')
+
+ expect { subject.fabricate_via_browser_ui!('something', factory: factory, parents: []) }
+ .to output(/==> Built a MyFactory via browser_ui with args \["something"\] in [\d\w\.\-]+/)
+ .to_stdout
end
end
@@ -75,9 +152,9 @@ describe QA::Factory::Base do
stub_const('Some::MyDependency', dependency)
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
@@ -89,44 +166,22 @@ describe QA::Factory::Base do
end
describe '.product' do
+ include_context 'fabrication context'
+
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
+ 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
- let(:page) { spy('page') }
-
- before do
- allow(factory).to receive(:class).and_return(subject)
- allow(QA::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
+ 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/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb
index 8aaa6665a18..657beddffb1 100644
--- a/qa/spec/factory/dependency_spec.rb
+++ b/qa/spec/factory/dependency_spec.rb
@@ -4,11 +4,11 @@ describe QA::Factory::Dependency do
let(:block) { spy('block') }
let(:signature) do
- double('signature', factory: dependency, block: block)
+ double('signature', name: :mydep, factory: dependency, block: block)
end
subject do
- described_class.new(:mydep, factory, signature)
+ described_class.new(factory, signature)
end
describe '#overridden?' do
@@ -55,16 +55,23 @@ describe QA::Factory::Dependency do
expect(factory).to have_received(:mydep=).with(dependency)
end
- context 'when receives a caller factory as block argument' do
- let(:dependency) { QA::Factory::Base }
+ it 'calls given block with dependency factory and caller factory' do
+ expect(dependency).to receive(:fabricate!).and_yield(dependency)
- it 'calls given block with dependency factory and caller factory' do
- allow_any_instance_of(QA::Factory::Base).to receive(:fabricate!).and_return(factory)
- allow(QA::Factory::Product).to receive(:populate!).and_return(spy('any'))
+ subject.build!
+
+ expect(block).to have_received(:call).with(dependency, factory)
+ end
+
+ context 'with no block given' do
+ let(:signature) do
+ double('signature', name: :mydep, factory: dependency, block: nil)
+ end
+ it 'does not error' do
subject.build!
- expect(block).to have_received(:call).with(an_instance_of(QA::Factory::Base), factory)
+ expect(dependency).to have_received(:fabricate!)
end
end
end
diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb
index f245aabbf43..43b1d93d769 100644
--- a/qa/spec/factory/product_spec.rb
+++ b/qa/spec/factory/product_spec.rb
@@ -1,36 +1,78 @@
describe QA::Factory::Product do
let(:factory) do
- QA::Factory::Base.new
- end
-
- let(:attributes) do
- { test: QA::Factory::Product::Attribute.new(:test, proc { 'returned' }) }
+ Class.new(QA::Factory::Base) do
+ def foo
+ 'bar'
+ end
+ end.new
end
let(:product) { spy('product') }
+ let(:product_location) { 'http://product_location' }
- before do
- allow(QA::Factory::Base).to receive(:attributes).and_return(attributes)
- end
+ subject { described_class.new(factory, product_location) }
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)
+ before do
+ expect(factory.class).to receive(:attributes).and_return(attributes)
+ end
+
+ context 'when the product attribute is populated via a block' do
+ let(:attributes) do
+ [QA::Factory::Product::Attribute.new(:test, proc { 'returned' })]
+ end
+
+ it 'returns a fabrication product and defines factory attributes as its methods' do
+ 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 attribute is populated via the api' do
+ let(:attributes) do
+ [QA::Factory::Product::Attribute.new(:test)]
+ end
- result = described_class.populate!(factory) do |instance|
- instance.something = 'string'
+ it 'returns a fabrication product and defines factory attributes as its methods' do
+ expect(factory).to receive(:api_resource).and_return({ test: 'returned' })
+
+ result = described_class.populate!(factory, product_location)
+
+ expect(result).to be_a(described_class)
+ expect(result.test).to eq('returned')
end
+ end
- expect(result).to be product
- expect(result.test).to eq('returned')
+ context 'when the product attribute is populated via a factory attribute' do
+ let(:attributes) do
+ [QA::Factory::Product::Attribute.new(:foo)]
+ end
+
+ it 'returns a fabrication product and defines factory attributes as its methods' do
+ result = described_class.populate!(factory, product_location)
+
+ expect(result).to be_a(described_class)
+ expect(result.foo).to eq('bar')
+ end
+ end
+
+ context 'when the product attribute has no value' do
+ let(:attributes) do
+ [QA::Factory::Product::Attribute.new(:bar)]
+ end
+
+ 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
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'