Skip to content

Commit 2203c35

Browse files
committed
Add support for one-dimensional hashes
1 parent 4181903 commit 2203c35

File tree

2 files changed

+207
-3
lines changed

2 files changed

+207
-3
lines changed

lib/super_diff/differ.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def call
2727
diff_strings
2828
elsif expected.is_a?(Array) && actual.is_a?(Array)
2929
diff_arrays
30+
elsif expected.is_a?(Hash) && actual.is_a?(Hash)
31+
diff_hashes
3032
else
3133
diff_objects
3234
end
@@ -137,6 +139,49 @@ def diff_arrays
137139
OUTPUT
138140
end
139141

142+
def diff_hashes
143+
all_keys = (expected.keys | actual.keys)
144+
145+
details = all_keys.inject([]) do |array, key|
146+
if expected.include?(key)
147+
if actual.include?(key)
148+
if expected[key] == actual[key]
149+
array
150+
else
151+
array << "*[#{key.inspect}]: " +
152+
"Differing #{plural_type_for(actual[key])}.\n" +
153+
" Expected: #{expected[key].inspect}\n" +
154+
" Actual: #{actual[key].inspect}"
155+
end
156+
else
157+
array << "*[#{key.inspect} -> ?]: Actual is missing key."
158+
end
159+
else
160+
if actual.include?(key)
161+
array << "*[? -> #{key.inspect}]: " +
162+
"Actual has extra key (with value of #{actual[key].inspect})."
163+
else
164+
array
165+
end
166+
end
167+
end
168+
169+
if details.any?
170+
<<~OUTPUT
171+
Differing hashes.
172+
173+
Expected: #{inspect(expected)}
174+
Actual: #{inspect(actual)}
175+
176+
Details:
177+
178+
#{details.map { |detail| "- #{detail}"}.join("\n")}
179+
OUTPUT
180+
else
181+
""
182+
end
183+
end
184+
140185
def diff_objects
141186
<<~OUTPUT
142187
Differing #{plural_type_for(actual)}.
@@ -166,5 +211,16 @@ def plural_type_for(value)
166211
else "objects"
167212
end
168213
end
214+
215+
def inspect(value)
216+
if value.is_a?(Hash)
217+
value.inspect.
218+
gsub(/([^,]+)=>([^,]+)/, '\1 => \2').
219+
gsub(/:(\w+) => /, '\1: ').
220+
gsub(/\{([^{}]+)\}/, '{ \1 }')
221+
else
222+
value.inspect
223+
end
224+
end
169225
end
170226
end

spec/unit/differ_spec.rb

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@
258258
end
259259
end
260260

