diff --git a/features/my_blog.feature b/features/my_blog.feature index 67f0476..de47f4b 100644 --- a/features/my_blog.feature +++ b/features/my_blog.feature @@ -70,32 +70,35 @@ Scenario: Using the blueprint adapter with hooks Then I configure Vigia with the following options: | source_file | my_blog/my_blog.apib | | host | my_blog.host | - Then I configure a "before_group" hook with this block: + Then I configure a "after_group" hook with this block: """ - group_name = described_class.described_object.name - let("#{ described_class.group.name }_name") { group_name } + 'a simple string' """ - Then I configure a "after_group" hook with this block: + Then I configure a "after_context" hook with this block: + """ + 'TODO' + """ + Then I configure a "extend_group" hook with this block: """ group_name = described_class.described_object.name + let("#{ described_class.group.name }_name") { group_name } + it 'has the described object name defined' do expect(group_name).to eql(described_class.described_object.name) end """ - Then I configure a "before_context" hook with this block: + Then I configure a "extend_context" hook with this block: """ let(:full_string_name) do "#{ resource_group.name }#{ resource.name }#{ action.name }#{ transactional_example.name }#{ response.name }" end - """ - Then I configure a "after_context" hook with this block: - """ - it 'has all the properties defined after the example' do + it 'has all the properties defined in the example' do expect(http_client_options).to be_a(Object) expect(expectations).to be_a(OpenStruct) expect(result).to be_a(OpenStruct) end + """ Then I run Vigia And the output should contain the following: @@ -113,9 +116,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 200 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example POST has the described object name defined Example #0 @@ -123,9 +126,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 201 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example Resource: 1.2 Post has the described object name defined GET @@ -135,9 +138,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 200 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example Resource Group: Comments has the described object name defined Resource: Comments @@ -149,9 +152,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 200 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example POST has the described object name defined Example #0 @@ -159,9 +162,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 201 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example Resource: 2.2 Comment has the described object name defined PUT @@ -171,9 +174,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 201 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example DELETE has the described object name defined Example #0 @@ -181,9 +184,9 @@ Scenario: Using the blueprint adapter with hooks Running Response 204 has the described object name defined context default + has all the properties defined in the example has the expected HTTP code includes the expected headers - has all the properties defined after the example """ And the total tests line should equal "48 examples, 0 failures" diff --git a/lib/vigia.rb b/lib/vigia.rb index e7f7bf1..ece4cba 100644 --- a/lib/vigia.rb +++ b/lib/vigia.rb @@ -8,6 +8,7 @@ require_relative 'vigia/adapter' require_relative 'vigia/adapters/blueprint' require_relative 'vigia/config' +require_relative 'vigia/hooks' require_relative 'vigia/http_client/options' require_relative 'vigia/http_client/rest_client' require_relative 'vigia/http_client/requests' @@ -39,3 +40,5 @@ def rspec! end end end + +require_relative 'vigia/sail/examples/default' diff --git a/lib/vigia/adapter.rb b/lib/vigia/adapter.rb index abdbbd2..f98feec 100644 --- a/lib/vigia/adapter.rb +++ b/lib/vigia/adapter.rb @@ -43,8 +43,8 @@ def initialize(adapter, template) def preload instance_exec(&template) - Vigia::Sail::Group.collection = groups - Vigia::Sail::Context.collection = contexts + groups.each { |name, options| Vigia::Sail::Group.register(name, options) } + contexts.each { |name, options| Vigia::Sail::Context.register(name, options) } end def after_initialize(&block) diff --git a/lib/vigia/adapters/blueprint.rb b/lib/vigia/adapters/blueprint.rb index e92c4c1..8bb1976 100644 --- a/lib/vigia/adapters/blueprint.rb +++ b/lib/vigia/adapters/blueprint.rb @@ -39,7 +39,7 @@ class Blueprint < Vigia::Adapter context :default, http_client_options: { - headers: -> { adapter.headers_for(resource, action, transactional_example, response) }, + headers: -> { adapter.headers_for(action, transactional_example, response) }, method: -> { action.method }, uri_template: -> { resource.uri_template }, parameters: -> { adapter.parameters_for(resource, action) }, @@ -47,13 +47,13 @@ class Blueprint < Vigia::Adapter }, expectations: { code: -> { response.name.to_i }, - headers: -> { adapter.headers_for(resource, action, transactional_example, response, include_payload = false) }, + headers: -> { adapter.headers_for(action, transactional_example, response, include_payload = false) }, body: -> { response.body } } end - def headers_for(resource, action, transactional_example, response, include_payload = true) - headers = headers_for_response(resource, response) + def headers_for(action, transactional_example, response, include_payload = true) + headers = headers_for_response(response) headers += headers_for_payload(transactional_example, response) if with_payload?(action) && include_payload compile_headers(headers) end @@ -82,13 +82,17 @@ def compile_headers(headers) end end - def headers_for_response(resource, response) + def headers_for_response(response) collection = [] - collection << [*resource.model.headers.collection] collection << [*response.headers.collection] collection.flatten end +# def resources +# apib.resource_groups.map(&:resources).flatten +# end + + def headers_for_payload(transactional_example, response) payload = get_payload(transactional_example, response) [ *payload.headers.collection ].flatten diff --git a/lib/vigia/config.rb b/lib/vigia/config.rb index 894b443..0a5f6d9 100644 --- a/lib/vigia/config.rb +++ b/lib/vigia/config.rb @@ -1,12 +1,13 @@ module Vigia class Config attr_accessor :source_file, :host, :custom_examples_paths, :custom_examples, :headers, :http_client_class - attr_accessor :adapter, :hooks, :rspec_config_block, :stderr, :stdout + attr_accessor :adapter, :hooks, :rspec_config_block, :stderr, :stdout, :internal_hosts def initialize @host = nil @source_file = nil @rspec_config_block = nil + @internal_hosts = [] @headers = {} @custom_examples_paths = [] @custom_examples = [] @@ -42,6 +43,10 @@ def after_group(&block) store_hook(Vigia::Sail::GroupInstance, :after, block) end + def extend_group(&block) + store_hook(Vigia::Sail::GroupInstance, :extend, block) + end + def before_context(&block) store_hook(Vigia::Sail::Context, :before, block) end @@ -50,6 +55,10 @@ def after_context(&block) store_hook(Vigia::Sail::Context, :after, block) end + def extend_context(&block) + store_hook(Vigia::Sail::Context, :extend, block) + end + def store_hook(rspec_class, filter, block) @hooks << { rspec_class: rspec_class, filter: filter, block: block } end diff --git a/lib/vigia/hooks.rb b/lib/vigia/hooks.rb new file mode 100644 index 0000000..e331597 --- /dev/null +++ b/lib/vigia/hooks.rb @@ -0,0 +1,46 @@ +module Vigia + module Hooks + + def execute_hook(filter_name, rspec_context) + hooks_for_object(filter_name).each do |hook| + rspec_context.instance_exec(&hook) + end + end + + def hooks_for_object(filter_name) + config_hooks(filter_name) + object_hooks(filter_name) + end + + def with_hooks(rspec_context) + instance = self + + rspec_context.before(:context) do + instance.execute_hook(:before, self) + end + + rspec_context.after(:context) do + instance.execute_hook(:after, self) + end + + instance.execute_hook(:extend, rspec_context) + + yield + end + + private + + def object_hooks(filter_name) + return [] unless respond_to?(:options) + option_name = "#{ filter_name }_#{ self.class.name.split('::').last.downcase }".to_sym + [ *options[option_name] ].compact + + end + + def config_hooks(filter_name) + Vigia.config.hooks.each_with_object([]) do |hook, collection| + next unless self.is_a?(hook[:rspec_class]) and filter_name == hook[:filter] + collection << hook[:block] + end + end + end +end diff --git a/lib/vigia/sail/example.rb b/lib/vigia/sail/example.rb index c03e2fb..24afe36 100644 --- a/lib/vigia/sail/example.rb +++ b/lib/vigia/sail/example.rb @@ -22,9 +22,10 @@ def run instance = self rspec.it instance do skip if instance.skip?(self) || (respond_to?(:skip?) and send(:skip?)) - skip('__vigia__') if instance.disabled?(self) || (respond_to?(:disabled?) and send(:disabled?)) - instance_exec(&instance.expectation) + unless instance.disabled?(self) || (respond_to?(:disabled?) and send(:disabled?)) + instance_exec(&instance.expectation) + end end end @@ -50,16 +51,3 @@ def disabled?(in_context) end end end - - -Vigia::Sail::Example.register( - :code_match, - description: 'has the expected HTTP code', - expectation: -> { expect(result.code).to be(expectations.code) } -) - -Vigia::Sail::Example.register( - :include_headers, - description: 'includes the expected headers', - expectation: -> { expect(result[:headers]).to include(expectations[:headers]) } -) diff --git a/lib/vigia/sail/examples/default.rb b/lib/vigia/sail/examples/default.rb new file mode 100644 index 0000000..641aa1c --- /dev/null +++ b/lib/vigia/sail/examples/default.rb @@ -0,0 +1,11 @@ +Vigia::Sail::Example.register( + :code_match, + description: 'has the expected HTTP code', + expectation: -> { expect(result.code).to be(expectations.code) } +) + +Vigia::Sail::Example.register( + :include_headers, + description: 'includes the expected headers', + expectation: -> { expect(result[:headers]).to include(expectations[:headers]) } +) diff --git a/lib/vigia/sail/rspec_object.rb b/lib/vigia/sail/rspec_object.rb index 94a5a5b..4438850 100644 --- a/lib/vigia/sail/rspec_object.rb +++ b/lib/vigia/sail/rspec_object.rb @@ -2,7 +2,12 @@ module Vigia module Sail class RSpecObject class << self - attr_accessor :collection + attr_reader :collection + + def register(name, options) + @collection = {} if collection.nil? + @collection.merge!(name => options) + end def setup_and_run(name, rspec) name, options = collection.select{ |k,v| k == name }.first @@ -11,6 +16,8 @@ def setup_and_run(name, rspec) end end + include Vigia::Hooks + attr_reader :name, :options, :rspec def initialize(name, options, rspec) @@ -20,22 +27,6 @@ def initialize(name, options, rspec) end - def execute_hook(filter_name, rspec_context) - hooks_for_object(filter_name).each do |hook| - rspec_context.instance_exec(&hook) - end - end - - def hooks_for_object(filter_name) - config_hooks(filter_name) + object_hooks(filter_name) - end - - def with_hooks(rspec_context) - execute_hook(:before, rspec_context) - yield - execute_hook(:after, rspec_context) - end - def contextual_object(option_name: nil, object: nil, context: nil) context ||= rspec.described_class object ||= options[option_name] @@ -55,19 +46,6 @@ def must_be_a_block(block, error_message) return block if block.respond_to?(:call) raise error_message end - - def object_hooks(filter_name) - option_name = "#{ filter_name }_#{ self.class.name.split('::').last.downcase }".to_sym - [ *options[option_name] ].compact - - end - - def config_hooks(filter_name) - Vigia.config.hooks.each_with_object([]) do |hook, collection| - next unless self.is_a?(hook[:rspec_class]) and filter_name == hook[:filter] - collection << hook[:block] - end - end end end end diff --git a/lib/vigia/url.rb b/lib/vigia/url.rb index d500dd2..c74ed00 100644 --- a/lib/vigia/url.rb +++ b/lib/vigia/url.rb @@ -1,7 +1,19 @@ require 'uri' +require 'addressable/template' +require 'addressable/uri' + module Vigia class Url + class << self + def template_defines_url?(template, url) + url_object = Addressable::URI.parse(url) + template_object = Addressable::Template.new(template) + url_parameters = template_object.extract(url_object) || {} + # recreate url and see if matches + template_object.expand(url_parameters) == url_object + end + end attr_reader :uri_template diff --git a/lib/vigia/version.rb b/lib/vigia/version.rb index 7352547..b24913e 100644 --- a/lib/vigia/version.rb +++ b/lib/vigia/version.rb @@ -1,5 +1,5 @@ # encoding: utf-8 module Vigia - VERSION = "0.0.9" + VERSION = "0.1.1" end diff --git a/spec/lib/config_spec.rb b/spec/lib/config_spec.rb index 9db78cd..0360c8b 100644 --- a/spec/lib/config_spec.rb +++ b/spec/lib/config_spec.rb @@ -30,6 +30,9 @@ it 'has a stderr with the default value' do expect(subject.stderr).to eql($stderr) end + it 'has an internal_hosts method with the default value' do + expect(subject.internal_hosts).to eql([]) + end end end @@ -80,7 +83,7 @@ subject.send("#{ filter }_#{ rspec_object_name }", &block) end - [ :after, :before ].each do |filter| + [ :after, :extend, :before ].each do |filter| context "when calling #{ filter }_#{ rspec_object_name }" do let(:filter) { 'before' } it 'calls the store hook method with the right parameters' do diff --git a/spec/lib/hooks_spec.rb b/spec/lib/hooks_spec.rb new file mode 100644 index 0000000..eb18863 --- /dev/null +++ b/spec/lib/hooks_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +class Hookeable + include Vigia::Hooks +end + +describe Hookeable do + let(:config) do + instance_double( + Vigia::Config, + hooks: config_hooks + ) + end + + before do + allow(subject).to receive(:options).and_return(options) + allow(Vigia).to receive(:config).and_return(config) + end + + context 'object hooks' do + let(:options) { { before_hookeable: 'hook in options' } } + let(:config_hooks) { [] } + + it 'returns the hook in options' do + expect(subject.hooks_for_object(:before)).to include 'hook in options' + end + end + + context 'config hooks' do + let(:options) { {} } + let(:config_hooks) { [{ rspec_class: Hookeable, filter: :before, block: 'hook in config' }] } + + it 'returns the hook in config' do + expect(subject.hooks_for_object(:before)).to include 'hook in config' + end + end +end diff --git a/spec/lib/url_spec.rb b/spec/lib/url_spec.rb index 35cf488..6598b1a 100644 --- a/spec/lib/url_spec.rb +++ b/spec/lib/url_spec.rb @@ -25,6 +25,16 @@ described_class.new uri_template end + describe '::template_defines_url?' do + it 'returns true if the url matches the template' do + expect(described_class.template_defines_url?('/a_resource/{id}', '/a_resource/test')).to be true + end + + it 'returns false if the url does not match the template' do + expect(described_class.template_defines_url?('/a_resource/{id}', '/a_resourcetest')).to be false + end + end + describe '#expand' do before do allow(subject).to receive(:host).and_return('http://this-example.com') diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6088935..69d3511 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,7 @@ # Cucumber + Rspec should cover 100% of the gem SimpleCov.minimum_coverage 100 -require 'vigia' require 'pry' +require 'vigia' Dir["./spec/support/**/*.rb"].sort.each { |f| require f }