diff --git a/README.md b/README.md index 952a98d..e7ae0eb 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ json_spec defines five new RSpec matchers: * `be_json_eql` * `include_json` * `have_json_path` +* `have_json_keys` * `have_json_type` * `have_json_size` diff --git a/features/keys.feature b/features/keys.feature new file mode 100644 index 0000000..bf980c2 --- /dev/null +++ b/features/keys.feature @@ -0,0 +1,39 @@ +Feature: Paths + Background: + Given the JSON is: + """ + { + "a": 1, + "b": 2, + "c": 3, + "z" : { + "d": 4, + "e": 5 + }, + "strange keys: '\_(\")_/', FTW!": true + } + """ + + Scenario: Base paths + When I get the JSON + Then the JSON should have keys "a", "b", "c" + And the JSON should not have keys "d", "e" + And the JSON at "z" should have keys "d", "e" + And the JSON at "z" should not have keys "m", "z" + And the JSON should have keys "strange keys: '\_(\")_/', FTW!", "a", "b" + + Scenario: Table format + When I get the JSON + Then the JSON should have the following keys: + | a | + | b | + | c | + And the JSON should not have the following keys: + | d | + | e | + And the JSON at "z" should have the following keys: + | d | + | e | + And the JSON at "z" should not have the following keys: + | m | + | z | diff --git a/lib/json_spec/cucumber.rb b/lib/json_spec/cucumber.rb index f4b66a9..03c6000 100644 --- a/lib/json_spec/cucumber.rb +++ b/lib/json_spec/cucumber.rb @@ -70,6 +70,16 @@ end end +Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? have the following keys:$/ do |base, negative, table| + match = have_json_keys(table.raw) + match = match.at_path(base) if base + if negative + last_json.should_not match + else + last_json.should match + end +end + Then /^the (?:JSON|json)(?: response)? should( not)? have "(.*)"$/ do |negative, path| if negative last_json.should_not have_json_path(path) @@ -78,6 +88,17 @@ end end +Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? have keys (.*)$/ do |base, negative, keys| + keys = eval("[#{keys}]") + match = have_json_keys(keys) + match = match.at_path(base) if base + if negative + last_json.should_not match + else + last_json.should match + end +end + Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be an? (.*)$/ do |path, negative, type| if negative last_json.should_not have_json_type(type).at_path(path) diff --git a/lib/json_spec/matchers.rb b/lib/json_spec/matchers.rb index cf58e89..26b1a97 100644 --- a/lib/json_spec/matchers.rb +++ b/lib/json_spec/matchers.rb @@ -3,6 +3,7 @@ require "json_spec/matchers/have_json_path" require "json_spec/matchers/have_json_type" require "json_spec/matchers/have_json_size" +require "json_spec/matchers/have_json_keys" module JsonSpec module Matchers @@ -25,6 +26,10 @@ def have_json_type(type) def have_json_size(size) JsonSpec::Matchers::HaveJsonSize.new(size) end + + def have_json_keys(*keys) + JsonSpec::Matchers::HaveJsonKeys.new(keys.flatten) + end end end diff --git a/lib/json_spec/matchers/have_json_keys.rb b/lib/json_spec/matchers/have_json_keys.rb new file mode 100644 index 0000000..4494268 --- /dev/null +++ b/lib/json_spec/matchers/have_json_keys.rb @@ -0,0 +1,35 @@ +module JsonSpec + module Matchers + class HaveJsonKeys + include JsonSpec::Helpers + include JsonSpec::Messages + + def initialize(keys) + @keys = keys + end + + def matches?(json) + @data = parse_json(json, @path) + return false unless @data.is_a?(Hash) + @keys.all?{|key| @data.has_key?(key.to_s)} + end + + def at_path(path) + @path = path + self + end + + def failure_message_for_should + message_with_path("Expected JSON to contain all of the following keys #{@keys.join(", ")}") + end + + def failure_message_for_should_not + message_with_path("Expected JSON to not contain any of the following keys #{@keys.join(", ")}") + end + + def description + message_with_path(%(have JSON keys "#{@keys.join(", ")}")) + end + end + end +end diff --git a/spec/json_spec/matchers/have_json_keys_spec.rb b/spec/json_spec/matchers/have_json_keys_spec.rb new file mode 100644 index 0000000..500eef1 --- /dev/null +++ b/spec/json_spec/matchers/have_json_keys_spec.rb @@ -0,0 +1,43 @@ +require "spec_helper" + +describe JsonSpec::Matchers::HaveJsonKeys do + it "fails for non-hashes" do + %([1,2,3]).should_not have_json_keys + %(1).should_not have_json_keys + %("test").should_not have_json_keys + end + + it "fails for bigger set of keys" do + %({"a": "1", "b": "2"}).should_not have_json_keys(:a, :b, :c) + end + + it "matches for equal set of keys" do + [:a, :b, :c].permutation.each do |keys| + %({"a": "1", "b": "2", "c": "3"}).should have_json_keys(*keys) + end + end + + it "matches for smaller set of keys" do + %({"a": "1", "b": "2", "c": "3"}).should have_json_keys(:a, :b) + end + + it "matches for empty set of keys" do + %({"a": "1", "b": "2", "c": "3"}).should have_json_keys + end + + it "accepts array of symbol keys" do + %({"a": "1", "b": "2"}).should have_json_keys([:a, :b]) + end + + it "accepts array of string keys" do + %({"a": "1", "b": "2"}).should have_json_keys(['a', 'b']) + end + + it "accepts symbol keys" do + %({"a": "1", "b": "2"}).should have_json_keys(:a, :b) + end + + it "accepts string keys" do + %({"a": "1", "b": "2"}).should have_json_keys('a', 'b') + end +end