261-
context "given two one-dimensional arrays where the actual has added elements" do
261+
context "given two one-dimensional arrays where the actual has extra elements" do
262262
it "returns a message along with the diff" do
263263
actual_output = described_class.call(
264264
expected: ["foo", "bar"],
@@ -307,14 +307,162 @@
307307
context "given the same hash" do
308308
it "returns an empty string" do
309309
output = described_class.call(
310-
expected: { foo: "bar" },
311-
actual: { foo: "bar" }
310+
expected: { name: "Elliot" },
311+
actual: { name: "Elliot" }
312312
)
313313

314314
expect(output).to eq("")
315315
end
316316
end
317317

318+
context "given two equal-size, one-dimensional hashes where the same key has differing numbers" do
319+
it "returns a message along with the diff" do
320+
actual_output = described_class.call(
321+
expected: { tall: 12, grande: 19, venti: 20 },
322+
actual: { tall: 12, grande: 16, venti: 20 }
323+
)
324+
325+
expected_output = <<~STR
326+
Differing hashes.
327+
328+
Expected: { tall: 12, grande: 19, venti: 20 }
329+
Actual: { tall: 12, grande: 16, venti: 20 }
330+
331+
Details:
332+
333+
- *[:grande]: Differing numbers.
334+
Expected: 19
335+
Actual: 16
336+
STR
337+
338+
expect(actual_output).to eq(expected_output)
339+
end
340+
end
341+
342+
context "given two equal-size, one-dimensional hashes where the same key has differing symbols" do
343+
it "returns a message along with the diff" do
344+
actual_output = described_class.call(
345+
expected: { tall: :small, grande: :grand, venti: :large },
346+
actual: { tall: :small, grande: :medium, venti: :large },
347+
)
348+
349+
expected_output = <<~STR
350+
Differing hashes.
351+
352+
Expected: { tall: :small, grande: :grand, venti: :large }
353+
Actual: { tall: :small, grande: :medium, venti: :large }
354+
355+
Details:
356+
357+
- *[:grande]: Differing symbols.
358+
Expected: :grand
359+
Actual: :medium
360+
STR
361+
362+
expect(actual_output).to eq(expected_output)
363+
end
364+
end
365+
366+
context "given two equal-size, one-dimensional hashes where the same key has differing strings" do
367+
it "returns a message along with the diff" do
368+
actual_output = described_class.call(
369+
expected: { tall: "small", grande: "grand", venti: "large" },
370+
actual: { tall: "small", grande: "medium", venti: "large" },
371+
)
372+
373+
expected_output = <<~STR
374+
Differing hashes.
375+
376+
Expected: { tall: "small", grande: "grand", venti: "large" }
377+
Actual: { tall: "small", grande: "medium", venti: "large" }
378+
379+
Details:
380+
381+
- *[:grande]: Differing strings.
382+
Expected: "grand"
383+
Actual: "medium"
384+
STR
385+
386+
expect(actual_output).to eq(expected_output)
387+
end
388+
end
389+
390+
context "given two equal-size, one-dimensional hashes where the same key has differing objects" do
391+
it "returns a message along with the diff" do
392+
actual_output = described_class.call(
393+
expected: {
394+
steve: SuperDiff::Test::Person.new(name: "Jobs"),
395+
susan: SuperDiff::Test::Person.new(name: "Kare")
396+
},
397+
actual: {
398+
steve: SuperDiff::Test::Person.new(name: "Wozniak"),
399+
susan: SuperDiff::Test::Person.new(name: "Kare")
400+
}
401+
)
402+
403+
expected_output = <<~STR
404+
Differing hashes.
405+
406+
Expected: { steve: #<Person name="Jobs">, susan: #<Person name="Kare"> }
407+
Actual: { steve: #<Person name="Wozniak">, susan: #<Person name="Kare"> }
408+
409+
Details:
410+
411+
- *[:steve]: Differing objects.
412+
Expected: #<Person name="Jobs">
413+
Actual: #<Person name="Wozniak">
414+
STR
415+
416+
expect(actual_output).to eq(expected_output)
417+
end
418+
end
419+
420+
context "given two equal-size, one-dimensional hashes where the actual has extra keys" do
421+
it "returns a message along with the diff" do
422+
actual_output = described_class.call(
423+
expected: { latte: 4.5 },
424+
actual: { latte: 4.5, mocha: 3.5, cortado: 3 }
425+
)
426+
427+
expected_output = <<~STR
428+
Differing hashes.
429+
430+
Expected: { latte: 4.5 }
431+
Actual: { latte: 4.5, mocha: 3.5, cortado: 3 }
432+
433+
Details:
434+
435+
- *[? -> :mocha]: Actual has extra key (with value of 3.5).
436+
- *[? -> :cortado]: Actual has extra key (with value of 3).
437+
STR
438+
439+
expect(actual_output).to eq(expected_output)
440+
end
441+
end
442+
443+
context "given two equal-size, one-dimensional hashes where the actual has missing keys" do
444+
it "returns a message along with the diff" do
445+
actual_output = described_class.call(
446+
expected: { latte: 4.5, mocha: 3.5, cortado: 3 },
447+
actual: { latte: 4.5 }
448+
)
449+
450+
expected_output = <<~STR
451+
Differing hashes.
452+
453+
Expected: { latte: 4.5, mocha: 3.5, cortado: 3 }
454+
Actual: { latte: 4.5 }
455+
456+
Details:
457+
458+
- *[:mocha -> ?]: Actual is missing key.
459+
- *[:cortado -> ?]: Actual is missing key.
460+
STR
461+
462+
expect(actual_output).to eq(expected_output)
463+
end
464+
end
465+
318466
context "given two objects which == each other" do
319467
it "returns an empty string" do
320468
expected = SuperDiff::Test::Person.new(name: "Elliot")

0 commit comments

Comments
 (0)