super_diff 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +174 -0
  3. data/lib/super_diff/csi/color_helper.rb +52 -0
  4. data/lib/super_diff/csi/eight_bit_color.rb +131 -0
  5. data/lib/super_diff/csi/eight_bit_sequence.rb +27 -0
  6. data/lib/super_diff/csi/four_bit_color.rb +80 -0
  7. data/lib/super_diff/csi/four_bit_sequence.rb +24 -0
  8. data/lib/super_diff/csi/reset_sequence.rb +9 -0
  9. data/lib/super_diff/csi/sequence.rb +22 -0
  10. data/lib/super_diff/csi/twenty_four_bit_color.rb +41 -0
  11. data/lib/super_diff/csi/twenty_four_bit_sequence.rb +27 -0
  12. data/lib/super_diff/csi.rb +29 -0
  13. data/lib/super_diff/diff_formatter.rb +37 -0
  14. data/lib/super_diff/diff_formatters/array.rb +21 -0
  15. data/lib/super_diff/diff_formatters/base.rb +37 -0
  16. data/lib/super_diff/diff_formatters/collection.rb +107 -0
  17. data/lib/super_diff/diff_formatters/hash.rb +34 -0
  18. data/lib/super_diff/diff_formatters/multi_line_string.rb +31 -0
  19. data/lib/super_diff/diff_formatters/object.rb +27 -0
  20. data/lib/super_diff/diff_formatters.rb +5 -0
  21. data/lib/super_diff/differ.rb +48 -0
  22. data/lib/super_diff/differs/array.rb +24 -0
  23. data/lib/super_diff/differs/base.rb +42 -0
  24. data/lib/super_diff/differs/empty.rb +13 -0
  25. data/lib/super_diff/differs/hash.rb +24 -0
  26. data/lib/super_diff/differs/multi_line_string.rb +27 -0
  27. data/lib/super_diff/differs/object.rb +68 -0
  28. data/lib/super_diff/differs.rb +5 -0
  29. data/lib/super_diff/equality_matcher.rb +45 -0
  30. data/lib/super_diff/equality_matchers/array.rb +44 -0
  31. data/lib/super_diff/equality_matchers/base.rb +42 -0
  32. data/lib/super_diff/equality_matchers/hash.rb +44 -0
  33. data/lib/super_diff/equality_matchers/multi_line_string.rb +44 -0
  34. data/lib/super_diff/equality_matchers/object.rb +18 -0
  35. data/lib/super_diff/equality_matchers/single_line_string.rb +28 -0
  36. data/lib/super_diff/equality_matchers.rb +5 -0
  37. data/lib/super_diff/errors.rb +20 -0
  38. data/lib/super_diff/helpers.rb +96 -0
  39. data/lib/super_diff/operation_sequences/array.rb +14 -0
  40. data/lib/super_diff/operation_sequences/base.rb +11 -0
  41. data/lib/super_diff/operation_sequences/hash.rb +14 -0
  42. data/lib/super_diff/operation_sequences/object.rb +14 -0
  43. data/lib/super_diff/operational_sequencer.rb +43 -0
  44. data/lib/super_diff/operational_sequencers/array.rb +127 -0
  45. data/lib/super_diff/operational_sequencers/base.rb +97 -0
  46. data/lib/super_diff/operational_sequencers/hash.rb +82 -0
  47. data/lib/super_diff/operational_sequencers/multi_line_string.rb +85 -0
  48. data/lib/super_diff/operational_sequencers/object.rb +96 -0
  49. data/lib/super_diff/operational_sequencers.rb +5 -0
  50. data/lib/super_diff/operations/binary_operation.rb +47 -0
  51. data/lib/super_diff/operations/unary_operation.rb +25 -0
  52. data/lib/super_diff/rspec/differ.rb +30 -0
  53. data/lib/super_diff/rspec/monkey_patches.rb +122 -0
  54. data/lib/super_diff/rspec.rb +19 -0
  55. data/lib/super_diff/value_inspection.rb +11 -0
  56. data/lib/super_diff/version.rb +3 -0
  57. data/lib/super_diff.rb +50 -0
  58. data/spec/examples.txt +46 -0
  59. data/spec/integration/rspec_spec.rb +261 -0
  60. data/spec/spec_helper.rb +44 -0
  61. data/spec/support/color_helper.rb +49 -0
  62. data/spec/support/command_runner.rb +279 -0
  63. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +76 -0
  64. data/spec/support/person.rb +23 -0
  65. data/spec/support/person_diff_formatter.rb +15 -0
  66. data/spec/support/person_operation_sequence.rb +14 -0
  67. data/spec/support/person_operational_sequencer.rb +19 -0
  68. data/spec/unit/equality_matcher_spec.rb +1233 -0
  69. data/super_diff.gemspec +23 -0
  70. metadata +153 -0
