json_spec 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.travis.yml +2 -4
  2. data/Gemfile +1 -1
  3. data/{LICENSE.md → LICENSE} +0 -0
  4. data/README.md +221 -210
  5. data/features/files.feature +89 -0
  6. data/features/support/env.rb +3 -0
  7. data/features/types.feature +7 -1
  8. data/json_spec.gemspec +16 -20
  9. data/lib/json_spec/configuration.rb +10 -2
  10. data/lib/json_spec/cucumber.rb +18 -3
  11. data/lib/json_spec/errors.rb +22 -1
  12. data/lib/json_spec/helpers.rb +17 -2
  13. data/lib/json_spec/matchers/be_json_eql.rb +63 -0
  14. data/lib/json_spec/matchers/have_json_path.rb +30 -0
  15. data/lib/json_spec/matchers/have_json_size.rb +35 -0
  16. data/lib/json_spec/matchers/have_json_type.rb +49 -0
  17. data/lib/json_spec/matchers/include_json.rb +57 -0
  18. data/lib/json_spec/matchers.rb +9 -218
  19. data/lib/json_spec/memory.rb +2 -3
  20. data/lib/json_spec/messages.rb +8 -0
  21. data/lib/json_spec.rb +3 -1
  22. data/spec/json_spec/configuration_spec.rb +10 -0
  23. data/spec/json_spec/helpers_spec.rb +33 -1
  24. data/spec/json_spec/matchers/be_json_eql_spec.rb +109 -0
  25. data/spec/json_spec/matchers/have_json_path_spec.rb +29 -0
  26. data/spec/json_spec/matchers/have_json_size_spec.rb +47 -0
  27. data/spec/json_spec/matchers/have_json_type_spec.rb +89 -0
  28. data/spec/json_spec/matchers/include_json_spec.rb +76 -0
  29. data/spec/json_spec/matchers_spec.rb +43 -287
  30. data/spec/json_spec/memory_spec.rb +8 -3
  31. data/spec/spec_helper.rb +4 -0
  32. data/spec/support/files/one.json +1 -0
  33. data/spec/support/files/project/one.json +1 -0
  34. data/spec/support/files/project/two.json +18 -0
  35. data/spec/support/files/project/version/one.json +1 -0
  36. data/spec/support/files/project/version/two.json +3 -0
  37. data/spec/support/files/two.json +24 -0
  38. metadata +49 -20
  39. data/lib/json_spec/version.rb +0 -3
data/json_spec.gemspec CHANGED
@@ -1,26 +1,22 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "json_spec/version"
1
+ # encoding: utf-8
4
2
 
5
- Gem::Specification.new do |s|
6
- s.name = "json_spec"
7
- s.version = JsonSpec::VERSION
8
- s.authors = ["Steve Richert"]
9
- s.email = ["steve.richert@gmail.com"]
10
- s.homepage = "https://github.com/collectiveidea/json_spec"
11
- s.summary = "Easily handle JSON in RSpec and Cucumber"
12
- s.description = "Easily handle JSON in RSpec and Cucumber"
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "json_spec"
5
+ gem.version = "1.0.0"
13
6
 
14
- s.rubyforge_project = "json_spec"
7
+ gem.authors = ["Steve Richert"]
8
+ gem.email = ["steve.richert@gmail.com"]
9
+ gem.summary = "Easily handle JSON in RSpec and Cucumber"
10
+ gem.description = "Easily handle JSON in RSpec and Cucumber"
11
+ gem.homepage = "https://github.com/collectiveidea/json_spec"
15
12
 
16
- s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f) }
19
- s.require_paths = ["lib"]
13
+ gem.add_dependency "multi_json", "~> 1.0"
14
+ gem.add_dependency "rspec", "~> 2.0"
20
15
 
21
- s.add_dependency "multi_json", "~> 1.0"
22
- s.add_dependency "rspec", "~> 2.0"
16
+ gem.add_development_dependency "cucumber", "~> 1.1", ">= 1.1.1"
17
+ gem.add_development_dependency "rake", "~> 0.9"
23
18
 
