lib/rspec-api/dsl/request/body.rb in rspec-api-0.2.0 vs lib/rspec-api/dsl/request/body.rb in rspec-api-0.4.0

- old
+ new

@@ -1,89 +1,85 @@ +require 'active_support' require 'rspec-api/matchers' module DSL module Request extend ActiveSupport::Concern module ClassMethods - def should_match_body_expectations(status_code, &block) - should_return_a_jsonp rspec_api[:callback] if rspec_api[:callback] - should_return_a_json rspec_api[:array] if success? status_code - should_include_attributes rspec_api.fetch(:attributes, {}) if success? status_code - if rspec_api[:array] - should_include_fixture_data unless rspec_api[:page] - should_be_sorted_by(rspec_api[:sort]) if rspec_api[:sort] - should_be_filtered_by(rspec_api[:filter]) if rspec_api[:filter] + # Creates an example group for expectations on the response body of the + # last API request and runs it to verify that it matches best practices: + # * if response is succesful and has a body + # - the body should be a JSON-marshalled Array or Hash + # - if request has a callback parameter, the body should be JSONP + # - if request has a sort parameter, the body should be sorted + # - if request has a filter parameter, the body should be filtered + # - if custom expectations are passed, they should pass + # - if some attributes are expected, the body should include them + def should_respond_with_expected_body(options = {}) + context 'responds with a body that' do + it { should_be_valid_json options[:type] } + it { should_be_wrapped_by options[:callbacks] } + it { should_be_sorted_by options[:sorts] } + it { should_be_filtered_by options[:filters] } + it { should_have_attributes options[:attributes] } end - should_satisfy_expectations_in &block if block_given? end + end - private + def should_be_valid_json(type) + expect(response).to be_valid_json_if response_is_successful?, type + end - def should_return_a_jsonp(callback_options) - it { - if callback_options[:value] == request_params[callback_options[:name].to_s] - expect(response_body).to be_a_jsonp(callback_options[:value]) - else - expect(response_body).to be_a_jsonp(nil) - end - } - end + # If the request had a 'callback' query parameter, then the body should be + # JSONP, otherwise it should not. For instance if the request was + # `GET /?method=alert` and the request `accepts_callback :method`, then + # the body must be a JSON wrapped in the alert(...) callback + # The +callback+ param says how the request might have been made, e.g. + # name: 'method', value: 'alert'... however to make sure that it was + # really made, we need to check that request_params['method'] is present + # and that is actually 'alert' + def should_be_wrapped_by(callback_params_sets) + callback_params = response_is_successful? && get_request_param_for_list(callback_params_sets) + value = callback_params[:value] if callback_params + expect(response).to be_a_jsonp_if callback_params, value + end - def should_return_a_json(is_array) - it { expect(response_body).to be_a_json(is_array ? Array : Hash) } - end + def should_be_sorted_by(sort_params_sets) + sort_params = response_is_successful? && get_request_param_for_list(sort_params_sets) + options = sort_params ? sort_params.slice(:by, :verse) : {} + expect(response).to be_sorted_if sort_params, options + end - def should_include_fixture_data - it { expect(response_body).to include_fixture_data } + def should_be_filtered_by(filter_params_sets) + # TODO: The following is just so the condition does not match if it's nil + # but this should be fixed in get_request_param_for_list + if filter_params_sets + filter_params_sets = filter_params_sets.dup + filter_params_sets.each{|x| x[:value] = request_params.fetch(x[:name].to_s, :something_nil)} end + filter_params = response_is_successful? && get_request_param_for_list(filter_params_sets) + value = filter_params[:value] if filter_params + options = filter_params ? filter_params.slice(:by, :comparing_with) : {} + expect(response).to be_filtered_if filter_params, value, options + end - def should_be_sorted_by(sort_options) - it { - if sort_options[:parameter].to_s == request_params['sort'] - expect(response_body).to be_sorted_by(sort_options[:attribute], verse: :asc) - elsif "-#{sort_options[:parameter].to_s}" == request_params['sort'] - expect(response_body).to be_sorted_by(sort_options[:attribute], verse: :desc) - else - expect(response_body).to be_sorted_by(nil) - end - } - end + def should_have_attributes(attributes) + expect(response).to have_attributes_if response_is_successful?, attributes + end - def should_be_filtered_by(filter_options) - it { - if json_value = request_params[filter_options[:name].to_s] - expect(response_body).to be_filtered_by(json_value, filter_options) - else - expect(response_body).to be_filtered_by(nil) - end - } - end - - def should_satisfy_expectations_in(&block) - it { instance_exec(response_body, @request_params, &block) } - end - - ## Attributes... might clean them up - def should_include_attributes(attributes, ancestors = [], can_be_nil=false) - attributes.each do |name, options = {}| - should_match_attributes name, options, ancestors, can_be_nil - should_include_nested_attributes name, options, ancestors + def get_request_param_for_list(params_sets) + (params_sets || []).find do |params| + conditions = [] + conditions << (request_params[params[:name].to_s] == params[:value]) + params.fetch(:extra_fields, {}).each do |name, value| + conditions << (request_params[name.to_s] == value) end + conditions.all? end + end - def should_match_attributes(name, options, ancestors, can_be_nil) - it { - parent = ancestors.inject(response_body) do |chain, ancestor| - Array.wrap(chain).map{|item| item[ancestor.to_s]}.flatten - end - expect(parent).to have_attribute(name, options.merge(parent_can_be_nil: can_be_nil, parent_can_be_empty: true)) - } - end - - def should_include_nested_attributes(name, options, ancestors) - attributes = options.fetch :attributes, {} - should_include_attributes attributes, ancestors + [name], options[:can_be_nil] - end + def response_is_successful? + response.status < 400 && !Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(response.status) end end end \ No newline at end of file