@@ -0,0 +1,1233 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe SuperDiff::EqualityMatcher do
4
+ describe "#call" do
5
+ context "given the same integers" do
6
+ it "returns an empty string" do
7
+ output = described_class.call(expected: 1, actual: 1)
8
+
9
+ expect(output).to eq("")
10
+ end
11
+ end
12
+
13
+ context "given the same numbers (even if they're different types)" do
14
+ it "returns an empty string" do
15
+ output = described_class.call(expected: 1, actual: 1.0)
16
+
17
+ expect(output).to eq("")
18
+ end
19
+ end
20
+
21
+ context "given differing numbers" do
22
+ it "returns a message along with a comparison" do
23
+ actual_output = described_class.call(expected: 42, actual: 1)
24
+
25
+ expected_output = <<~STR.strip
26
+ Differing numbers.
27
+
28
+ #{
29
+ colored do
30
+ red_line %(Expected: 42)
31
+ green_line %( Actual: 1)
32
+ end
33
+ }
34
+ STR
35
+
36
+ expect(actual_output).to eq(expected_output)
37
+ end
38
+ end
39
+
40
+ context "given the same symbol" do
41
+ it "returns an empty string" do
42
+ output = described_class.call(expected: :foo, actual: :foo)
43
+
44
+ expect(output).to eq("")
45
+ end
46
+ end
47
+
48
+ context "given differing symbols" do
49
+ it "returns a message along with a comparison" do
50
+ actual_output = described_class.call(expected: :foo, actual: :bar)
51
+
52
+ expected_output = <<~STR.strip
53
+ Differing symbols.
54
+
55
+ #{
56
+ colored do
57
+ red_line %(Expected: :foo)
58
+ green_line %( Actual: :bar)
59
+ end
60
+ }
61
+ STR
62
+
63
+ expect(actual_output).to eq(expected_output)
64
+ end
65
+ end
66
+
67
+ context "given the same string" do
68
+ it "returns an empty string" do
69
+ output = described_class.call(expected: "", actual: "")
70
+
71
+ expect(output).to eq("")
72
+ end
73
+ end
74
+
75
+ context "given completely different single-line strings" do
76
+ it "returns a message along with the diff" do
77
+ actual_output = described_class.call(
78
+ expected: "Marty",
79
+ actual: "Jennifer",
80
+ )
81
+
82
+ expected_output = <<~STR.strip
83
+ Differing strings.
84
+
85
+ #{
86
+ colored do
87
+ red_line %(Expected: "Marty")
88
+ green_line %( Actual: "Jennifer")
89
+ end
90
+ }
91
+ STR
92
+
93
+ expect(actual_output).to eq(expected_output)
94
+ end
95
+ end
96
+
97
+ context "given closely different single-line strings" do
98
+ it "returns a message along with the diff" do
99
+ actual_output = described_class.call(
100
+ expected: "Marty",
101
+ actual: "Marty McFly",
102
+ )
103
+
104
+ expected_output = <<~STR.strip
105
+ Differing strings.
106
+
107
+ #{
108
+ colored do
109
+ red_line %(Expected: "Marty")
110
+ green_line %( Actual: "Marty McFly")
111
+ end
112
+ }
113
+ STR
114
+
115
+ expect(actual_output).to eq(expected_output)
116
+ end
117
+ end
118
+
119
+ context "given closely different multi-line strings" do
120
+ it "returns a message along with the diff" do
121
+ actual_output = described_class.call(
122
+ expected: "This is a line\nAnd that's a line\nAnd there's a line too",
123
+ actual: "This is a line\nSomething completely different\nAnd there's a line too",
124
+ )
125
+
126
+ expected_output = <<~STR.strip
127
+ Differing strings.
128
+
129
+ #{
130
+ colored do
131
+ red_line %(Expected: "This is a line⏎And that's a line⏎And there's a line too")
132
+ green_line %( Actual: "This is a line⏎Something completely different⏎And there's a line too")
133
+ end
134
+ }
135
+
136
+ Diff:
137
+
138
+ #{
139
+ colored do
140
+ plain_line %( This is a line⏎)
141
+ red_line %(- And that's a line⏎)
142
+ green_line %(+ Something completely different⏎)
143
+ plain_line %( And there's a line too)
144
+ end
145
+ }
146
+ STR
147
+
148
+ expect(actual_output).to eq(expected_output)
149
+ end
150
+ end
151
+
152
+ context "given completely different multi-line strings" do
153
+ it "returns a message along with the diff" do
154
+ actual_output = described_class.call(
155
+ expected: "This is a line\nAnd that's a line\n",
156
+ actual: "Something completely different\nAnd something else too\n",
157
+ )
158
+
159
+ expected_output = <<~STR.strip
160
+ Differing strings.
161
+
162
+ #{
163
+ colored do
164
+ red_line %(Expected: "This is a line⏎And that's a line⏎")
165
+ green_line %( Actual: "Something completely different⏎And something else too⏎")
166
+ end
167
+ }
168
+
169
+ Diff:
170
+
171
+ #{
172
+ colored do
173
+ red_line %(- This is a line⏎)
174
+ red_line %(- And that's a line⏎)
175
+ green_line %(+ Something completely different⏎)
176
+ green_line %(+ And something else too⏎)
177
+ end
178
+ }
179
+ STR
180
+
181
+ expect(actual_output).to eq(expected_output)
182
+ end
183
+ end
184
+
185
+ context "given the same array" do
186
+ it "returns an empty string" do
187
+ output = described_class.call(
188
+ expected: ["sausage", "egg", "cheese"],
189
+ actual: ["sausage", "egg", "cheese"],
190
+ )
191
+
192
+ expect(output).to eq("")
193
+ end
194
+ end
195
+
196
+ context "given two equal-length, one-dimensional arrays with differing numbers" do
197
+ it "returns a message along with the diff" do
198
+ actual_output = described_class.call(
199
+ expected: [1, 2, 3, 4],
200
+ actual: [1, 2, 99, 4],
201
+ )
202
+
203
+ expected_output = <<~STR.strip
204
+ Differing arrays.
205
+
206
+ #{
207
+ colored do
208
+ red_line %(Expected: [1, 2, 3, 4])
209
+ green_line %( Actual: [1, 2, 99, 4])
210
+ end
211
+ }
212
+
213
+ Diff:
214
+
215
+ #{
216
+ colored do
217
+ plain_line %( [)
218
+ plain_line %( 1,)
219
+ plain_line %( 2,)
220
+ red_line %(- 3,)
221
+ green_line %(+ 99,)
222
+ plain_line %( 4)
223
+ plain_line %( ])
224
+ end
225
+ }
226
+ STR
227
+
228
+ expect(actual_output).to eq(expected_output)
229
+ end
230
+ end
231
+
232
+ context "given two equal-length, one-dimensional arrays with differing symbols" do
233
+ it "returns a message along with the diff" do
234
+ actual_output = described_class.call(
235
+ expected: [:one, :fish, :two, :fish],
236
+ actual: [:one, :FISH, :two, :fish],
237
+ )
238
+
239
+ expected_output = <<~STR.strip
240
+ Differing arrays.
241
+
242
+ #{
243
+ colored do
244
+ red_line %(Expected: [:one, :fish, :two, :fish])
245
+ green_line %( Actual: [:one, :FISH, :two, :fish])
246
+ end
247
+ }
248
+
249
+ Diff:
250
+
251
+ #{
252
+ colored do
253
+ plain_line %( [)
254
+ plain_line %( :one,)
255
+ red_line %(- :fish,)
256
+ green_line %(+ :FISH,)
257
+ plain_line %( :two,)
258
+ plain_line %( :fish)
259
+ plain_line %( ])
260
+ end
261
+ }
262
+ STR
263
+
264
+ expect(actual_output).to eq(expected_output)
265
+ end
266
+ end
267
+
268
+ context "given two equal-length, one-dimensional arrays with differing strings" do
269
+ it "returns a message along with the diff" do
270
+ actual_output = described_class.call(
271
+ expected: ["sausage", "egg", "cheese"],
272
+ actual: ["bacon", "egg", "cheese"],
273
+ )
274
+
275
+ expected_output = <<~STR.strip
276
+ Differing arrays.
277
+
278
+ #{
279
+ colored do
280
+ red_line %(Expected: ["sausage", "egg", "cheese"])
281
+ green_line %( Actual: ["bacon", "egg", "cheese"])
282
+ end
283
+ }
284
+
285
+ Diff:
286
+
287
+ #{
288
+ colored do
289
+ plain_line %( [)
290
+ red_line %(- "sausage",)
291
+ green_line %(+ "bacon",)
292
+ plain_line %( "egg",)
293
+ plain_line %( "cheese")
294
+ plain_line %( ])
295
+ end
296
+ }
297
+ STR
298
+
299
+ expect(actual_output).to eq(expected_output)
300
+ end
301
+ end
302
+
303
+ context "given two equal-length, one-dimensional arrays with differing objects" do
304
+ it "returns a message along with the diff" do
305
+ actual_output = described_class.call(
306
+ expected: [
307
+ SuperDiff::Test::Person.new(name: "Marty"),
308
+ SuperDiff::Test::Person.new(name: "Jennifer"),
309
+ ],
310
+ actual: [
311
+ SuperDiff::Test::Person.new(name: "Marty"),
312
+ SuperDiff::Test::Person.new(name: "Doc"),
313
+ ],
314
+ extra_operational_sequencer_classes: [
315
+ SuperDiff::Test::PersonOperationalSequencer,
316
+ ],
317
+ extra_diff_formatter_classes: [
318
+ SuperDiff::Test::PersonDiffFormatter,
319
+ ],
320
+ )
321
+
322
+ expected_output = <<~STR.strip
323
+ Differing arrays.
324
+
325
+ #{
326
+ colored do
327
+ red_line %(Expected: [#<SuperDiff::Test::Person name: "Marty">, #<SuperDiff::Test::Person name: "Jennifer">])
328
+ green_line %( Actual: [#<SuperDiff::Test::Person name: "Marty">, #<SuperDiff::Test::Person name: "Doc">])
329
+ end
330
+ }
331
+
332
+ Diff:
333
+
334
+ #{
335
+ colored do
336
+ plain_line %( [)
337
+ plain_line %( #<SuperDiff::Test::Person {)
338
+ plain_line %( name: "Marty")
339
+ plain_line %( }>,)
340
+ plain_line %( #<SuperDiff::Test::Person {)
341
+ red_line %(- name: "Jennifer")
342
+ green_line %(+ name: "Doc")
343
+ plain_line %( }>)
344
+ plain_line %( ])
345
+ end
346
+ }
347
+ STR
348
+
349
+ expect(actual_output).to eq(expected_output)
350
+ end
351
+ end
352
+
353
+ context "given two one-dimensional arrays where the actual has elements added to the end" do
354
+ it "returns a message along with the diff" do
355
+ actual_output = described_class.call(
356
+ expected: ["bread"],
357
+ actual: ["bread", "eggs", "milk"],
358
+ )
359
+
360
+ expected_output = <<~STR.strip
361
+ Differing arrays.
362
+
363
+ #{
364
+ colored do
365
+ red_line %(Expected: ["bread"])
366
+ green_line %( Actual: ["bread", "eggs", "milk"])
367
+ end
368
+ }
369
+
370
+ Diff:
371
+
372
+ #{
373
+ colored do
374
+ plain_line %( [)
375
+ plain_line %( "bread",)
376
+ green_line %(+ "eggs",)
377
+ green_line %(+ "milk")
378
+ plain_line %( ])
379
+ end
380
+ }
381
+ STR
382
+
383
+ expect(actual_output).to eq(expected_output)
384
+ end
385
+ end
386
+
387
+ context "given two one-dimensional arrays where the actual has elements missing from the end" do
388
+ it "returns a message along with the diff" do
389
+ actual_output = described_class.call(
390
+ expected: ["bread", "eggs", "milk"],
391
+ actual: ["bread"],
392
+ )
393
+
394
+ expected_output = <<~STR.strip
395
+ Differing arrays.
396
+
397
+ #{
398
+ colored do
399
+ red_line %(Expected: ["bread", "eggs", "milk"])
400
+ green_line %( Actual: ["bread"])
401
+ end
402
+ }
403
+
404
+ Diff:
405
+
406
+ #{
407
+ colored do
408
+ plain_line %( [)
409
+ plain_line %( "bread")
410
+ red_line %(- "eggs",)
411
+ red_line %(- "milk")
412
+ plain_line %( ])
413
+ end
414
+ }
415
+ STR
416
+
417
+ expect(actual_output).to eq(expected_output)
418
+ end
419
+ end
420
+
421
+ context "given two one-dimensional arrays where the actual has elements added to the beginning" do
422
+ it "returns a message along with the diff" do
423
+ actual_output = described_class.call(
424
+ expected: ["milk"],
425
+ actual: ["bread", "eggs", "milk"],
426
+ )
427
+
428
+ expected_output = <<~STR.strip
429
+ Differing arrays.
430
+
431
+ #{
432
+ colored do
433
+ red_line %(Expected: ["milk"])
434
+ green_line %( Actual: ["bread", "eggs", "milk"])
435
+ end
436
+ }
437
+
438
+ Diff:
439
+
440
+ #{
441
+ colored do
442
+ plain_line %( [)
443
+ green_line %(+ "bread",)
444
+ green_line %(+ "eggs",)
445
+ plain_line %( "milk")
446
+ plain_line %( ])
447
+ end
448
+ }
449
+ STR
450
+
451
+ expect(actual_output).to eq(expected_output)
452
+ end
453
+ end
454
+
455
+ context "given two one-dimensional arrays where the actual has elements removed from the beginning" do
456
+ it "returns a message along with the diff" do
457
+ actual_output = described_class.call(
458
+ expected: ["bread", "eggs", "milk"],
459
+ actual: ["milk"],
460
+ )
461
+
462
+ expected_output = <<~STR.strip
463
+ Differing arrays.
464
+
465
+ #{
466
+ colored do
467
+ red_line %(Expected: ["bread", "eggs", "milk"])
468
+ green_line %( Actual: ["milk"])
469
+ end
470
+ }
471
+
472
+ Diff:
473
+
474
+ #{
475
+ colored do
476
+ plain_line %( [)
477
+ red_line %(- "bread",)
478
+ red_line %(- "eggs",)
479
+ plain_line %( "milk")
480
+ plain_line %( ])
481
+ end
482
+ }
483
+ STR
484
+
485
+ expect(actual_output).to eq(expected_output)
486
+ end
487
+ end
488
+
489
+ context "given two arrays containing arrays with differing values" do
490
+ it "returns a message along with the diff" do
491
+ actual_output = described_class.call(
492
+ expected: [1, 2, [:a, :b, :c], 4],
493
+ actual: [1, 2, [:a, :x, :c], 4],
494
+ )
495
+
496
+ expected_output = <<~STR.strip
497
+ Differing arrays.
498
+
499
+ #{
500
+ colored do
501
+ red_line %(Expected: [1, 2, [:a, :b, :c], 4])
502
+ green_line %( Actual: [1, 2, [:a, :x, :c], 4])
503
+ end
504
+ }
505
+
506
+ Diff:
507
+
508
+ #{
509
+ colored do
510
+ plain_line %( [)
511
+ plain_line %( 1,)
512
+ plain_line %( 2,)
513
+ plain_line %( [)
514
+ plain_line %( :a,)
515
+ red_line %(- :b,)
516
+ green_line %(+ :x,)
517
+ plain_line %( :c)
518
+ plain_line %( ],)
519
+ plain_line %( 4)
520
+ plain_line %( ])
521
+ end
522
+ }
523
+ STR
524
+
525
+ expect(actual_output).to eq(expected_output)
526
+ end
527
+ end
528
+
529
+ context "given two arrays containing hashes with differing values" do
530
+ it "returns a message along with the diff" do
531
+ actual_output = described_class.call(
532
+ expected: [1, 2, { foo: "bar", baz: "qux" }, 4],
533
+ actual: [1, 2, { foo: "bar", baz: "qox" }, 4],
534
+ )
535
+
536
+ expected_output = <<~STR.strip
537
+ Differing arrays.
538
+
539
+ #{
540
+ colored do
541
+ red_line %(Expected: [1, 2, { foo: "bar", baz: "qux" }, 4])
542
+ green_line %( Actual: [1, 2, { foo: "bar", baz: "qox" }, 4])
543
+ end
544
+ }
545
+
546
+ Diff:
547
+
548
+ #{
549
+ colored do
550
+ plain_line %( [)
551
+ plain_line %( 1,)
552
+ plain_line %( 2,)
553
+ plain_line %( {)
554
+ plain_line %( foo: "bar",)
555
+ red_line %(- baz: "qux")
556
+ green_line %(+ baz: "qox")
557
+ plain_line %( },)
558
+ plain_line %( 4)
559
+ plain_line %( ])
560
+ end
561
+ }
562
+ STR
563
+
564
+ expect(actual_output).to eq(expected_output)
565
+ end
566
+ end
567
+
568
+ context "given two arrays containing custom objects with differing attributes" do
569
+ it "returns a message along with the diff" do
570
+ actual_output = described_class.call(
571
+ expected: [1, 2, SuperDiff::Test::Person.new(name: "Marty"), 4],
572
+ actual: [1, 2, SuperDiff::Test::Person.new(name: "Doc"), 4],
573
+ extra_operational_sequencer_classes: [
574
+ SuperDiff::Test::PersonOperationalSequencer,
575
+ ],
576
+ extra_diff_formatter_classes: [
577
+ SuperDiff::Test::PersonDiffFormatter,
578
+ ],
579
+ )
580
+
581
+ expected_output = <<~STR.strip
582
+ Differing arrays.
583
+
584
+ #{
585
+ colored do
586
+ red_line %(Expected: [1, 2, #<SuperDiff::Test::Person name: "Marty">, 4])
587
+ green_line %( Actual: [1, 2, #<SuperDiff::Test::Person name: "Doc">, 4])
588
+ end
589
+ }
590
+
591
+ Diff:
592
+
593
+ #{
594
+ colored do
595
+ plain_line %( [)
596
+ plain_line %( 1,)
597
+ plain_line %( 2,)
598
+ plain_line %( #<SuperDiff::Test::Person {)
599
+ red_line %(- name: "Marty")
600
+ green_line %(+ name: "Doc")
601
+ plain_line %( }>,)
602
+ plain_line %( 4)
603
+ plain_line %( ])
604
+ end
605
+ }
606
+ STR
607
+
608
+ expect(actual_output).to eq(expected_output)
609
+ end
610
+ end
611
+
612
+ context "given two arrays which contain all different kinds of values, some which differ" do
613
+ it "returns a message along with the diff" do
614
+ actual_output = described_class.call(
615
+ expected: [
616
+ [
617
+ :h1,
618
+ [:span, [:text, "Hello world"]],
619
+ {
620
+ class: "header",
621
+ data: { "sticky" => true },
622
+ },
623
+ ],
624
+ ],
625
+ actual: [
626
+ [
627
+ :h2,
628
+ [:span, [:text, "Goodbye world"]],
629
+ {
630
+ id: "hero",
631
+ class: "header",
632
+ data: { "sticky" => false, role: "deprecated" },
633
+ },
634
+ ],
635
+ :br,
636
+ ],
637
+ )
638
+
639
+ expected_output = <<~STR.strip
640
+ Differing arrays.
641
+
642
+ #{
643
+ colored do
644
+ red_line %(Expected: [[:h1, [:span, [:text, "Hello world"]], { class: "header", data: { "sticky" => true } }]])
645
+ green_line %( Actual: [[:h2, [:span, [:text, "Goodbye world"]], { id: "hero", class: "header", data: { "sticky" => false, role: "deprecated" } }], :br])
646
+ end
647
+ }
648
+
649
+ Diff:
650
+
651
+ #{
652
+ colored do
653
+ plain_line %( [)
654
+ plain_line %( [)
655
+ red_line %(- :h1,)
656
+ green_line %(+ :h2,)
657
+ plain_line %( [)
658
+ plain_line %( :span,)
659
+ plain_line %( [)
660
+ plain_line %( :text,)
661
+ red_line %(- "Hello world")
662
+ green_line %(+ "Goodbye world")
663
+ plain_line %( ])
664
+ plain_line %( ],)
665
+ plain_line %( {)
666
+ plain_line %( class: "header",)
667
+ plain_line %( data: {)
668
+ red_line %(- "sticky" => true)
669
+ green_line %(+ "sticky" => false,)
670
+ green_line %(+ role: "deprecated")
671
+ plain_line %( },)
672
+ green_line %(+ id: "hero")
673
+ plain_line %( })
674
+ plain_line %( ],)
675
+ green_line %(+ :br)
676
+ plain_line %( ])
677
+ end
678
+ }
679
+ STR
680
+
681
+ expect(actual_output).to eq(expected_output)
682
+ end
683
+ end
684
+
685
+ context "given the same hash" do
686
+ it "returns an empty string" do
687
+ output = described_class.call(
688
+ expected: { name: "Marty" },
689
+ actual: { name: "Marty" },
690
+ )
691
+
692
+ expect(output).to eq("")
693
+ end
694
+ end
695
+
696
+ context "given two equal-size, one-dimensional hashes where the same key has differing numbers" do
697
+ it "returns a message along with the diff" do
698
+ actual_output = described_class.call(
699
+ expected: { tall: 12, grande: 19, venti: 20 },
700
+ actual: { tall: 12, grande: 16, venti: 20 },
701
+ )
702
+
703
+ expected_output = <<~STR.strip
704
+ Differing hashes.
705
+
706
+ #{
707
+ colored do
708
+ red_line %(Expected: { tall: 12, grande: 19, venti: 20 })
709
+ green_line %( Actual: { tall: 12, grande: 16, venti: 20 })
710
+ end
711
+ }
712
+
713
+ Diff:
714
+
715
+ #{
716
+ colored do
717
+ plain_line %( {)
718
+ plain_line %( tall: 12,)
719
+ red_line %(- grande: 19,)
720
+ green_line %(+ grande: 16,)
721
+ plain_line %( venti: 20)
722
+ plain_line %( })
723
+ end
724
+ }
725
+ STR
726
+
727
+ expect(actual_output).to eq(expected_output)
728
+ end
729
+ end
730
+
731
+ context "given two equal-size, one-dimensional hashes where keys are strings and the same key has differing numbers" do
732
+ it "returns a message along with the diff" do
733
+ actual_output = described_class.call(
734
+ expected: { "tall" => 12, "grande" => 19, "venti" => 20 },
735
+ actual: { "tall" => 12, "grande" => 16, "venti" => 20 },
736
+ )
737
+
738
+ expected_output = <<~STR.strip
739
+ Differing hashes.
740
+
741
+ #{
742
+ colored do
743
+ red_line %(Expected: { "tall" => 12, "grande" => 19, "venti" => 20 })
744
+ green_line %( Actual: { "tall" => 12, "grande" => 16, "venti" => 20 })
745
+ end
746
+ }
747
+
748
+ Diff:
749
+
750
+ #{
751
+ colored do
752
+ plain_line %( {)
753
+ plain_line %( "tall" => 12,)
754
+ red_line %(- "grande" => 19,)
755
+ green_line %(+ "grande" => 16,)
756
+ plain_line %( "venti" => 20)
757
+ plain_line %( })
758
+ end
759
+ }
760
+ STR
761
+
762
+ expect(actual_output).to eq(expected_output)
763
+ end
764
+ end
765
+
766
+ context "given two equal-size, one-dimensional hashes where the same key has differing symbols" do
767
+ it "returns a message along with the diff" do
768
+ actual_output = described_class.call(
769
+ expected: { tall: :small, grande: :grand, venti: :large },
770
+ actual: { tall: :small, grande: :medium, venti: :large },
771
+ )
772
+
773
+ expected_output = <<~STR.strip
774
+ Differing hashes.
775
+
776
+ #{
777
+ colored do
778
+ red_line %(Expected: { tall: :small, grande: :grand, venti: :large })
779
+ green_line %( Actual: { tall: :small, grande: :medium, venti: :large })
780
+ end
781
+ }
782
+
783
+ Diff:
784
+
785
+ #{
786
+ colored do
787
+ plain_line %( {)
788
+ plain_line %( tall: :small,)
789
+ red_line %(- grande: :grand,)
790
+ green_line %(+ grande: :medium,)
791
+ plain_line %( venti: :large)
792
+ plain_line %( })
793
+ end
794
+ }
795
+ STR
796
+
797
+ expect(actual_output).to eq(expected_output)
798
+ end
799
+ end
800
+
801
+ context "given two equal-size, one-dimensional hashes where the same key has differing strings" do
802
+ it "returns a message along with the diff" do
803
+ actual_output = described_class.call(
804
+ expected: { tall: "small", grande: "grand", venti: "large" },
805
+ actual: { tall: "small", grande: "medium", venti: "large" },
806
+ )
807
+
808
+ expected_output = <<~STR.strip
809
+ Differing hashes.
810
+
811
+ #{
812
+ colored do
813
+ red_line %(Expected: { tall: "small", grande: "grand", venti: "large" })
814
+ green_line %( Actual: { tall: "small", grande: "medium", venti: "large" })
815
+ end
816
+ }
817
+
818
+ Diff:
819
+
820
+ #{
821
+ colored do
822
+ plain_line %( {)
823
+ plain_line %( tall: "small",)
824
+ red_line %(- grande: "grand",)
825
+ green_line %(+ grande: "medium",)
826
+ plain_line %( venti: "large")
827
+ plain_line %( })
828
+ end
829
+ }
830
+ STR
831
+
832
+ expect(actual_output).to eq(expected_output)
833
+ end
834
+ end
835
+
836
+ context "given two equal-size, one-dimensional hashes where the same key has differing objects" do
837
+ it "returns a message along with the diff" do
838
+ actual_output = described_class.call(
839
+ expected: {
840
+ steve: SuperDiff::Test::Person.new(name: "Jobs"),
841
+ susan: SuperDiff::Test::Person.new(name: "Kare"),
842
+ },
843
+ actual: {
844
+ steve: SuperDiff::Test::Person.new(name: "Wozniak"),
845
+ susan: SuperDiff::Test::Person.new(name: "Kare"),
846
+ },
847
+ extra_operational_sequencer_classes: [
848
+ SuperDiff::Test::PersonOperationalSequencer,
849
+ ],
850
+ extra_diff_formatter_classes: [
851
+ SuperDiff::Test::PersonDiffFormatter,
852
+ ],
853
+ )
854
+
855
+ expected_output = <<~STR.strip
856
+ Differing hashes.
857
+
858
+ #{
859
+ colored do
860
+ red_line %(Expected: { steve: #<SuperDiff::Test::Person name: "Jobs">, susan: #<SuperDiff::Test::Person name: "Kare"> })
861
+ green_line %( Actual: { steve: #<SuperDiff::Test::Person name: "Wozniak">, susan: #<SuperDiff::Test::Person name: "Kare"> })
862
+ end
863
+ }
864
+
865
+ Diff:
866
+
867
+ #{
868
+ colored do
869
+ plain_line %( {)
870
+ plain_line %( steve: #<SuperDiff::Test::Person {)
871
+ red_line %(- name: "Jobs")
872
+ green_line %(+ name: "Wozniak")
873
+ plain_line %( }>,)
874
+ plain_line %( susan: #<SuperDiff::Test::Person {)
875
+ plain_line %( name: "Kare")
876
+ plain_line %( }>)
877
+ plain_line %( })
878
+ end
879
+ }
880
+ STR
881
+
882
+ expect(actual_output).to eq(expected_output)
883
+ end
884
+ end
885
+
886
+ context "given two equal-size, one-dimensional hashes where the actual has extra keys" do
887
+ it "returns a message along with the diff" do
888
+ actual_output = described_class.call(
889
+ expected: { latte: 4.5 },
890
+ actual: { latte: 4.5, mocha: 3.5, cortado: 3 },
891
+ )
892
+
893
+ expected_output = <<~STR.strip
894
+ Differing hashes.
895
+
896
+ #{
897
+ colored do
898
+ red_line %(Expected: { latte: 4.5 })
899
+ green_line %( Actual: { latte: 4.5, mocha: 3.5, cortado: 3 })
900
+ end
901
+ }
902
+
903
+ Diff:
904
+
905
+ #{
906
+ colored do
907
+ plain_line %( {)
908
+ plain_line %( latte: 4.5,)
909
+ green_line %(+ mocha: 3.5,)
910
+ green_line %(+ cortado: 3)
911
+ plain_line %( })
912
+ end
913
+ }
914
+ STR
915
+
916
+ expect(actual_output).to eq(expected_output)
917
+ end
918
+ end
919
+
920
+ context "given two equal-size, one-dimensional hashes where the actual has missing keys" do
921
+ it "returns a message along with the diff" do
922
+ actual_output = described_class.call(
923
+ expected: { latte: 4.5, mocha: 3.5, cortado: 3 },
924
+ actual: { latte: 4.5 },
925
+ )
926
+
927
+ expected_output = <<~STR.strip
928
+ Differing hashes.
929
+
930
+ #{
931
+ colored do
932
+ red_line %(Expected: { latte: 4.5, mocha: 3.5, cortado: 3 })
933
+ green_line %( Actual: { latte: 4.5 })
934
+ end
935
+ }
936
+
937
+ Diff:
938
+
939
+ #{
940
+ colored do
941
+ plain_line %( {)
942
+ plain_line %( latte: 4.5)
943
+ red_line %(- mocha: 3.5,)
944
+ red_line %(- cortado: 3)
945
+ plain_line %( })
946
+ end
947
+ }
948
+ STR
949
+
950
+ expect(actual_output).to eq(expected_output)
951
+ end
952
+ end
953
+
954
+ context "given two hashes containing arrays with differing values" do
955
+ it "returns a message along with the diff" do
956
+ actual_output = described_class.call(
957
+ expected: {
958
+ name: "Elliot",
959
+ interests: ["music", "football", "programming"],
960
+ age: 30,
961
+ },
962
+ actual: {
963
+ name: "Elliot",
964
+ interests: ["music", "travel", "programming"],
965
+ age: 30,
966
+ },
967
+ )
968
+
969
+ expected_output = <<~STR.strip
970
+ Differing hashes.
971
+
972
+ #{
973
+ colored do
974
+ red_line %(Expected: { name: "Elliot", interests: ["music", "football", "programming"], age: 30 })
975
+ green_line %( Actual: { name: "Elliot", interests: ["music", "travel", "programming"], age: 30 })
976
+ end
977
+ }
978
+
979
+ Diff:
980
+
981
+ #{
982
+ colored do
983
+ plain_line %( {)
984
+ plain_line %( name: "Elliot",)
985
+ plain_line %( interests: [)
986
+ plain_line %( "music",)
987
+ red_line %(- "football",)
988
+ green_line %(+ "travel",)
989
+ plain_line %( "programming")
990
+ plain_line %( ],)
991
+ plain_line %( age: 30)
992
+ plain_line %( })
993
+ end
994
+ }
995
+ STR
996
+
997
+ expect(actual_output).to eq(expected_output)
998
+ end
999
+ end
1000
+
1001
+ context "given two hashes containing hashes with differing values" do
1002
+ it "returns a message along with the diff" do
1003
+ actual_output = described_class.call(
1004
+ expected: {
1005
+ check_spelling: true,
1006
+ substitutions: {
1007
+ "YOLO" => "You only live once",
1008
+ "BRB" => "Buns, ribs, and bacon",
1009
+ "YMMV" => "Your mileage may vary",
1010
+ },
1011
+ check_grammar: false,
1012
+ },
1013
+ actual: {
1014
+ check_spelling: true,
1015
+ substitutions: {
1016
+ "YOLO" => "You only live once",
1017
+ "BRB" => "Be right back",
1018
+ "YMMV" => "Your mileage may vary",
1019
+ },
1020
+ check_grammar: false,
1021
+ },
1022
+ )
1023
+
1024
+ expected_output = <<~STR.strip
1025
+ Differing hashes.
1026
+
1027
+ #{
1028
+ colored do
1029
+ red_line %(Expected: { check_spelling: true, substitutions: { "YOLO" => "You only live once", "BRB" => "Buns, ribs, and bacon", "YMMV" => "Your mileage may vary" }, check_grammar: false })
1030
+ green_line %( Actual: { check_spelling: true, substitutions: { "YOLO" => "You only live once", "BRB" => "Be right back", "YMMV" => "Your mileage may vary" }, check_grammar: false })
1031
+ end
1032
+ }
1033
+
1034
+ Diff:
1035
+
1036
+ #{
1037
+ colored do
1038
+ plain_line %( {)
1039
+ plain_line %( check_spelling: true,)
1040
+ plain_line %( substitutions: {)
1041
+ plain_line %( "YOLO" => "You only live once",)
1042
+ red_line %(- "BRB" => "Buns, ribs, and bacon",)
1043
+ green_line %(+ "BRB" => "Be right back",)
1044
+ plain_line %( "YMMV" => "Your mileage may vary")
1045
+ plain_line %( },)
1046
+ plain_line %( check_grammar: false)
1047
+ plain_line %( })
1048
+ end
1049
+ }
1050
+ STR
1051
+
1052
+ expect(actual_output).to eq(expected_output)
1053
+ end
1054
+ end
1055
+
1056
+ context "given two hashes containing custom objects with differing attributes" do
1057
+ it "returns a message along with the diff" do
1058
+ actual_output = described_class.call(
1059
+ expected: {
1060
+ order_id: 1234,
1061
+ person: SuperDiff::Test::Person.new(name: "Marty"),
1062
+ amount: 350_00,
1063
+ },
1064
+ actual: {
1065
+ order_id: 1234,
1066
+ person: SuperDiff::Test::Person.new(name: "Doc"),
1067
+ amount: 350_00,
1068
+ },
1069
+ extra_operational_sequencer_classes: [
1070
+ SuperDiff::Test::PersonOperationalSequencer,
1071
+ ],
1072
+ extra_diff_formatter_classes: [
1073
+ SuperDiff::Test::PersonDiffFormatter,
1074
+ ],
1075
+ )
1076
+
1077
+ expected_output = <<~STR.strip
1078
+ Differing hashes.
1079
+
1080
+ #{
1081
+ colored do
1082
+ red_line %(Expected: { order_id: 1234, person: #<SuperDiff::Test::Person name: "Marty">, amount: 35000 })
1083
+ green_line %( Actual: { order_id: 1234, person: #<SuperDiff::Test::Person name: "Doc">, amount: 35000 })
1084
+ end
1085
+ }
1086
+
1087
+ Diff:
1088
+
1089
+ #{
1090
+ colored do
1091
+ plain_line %( {)
1092
+ plain_line %( order_id: 1234,)
1093
+ plain_line %( person: #<SuperDiff::Test::Person {)
1094
+ red_line %(- name: "Marty")
1095
+ green_line %(+ name: "Doc")
1096
+ plain_line %( }>,)
1097
+ plain_line %( amount: 35000)
1098
+ plain_line %( })
1099
+ end
1100
+ }
1101
+ STR
1102
+
1103
+ expect(actual_output).to eq(expected_output)
1104
+ end
1105
+ end
1106
+
1107
+ context "given two hashes which contain all different kinds of values, some which differ" do
1108
+ it "returns a message along with the diff" do
1109
+ actual_output = described_class.call(
1110
+ expected: {
1111
+ customer: {
1112
+ name: "Marty McFly",
1113
+ shipping_address: {
1114
+ line_1: "123 Main St.",
1115
+ city: "Hill Valley",
1116
+ state: "CA",
1117
+ zip: "90382",
1118
+ },
1119
+ },
1120
+ items: [
1121
+ {
1122
+ name: "Fender Stratocaster",
1123
+ cost: 100_000,
1124
+ options: ["red", "blue", "green"],
1125
+ },
1126
+ { name: "Chevy 4x4" },
1127
+ ],
1128
+ },
1129
+ actual: {
1130
+ customer: {
1131
+ name: "Marty McFly, Jr.",
1132
+ shipping_address: {
1133
+ line_1: "456 Ponderosa Ct.",
1134
+ city: "Hill Valley",
1135
+ state: "CA",
1136
+ zip: "90382",
1137
+ },
1138
+ },
1139
+ items: [
1140
+ {
1141
+ name: "Fender Stratocaster",
1142
+ cost: 100_000,
1143
+ options: ["red", "blue", "green"],
1144
+ },
1145
+ { name: "Mattel Hoverboard" },
1146
+ ],
1147
+ },
1148
+ )
1149
+
1150
+ expected_output = <<~STR.strip
1151
+ Differing hashes.
1152
+
1153
+ #{
1154
+ colored do
1155
+ red_line %(Expected: { customer: { name: "Marty McFly", shipping_address: { line_1: "123 Main St.", city: "Hill Valley", state: "CA", zip: "90382" } }, items: [{ name: "Fender Stratocaster", cost: 100000, options: ["red", "blue", "green"] }, { name: "Chevy 4x4" }] })
1156
+ green_line %( Actual: { customer: { name: "Marty McFly, Jr.", shipping_address: { line_1: "456 Ponderosa Ct.", city: "Hill Valley", state: "CA", zip: "90382" } }, items: [{ name: "Fender Stratocaster", cost: 100000, options: ["red", "blue", "green"] }, { name: "Mattel Hoverboard" }] })
1157
+ end
1158
+ }
1159
+
1160
+ Diff:
1161
+
1162
+ #{
1163
+ colored do
1164
+ plain_line %( {)
1165
+ plain_line %( customer: {)
1166
+ red_line %(- name: "Marty McFly",)
1167
+ green_line %(+ name: "Marty McFly, Jr.",)
1168
+ plain_line %( shipping_address: {)
1169
+ red_line %(- line_1: "123 Main St.",)
1170
+ green_line %(+ line_1: "456 Ponderosa Ct.",)
1171
+ plain_line %( city: "Hill Valley",)
1172
+ plain_line %( state: "CA",)
1173
+ plain_line %( zip: "90382")
1174
+ plain_line %( })
1175
+ plain_line %( },)
1176
+ plain_line %( items: [)
1177
+ plain_line %( {)
1178
+ plain_line %( name: "Fender Stratocaster",)
1179
+ plain_line %( cost: 100000,)
1180
+ plain_line %( options: ["red", "blue", "green"])
1181
+ plain_line %( },)
1182
+ plain_line %( {)
1183
+ red_line %(- name: "Chevy 4x4")
1184
+ green_line %(+ name: "Mattel Hoverboard")
1185
+ plain_line %( })
1186
+ plain_line %( ])
1187
+ plain_line %( })
1188
+ end
1189
+ }
1190
+ STR
1191
+
1192
+ expect(actual_output).to eq(expected_output)
1193
+ end
1194
+ end
1195
+
1196
+ context "given two objects which == each other" do
1197
+ it "returns an empty string" do
1198
+ expected = SuperDiff::Test::Person.new(name: "Marty")
1199
+ actual = SuperDiff::Test::Person.new(name: "Marty")
1200
+
1201
+ output = described_class.call(expected: expected, actual: actual)
1202
+
1203
+ expect(output).to eq("")
1204
+ end
1205
+ end
1206
+
1207
+ context "given two objects which do not == each other" do
1208
+ it "returns a message along with a comparison" do
1209
+ expected = SuperDiff::Test::Person.new(name: "Marty")
1210
+ actual = SuperDiff::Test::Person.new(name: "Doc")
1211
+
1212
+ actual_output = described_class.call(expected: expected, actual: actual)
1213
+
1214
+ expected_output = <<~STR.strip
1215
+ Differing objects.
1216
+
1217
+ #{
1218
+ colored do
1219
+ red_line %(Expected: #<SuperDiff::Test::Person name: "Marty">)
1220
+ green_line %( Actual: #<SuperDiff::Test::Person name: "Doc">)
1221
+ end
1222
+ }
1223
+ STR
1224
+
1225
+ expect(actual_output).to eq(expected_output)
1226
+ end
1227
+ end
1228
+ end
1229
+
1230
+ def colored(&block)
1231
+ SuperDiff::Tests::Colorizer.call(&block).chomp
1232
+ end
1233
+ end