24
- s.add_development_dependency "rake", "~> 0.9"
25
- s.add_development_dependency "cucumber", "~> 1.1", ">= 1.1.1"
19
+ gem.files = `git ls-files`.split("\n")
20
+ gem.test_files = `git ls-files -- {spec,features}/*`.split("\n")
21
+ gem.require_paths = ["lib"]
26
22
  end
@@ -13,15 +13,23 @@ module JsonSpec
13
13
  end
14
14
 
15
15
  def excluded_keys=(keys)
16
- @excluded_keys = keys.map{|k| k.to_s }.uniq
16
+ @excluded_keys = keys.map(&:to_s).uniq
17
17
  end
18
18
 
19
19
  def exclude_keys(*keys)
20
20
  self.excluded_keys = keys
21
21
  end
22
22
 
23
+ def directory
24
+ @directory
25
+ end
26
+
27
+ def directory=(directory)
28
+ @directory = directory
29
+ end
30
+
23
31
  def reset
24
- instance_variables.each{|iv| remove_instance_variable(iv) }
32
+ instance_variables.each{|ivar| remove_instance_variable(ivar) }
25
33
  end
26
34
  end
27
35
  end
@@ -18,6 +18,14 @@ Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be:$/ do |pa
18
18
  end
19
19
  end
20
20
 
21
+ Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be file "(.+)"$/ do |path, negative, file_path|
22
+ if negative
23
+ last_json.should_not be_json_eql.to_file(file_path).at_path(path)
24
+ else
25
+ last_json.should be_json_eql.to_file(file_path).at_path(path)
26
+ end
27
+ end
28
+
21
29
  Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ do |path, negative, value|
22
30
  if negative
23
31
  last_json.should_not be_json_eql(JsonSpec.remember(value)).at_path(path)
@@ -34,6 +42,14 @@ Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include:$/ d
34
42
  end
35
43
  end
36
44
 
45
+ Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include file "(.+)"$/ do |path, negative, file_path|
46
+ if negative
47
+ last_json.should_not include_json.from_file(file_path).at_path(path)
48
+ else
49
+ last_json.should include_json.from_file(file_path).at_path(path)
50
+ end
51
+ end
52
+
37
53
  Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ do |path, negative, value|
38
54
  if negative
39
55
  last_json.should_not include_json(JsonSpec.remember(value)).at_path(path)
@@ -63,11 +79,10 @@ Then /^the (?:JSON|json)(?: response)? should( not)? have "(.*)"$/ do |negative,
63
79
  end
64
80
 
65
81
  Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be an? (.*)$/ do |path, negative, type|
66
- klass = Module.const_get(type.gsub(/^./){|x| x.upcase })
67
82
  if negative
68
- last_json.should_not have_json_type(klass).at_path(path)
83
+ last_json.should_not have_json_type(type).at_path(path)
69
84
  else
70
- last_json.should have_json_type(klass).at_path(path)
85
+ last_json.should have_json_type(type).at_path(path)
71
86
  end
72
87
  end
73
88
 
@@ -1,5 +1,8 @@
1
1
  module JsonSpec
2
- class MissingPathError < StandardError
2
+ class Error < StandardError
3
+ end
4
+
5
+ class MissingPath < Error
3
6
  attr_reader :path
4
7
 
5
8
  def initialize(path)
@@ -10,4 +13,22 @@ module JsonSpec
10
13
  %(Missing JSON path "#{path}")
11
14
  end
12
15
  end
16
+
17
+ class MissingDirectory < Error
18
+ def to_s
19
+ "No JsonSpec.directory set"
20
+ end
21
+ end
22
+
23
+ class MissingFile < Error
24
+ attr_reader :path
25
+
26
+ def initialize(path)
27
+ @path = path
28
+ end
29
+
30
+ def to_s
31
+ "No JSON file at #{path}"
32
+ end
33
+ end
13
34
  end
@@ -1,4 +1,4 @@
1
- require 'multi_json'
1
+ require "multi_json"
2
2
 
3
3
  module JsonSpec
4
4
  module Helpers
@@ -23,6 +23,13 @@ module JsonSpec
23
23
  end
24
24
  end
25
25
 
26
+ def load_json(relative_path)
27
+ missing_json_directory! if JsonSpec.directory.nil?
28
+ path = File.join(JsonSpec.directory, relative_path)
29
+ missing_json_file!(path) unless File.exist?(path)
30
+ File.read(path)
31
+ end
32
+
26
33
  private
27
34
  def value_at_json_path(ruby, path)
28
35
  return ruby unless path
@@ -40,7 +47,15 @@ module JsonSpec
40
47
  end
41
48
 
42
49
  def missing_json_path!(path)
43
- raise JsonSpec::MissingPathError.new(path)
50
+ raise JsonSpec::MissingPath.new(path)
51
+ end
52
+
53
+ def missing_json_directory!
54
+ raise JsonSpec::MissingDirectory
55
+ end
56
+
57
+ def missing_json_file!(path)
58
+ raise JsonSpec::MissingFile.new(path)
44
59
  end
45
60
  end
46
61
  end
@@ -0,0 +1,63 @@
1
+ module JsonSpec
2
+ module Matchers
3
+ class BeJsonEql
4
+ include JsonSpec::Helpers
5
+ include JsonSpec::Exclusion
6
+ include JsonSpec::Messages
7
+
8
+ attr_reader :expected, :actual
9
+
10
+ def diffable?
11
+ true
12
+ end
13
+
14
+ def initialize(expected_json = nil)
15
+ @expected_json = expected_json
16
+ end
17
+
18
+ def matches?(actual_json)
19
+ raise "Expected equivalent JSON not provided" if @expected_json.nil?
20
+
21
+ @actual, @expected = scrub(actual_json, @path), scrub(@expected_json)
22
+ @actual == @expected
23
+ end
24
+
25
+ def at_path(path)
26
+ @path = path
27
+ self
28
+ end
29
+
30
+ def to_file(path)
31
+ @expected_json = load_json(path)
32
+ self
33
+ end
34
+
35
+ def excluding(*keys)
36
+ excluded_keys.merge(keys.map(&:to_s))
37
+ self
38
+ end
39
+
40
+ def including(*keys)
41
+ excluded_keys.subtract(keys.map(&:to_s))
42
+ self
43
+ end
44
+
45
+ def failure_message_for_should
46
+ message_with_path("Expected equivalent JSON")
47
+ end
48
+
49
+ def failure_message_for_should_not
50
+ message_with_path("Expected inequivalent JSON")
51
+ end
52
+
53
+ def description
54
+ message_with_path("equal JSON")
55
+ end
56
+
57
+ private
58
+ def scrub(json, path = nil)
59
+ generate_normalized_json(exclude_keys(parse_json(json, path))).chomp + "\n"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ module JsonSpec
2
+ module Matchers
3
+ class HaveJsonPath
4
+ include JsonSpec::Helpers
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def matches?(json)
11
+ parse_json(json, @path)
12
+ true
13
+ rescue JsonSpec::MissingPath
14
+ false
15
+ end
16
+
17
+ def failure_message_for_should
18
+ %(Expected JSON path "#{@path}")
19
+ end
20
+
21
+ def failure_message_for_should_not
22
+ %(Expected no JSON path "#{@path}")
23
+ end
24
+
25
+ def description
26
+ %(have JSON path "#{@path}")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module JsonSpec
2
+ module Matchers
3
+ class HaveJsonSize
4
+ include JsonSpec::Helpers
5
+ include JsonSpec::Messages
6
+
7
+ def initialize(size)
8
+ @expected = size
9
+ end
10
+
11
+ def matches?(json)
12
+ ruby = parse_json(json, @path)
13
+ @actual = Enumerable === ruby ? ruby.size : 1
14
+ @actual == @expected
15
+ end
16
+
17
+ def at_path(path)
18
+ @path = path
19
+ self
20
+ end
21
+
22
+ def failure_message_for_should
23
+ message_with_path("Expected JSON value size to be #{@expected}, got #{@actual}")
24
+ end
25
+
26
+ def failure_message_for_should_not
27
+ message_with_path("Expected JSON value size to not be #{@expected}, got #{@actual}")
28
+ end
29
+
30
+ def description
31
+ message_with_path(%(have JSON size "#{@expected}"))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ module JsonSpec
2
+ module Matchers
3
+ class HaveJsonType
4
+ include JsonSpec::Helpers
5
+ include JsonSpec::Messages
6
+
7
+ def initialize(type)
8
+ @classes = type_to_classes(type)
9
+ end
10
+
11
+ def matches?(json)
12
+ @ruby = parse_json(json, @path)
13
+ @classes.any?{|c| c === @ruby }
14
+ end
15
+
16
+ def at_path(path)
17
+ @path = path
18
+ self
19
+ end
20
+
21
+ def failure_message_for_should
22
+ message_with_path("Expected JSON value type to be #{@classes.join(", ")}, got #{@ruby.class}")
23
+ end
24
+
25
+ def failure_message_for_should_not
26
+ message_with_path("Expected JSON value type to not be #{@classes.join(", ")}, got #{@ruby.class}")
27
+ end
28
+
29
+ def description
30
+ message_with_path(%(have JSON type "#{@classes.join(", ")}"))
31
+ end
32
+
33
+ private
34
+ def type_to_classes(type)
35
+ case type
36
+ when Class then [type]
37
+ when Array then type.map{|t| type_to_classes(t) }.flatten
38
+ else
39
+ case type.to_s.downcase
40
+ when "boolean" then [TrueClass, FalseClass]
41
+ when "object" then [Hash]
42
+ when "nil", "null" then [NilClass]
43
+ else [Module.const_get(type.to_s.capitalize)]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ module JsonSpec
2
+ module Matchers
3
+ class IncludeJson
4
+ include JsonSpec::Helpers
5
+ include JsonSpec::Exclusion
6
+ include JsonSpec::Messages
7
+
8
+ def initialize(expected_json = nil)
9
+ @expected_json = expected_json
10
+ end
11
+
12
+ def matches?(actual_json)
13
+ raise "Expected included JSON not provided" if @expected_json.nil?
14
+
15
+ actual = parse_json(actual_json, @path)
16
+ expected = exclude_keys(parse_json(@expected_json))
17
+ case actual
18
+ when Hash then actual.values.map{|v| exclude_keys(v) }.include?(expected)
19
+ when Array then actual.map{|e| exclude_keys(e) }.include?(expected)
20
+ else false
21
+ end
22
+ end
23
+
24
+ def at_path(path)
25
+ @path = path
26
+ self
27
+ end
28
+
29
+ def from_file(path)
30
+ @expected_json = load_json(path)
31
+ self
32
+ end
33
+
34
+ def excluding(*keys)
35
+ excluded_keys.merge(keys.map(&:to_s))
36
+ self
37
+ end
38
+
39
+ def including(*keys)
40
+ excluded_keys.subtract(keys.map(&:to_s))
41
+ self
42
+ end
43
+
44
+ def failure_message_for_should
45
+ message_with_path("Expected included JSON")
46
+ end
47
+
48
+ def failure_message_for_should_not
49
+ message_with_path("Expected excluded JSON")
50
+ end
51
+
52
+ def description
53
+ message_with_path("include JSON")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,225 +1,16 @@
1
- require "json"
2
- require "rspec"
1
+ require "json_spec/matchers/be_json_eql"
2
+ require "json_spec/matchers/include_json"
3
+ require "json_spec/matchers/have_json_path"
4
+ require "json_spec/matchers/have_json_type"
5
+ require "json_spec/matchers/have_json_size"
3
6
 
4
7
  module JsonSpec
5
8
  module Matchers
6
- class BeJsonEql
7
- include JsonSpec::Helpers
8
- include JsonSpec::Exclusion
9
-
10
- attr_reader :expected, :actual
11
-
12
- def diffable?
13
- true
14
- end
15
-
16
- def initialize(expected_json)
17
- @expected_json = expected_json
18
- end
19
-
20
- def matches?(actual_json)
21
- @actual, @expected = scrub(actual_json, @path), scrub(@expected_json)
22
- @actual == @expected
23
- end
24
-
25
- def at_path(path)
26
- @path = path
27
- self
28
- end
29
-
30
- def excluding(*keys)
31
- excluded_keys.merge(keys.map{|k| k.to_s })
32
- self
33
- end
34
-
35
- def including(*keys)
36
- excluded_keys.subtract(keys.map{|k| k.to_s })
37
- self
38
- end
39
-
40
- def failure_message_for_should
41
- message = "Expected equivalent JSON"
42
- message << %( at path "#{@path}") if @path
43
- message
44
- end
45
-
46
- def failure_message_for_should_not
47
- message = "Expected inequivalent JSON"
48
- message << %( at path "#{@path}") if @path
49
- message
50
- end
51
-
52
- def description
53
- message = "equal JSON"
54
- message << %( at path "#{@path}") if @path
55
- message
56
- end
57
-
58
- private
59
- def scrub(json, path = nil)
60
- generate_normalized_json(exclude_keys(parse_json(json, path))).chomp + "\n"
61
- end
62
- end
63
-
64
- class IncludeJson
65
- include JsonSpec::Helpers
66
- include JsonSpec::Exclusion
67
-
68
- def initialize(expected_json)
69
- @expected_json = expected_json
70
- end
71
-
72
- def matches?(actual_json)
73
- actual = parse_json(actual_json, @path)
74
- expected = exclude_keys(parse_json(@expected_json))
75
- case actual
76
- when Hash then actual.values.map{|v| exclude_keys(v) }.include?(expected)
77
- when Array then actual.map{|e| exclude_keys(e) }.include?(expected)
78
- else false
79
- end
80
- end
81
-
82
- def at_path(path)
83
- @path = path
84
- self
85
- end
86
-
87
- def excluding(*keys)
88
- excluded_keys.merge(keys.map{|k| k.to_s })
89
- self
90
- end
91
-
92
- def including(*keys)
93
- excluded_keys.subtract(keys.map{|k| k.to_s })
94
- self
95
- end
96
-
97
- def failure_message_for_should
98
- message = "Expected included JSON"
99
- message << %( at path "#{@path}") if @path
100
- message
101
- end
102
-
103
- def failure_message_for_should_not
104
- message = "Expected excluded JSON"
105
- message << %( at path "#{@path}") if @path
106
- message
107
- end
108
-
109
- def description
110
- message = "include JSON"
111
- message << %( at path "#{@path}") if @path
112
- message
113
- end
114
- end
115
-
116
- class HaveJsonPath
117
- include JsonSpec::Helpers
118
-
119
- def initialize(path)
120
- @path = path
121
- end
122
-
123
- def matches?(json)
124
- begin
125
- parse_json(json, @path)
126
- true
127
- rescue JsonSpec::MissingPathError
128
- false
129
- end
130
- end
131
-
132
- def failure_message_for_should
133
- %(Expected JSON path "#{@path}")
134
- end
135
-
136
- def failure_message_for_should_not
137
- %(Expected no JSON path "#{@path}")
138
- end
139
-
140
- def description
141
- %(have JSON path "#{@path}")
142
- end
143
- end
144
-
145
- class HaveJsonType
146
- include JsonSpec::Helpers
147
-
148
- def initialize(klass)
149
- @klass = klass
150
- end
151
-
152
- def matches?(json)
153
- @ruby = parse_json(json, @path)
154
- @ruby.is_a?(@klass)
155
- end
156
-
157
- def at_path(path)
158
- @path = path
159
- self
160
- end
161
-
162
- def failure_message_for_should
163
- message = "Expected JSON value type to be #{@klass}, got #{@ruby.class}"
164
- message << %( at path "#{@path}") if @path
165
- message
166
- end
167
-
168
- def failure_message_for_should_not
169
- message = "Expected JSON value type to not be #{@klass}, got #{@ruby.class}"
170
- message << %( at path "#{@path}") if @path
171
- message
172
- end
173
-
174
- def description
175
- message = %(have JSON type "#{@klass.to_s}")
176
- message << %( at path "#{@path}") if @path
177
- message
178
- end
179
- end
180
-
181
- class HaveJsonSize
182
- include JsonSpec::Helpers
183
-
184
- def initialize(size)
185
- @expected = size
186
- end
187
-
188
- def matches?(json)
189
- ruby = parse_json(json, @path)
190
- @actual = ruby.is_a?(Enumerable) ? ruby.size : 1
191
- @actual == @expected
192
- end
193
-
194
- def at_path(path)
195
- @path = path
196
- self
197
- end
198
-
199
- def failure_message_for_should
200
- message = "Expected JSON value size to be #{@expected}, got #{@actual}"
201
- message << %( at path "#{@path}") if @path
202
- message
203
- end
204
-
205
- def failure_message_for_should_not
206
- message = "Expected JSON value size to not be #{@expected}, got #{@actual}"
207
- message << %( at path "#{@path}") if @path
208
- message
209
- end
210
-
211
- def description
212
- message = %(have JSON size "#{@expected}")
213
- message << %( at path "#{@path}") if @path
214
- message
215
- end
216
- end
217
-
218
- def be_json_eql(json)
9
+ def be_json_eql(json = nil)
219
10
  JsonSpec::Matchers::BeJsonEql.new(json)
220
11
  end
221
12
 
222
- def include_json(json)
13
+ def include_json(json = nil)
223
14
  JsonSpec::Matchers::IncludeJson.new(json)
224
15
  end
225
16
 
@@ -227,8 +18,8 @@ module JsonSpec
227
18
  JsonSpec::Matchers::HaveJsonPath.new(path)
228
19
  end
229
20
 
230
- def have_json_type(klass)
231
- JsonSpec::Matchers::HaveJsonType.new(klass)
21
+ def have_json_type(type)
22
+ JsonSpec::Matchers::HaveJsonType.new(type)
232
23
  end
233
24
 
234
25
  def have_json_size(size)
@@ -5,12 +5,11 @@ module JsonSpec
5
5
  end
6
6
 
7
7
  def memorize(key, value)
8
- memory[key] = value
8
+ memory[key.to_sym] = value
9
9
  end
10
10
 
11
11
  def remember(json)
12
- return json if memory.empty?
13
- json.gsub(/%\{(#{memory.keys.map{|k| Regexp.quote(k) }.join("|")})\}/){ memory[$1] }
12
+ memory.empty? ? json : json % memory
14
13
  end
15
14
 
16
15
  def forget
@@ -0,0 +1,8 @@
1
+ module JsonSpec
2
+ module Messages
3
+ def message_with_path(message)
4
+ message << %( at path "#{@path}") if @path
5
+ message
6
+ end
7
+ end
8
+ end
data/lib/json_spec.rb CHANGED
@@ -1,8 +1,10 @@
1
- require "json_spec/version"
1
+ require "json"
2
+ require "rspec"
2
3
  require "json_spec/errors"
3
4
  require "json_spec/configuration"
4
5
  require "json_spec/exclusion"
5
6
  require "json_spec/helpers"
7
+ require "json_spec/messages"
6
8
  require "json_spec/matchers"
7
9
  require "json_spec/memory"
8
10