1
- import type { JsonValue , ObjectHashContext , Patch } from './index' ;
2
- import { generateJSONPatch , pathInfo } from './index' ;
3
- import { applyPatch , deepClone } from 'fast-json-patch' ;
4
- import { assert , expect } from 'chai' ;
1
+ import type { JsonValue , ObjectHashContext , Patch } from './index' ;
2
+ import { generateJSONPatch , pathInfo } from './index' ;
3
+ import { applyPatch , deepClone } from 'fast-json-patch' ;
4
+ import { assert , expect } from 'chai' ;
5
5
6
6
type Title = string ;
7
7
type Before = JsonValue ;
@@ -18,9 +18,9 @@ const jsonValues = {
18
18
primitiveNumberZero : 0 ,
19
19
primitiveBooleanTrue : true ,
20
20
primitiveBooleanFalse : false ,
21
- jsonObjectWithFlatPropertiesAndStringValues : { a : 'a' , b : 'b' , c : 'c' } ,
22
- jsonObjectWithFlatPropertiesAndNumberValues : { a : 3 , b : 2 , c : 1 } ,
23
- jsonObjectWithFlatPropertiesAndMixedValues : { a : true , b : 'b' , c : 12 } ,
21
+ jsonObjectWithFlatPropertiesAndStringValues : { a : 'a' , b : 'b' , c : 'c' } ,
22
+ jsonObjectWithFlatPropertiesAndNumberValues : { a : 3 , b : 2 , c : 1 } ,
23
+ jsonObjectWithFlatPropertiesAndMixedValues : { a : true , b : 'b' , c : 12 } ,
24
24
} as const ;
25
25
26
26
describe ( 'a generate json patch function' , ( ) => {
@@ -47,12 +47,12 @@ describe('a generate json patch function', () => {
47
47
'adds root array elements' ,
48
48
[ 1 , 2 , 3 ] ,
49
49
[ 1 , 2 , 3 , 4 ] ,
50
- [ { op : 'add' , path : '/3' , value : 4 } ] ,
50
+ [ { op : 'add' , path : '/3' , value : 4 } ] ,
51
51
] ,
52
52
[
53
53
'adds root object property' ,
54
- { a : 'a' , b : 'b' } ,
55
- { a : 'a' , b : 'b' , c : 'c' } ,
54
+ { a : 'a' , b : 'b' } ,
55
+ { a : 'a' , b : 'b' , c : 'c' } ,
56
56
[
57
57
{
58
58
op : 'add' ,
@@ -65,38 +65,38 @@ describe('a generate json patch function', () => {
65
65
'removes root array elements' ,
66
66
[ 1 , 2 , 3 , 4 ] ,
67
67
[ 1 , 2 , 3 ] ,
68
- [ { op : 'remove' , path : '/3' } ] ,
68
+ [ { op : 'remove' , path : '/3' } ] ,
69
69
] ,
70
70
[
71
71
'removes root object property' ,
72
- { a : 'a' , b : 'b' , c : 'c' } ,
73
- { a : 'a' , b : 'b' } ,
74
- [ { op : 'remove' , path : '/c' } ] ,
72
+ { a : 'a' , b : 'b' , c : 'c' } ,
73
+ { a : 'a' , b : 'b' } ,
74
+ [ { op : 'remove' , path : '/c' } ] ,
75
75
] ,
76
76
[
77
77
'replaces root number values' ,
78
78
1 ,
79
79
2 ,
80
- [ { op : 'replace' , path : '' , value : 2 } ] ,
80
+ [ { op : 'replace' , path : '' , value : 2 } ] ,
81
81
] ,
82
82
[
83
83
'replaces root string values' ,
84
84
'hello' ,
85
85
'world' ,
86
- [ { op : 'replace' , path : '' , value : 'world' } ] ,
86
+ [ { op : 'replace' , path : '' , value : 'world' } ] ,
87
87
] ,
88
88
[
89
89
'replaces root boolean values' ,
90
90
true ,
91
91
false ,
92
- [ { op : 'replace' , path : '' , value : false } ] ,
92
+ [ { op : 'replace' , path : '' , value : false } ] ,
93
93
] ,
94
94
95
95
[ 'replaces root empty arrays' , [ ] , [ ] , [ ] ] ,
96
96
[
97
97
'replaces root object property' ,
98
- { a : 'a' , b : 'b' } ,
99
- { a : 'a' , b : 'c' } ,
98
+ { a : 'a' , b : 'b' } ,
99
+ { a : 'a' , b : 'c' } ,
100
100
[
101
101
{
102
102
op : 'replace' ,
@@ -109,12 +109,12 @@ describe('a generate json patch function', () => {
109
109
'replaces root array elements' ,
110
110
[ 1 , 2 , 3 ] ,
111
111
[ 1 , 2 , 4 ] ,
112
- [ { op : 'replace' , path : '/2' , value : 4 } ] ,
112
+ [ { op : 'replace' , path : '/2' , value : 4 } ] ,
113
113
] ,
114
114
[
115
115
'replaces an obj prop with an array property' ,
116
- { prop : { hello : 'world' } } ,
117
- { prop : [ 'hello' , 'world' ] } ,
116
+ { prop : { hello : 'world' } } ,
117
+ { prop : [ 'hello' , 'world' ] } ,
118
118
[
119
119
{
120
120
op : 'replace' ,
@@ -125,20 +125,20 @@ describe('a generate json patch function', () => {
125
125
] ,
126
126
[
127
127
'replaces an array prop with an obj property' ,
128
- { prop : [ 'hello' , 'world' ] } ,
129
- { prop : { hello : 'world' } } ,
128
+ { prop : [ 'hello' , 'world' ] } ,
129
+ { prop : { hello : 'world' } } ,
130
130
[
131
131
{
132
132
op : 'replace' ,
133
133
path : '/prop' ,
134
- value : { hello : 'world' } ,
134
+ value : { hello : 'world' } ,
135
135
} ,
136
136
] ,
137
137
] ,
138
138
[
139
139
'replaces a deep nested object property' ,
140
- { root : { first : { second : { third : 'before' } } } } ,
141
- { root : { first : { second : { third : 'after' } } } } ,
140
+ { root : { first : { second : { third : 'before' } } } } ,
141
+ { root : { first : { second : { third : 'after' } } } } ,
142
142
[
143
143
{
144
144
op : 'replace' ,
@@ -153,13 +153,13 @@ describe('a generate json patch function', () => {
153
153
root : {
154
154
first : [
155
155
{ } ,
156
- { second : { third : 'before' , list : [ 'hello' , 'world' ] } } ,
156
+ { second : { third : 'before' , list : [ 'hello' , 'world' ] } } ,
157
157
] ,
158
158
} ,
159
159
} ,
160
160
{
161
161
root : {
162
- first : [ { } , { second : { third : 'after' , list : [ 'hello' , 'world' ] } } ] ,
162
+ first : [ { } , { second : { third : 'after' , list : [ 'hello' , 'world' ] } } ] ,
163
163
} ,
164
164
} ,
165
165
[
@@ -172,8 +172,8 @@ describe('a generate json patch function', () => {
172
172
] ,
173
173
[
174
174
'detects several changes on arrays by reference' ,
175
- { root : [ { id : 1 } , { id : 2 } , { id : 3 } , { id : 4 } ] } ,
176
- { root : [ { id : 4 } , { id : 3 } , { id : 2 } ] } ,
175
+ { root : [ { id : 1 } , { id : 2 } , { id : 3 } , { id : 4 } ] } ,
176
+ { root : [ { id : 4 } , { id : 3 } , { id : 2 } ] } ,
177
177
[
178
178
{
179
179
op : 'remove' ,
@@ -210,8 +210,8 @@ describe('a generate json patch function', () => {
210
210
211
211
describe ( 'with an array value hash function' , ( ) => {
212
212
it ( 'throws when objectHash is not a function' , ( ) => {
213
- const before = [ { id : 1 , paramOne : 'before' } ] ;
214
- const after = [ { id : 2 , paramOne : 'after' } ] ;
213
+ const before = [ { id : 1 , paramOne : 'before' } ] ;
214
+ const after = [ { id : 2 , paramOne : 'after' } ] ;
215
215
216
216
assert . throws ( ( ) =>
217
217
generateJSONPatch ( before , after , {
@@ -223,12 +223,12 @@ describe('a generate json patch function', () => {
223
223
224
224
it ( 'handles changes with change and move on the same property' , ( ) => {
225
225
const before = [
226
- { id : 1 , paramOne : 'future' , paramTwo : 'past' } ,
227
- { id : 2 , paramOne : 'current' } ,
226
+ { id : 1 , paramOne : 'future' , paramTwo : 'past' } ,
227
+ { id : 2 , paramOne : 'current' } ,
228
228
] ;
229
229
const after = [
230
- { id : 2 , paramOne : 'current' } ,
231
- { id : 1 , paramOne : 'current' } ,
230
+ { id : 2 , paramOne : 'current' } ,
231
+ { id : 1 , paramOne : 'current' } ,
232
232
] ;
233
233
234
234
const patch = generateJSONPatch ( before , after , {
@@ -239,19 +239,19 @@ describe('a generate json patch function', () => {
239
239
240
240
const patched = doPatch ( before , patch ) ;
241
241
expect ( patched ) . to . be . eql ( [
242
- { id : 2 , paramOne : 'current' } ,
243
- { id : 1 , paramOne : 'current' } ,
242
+ { id : 2 , paramOne : 'current' } ,
243
+ { id : 1 , paramOne : 'current' } ,
244
244
] ) ;
245
245
} ) ;
246
246
247
247
it ( 'handles changes on array objects with different shape' , ( ) => {
248
- const before = [ { id : 1 , paramOne : 'current' } ] ;
248
+ const before = [ { id : 1 , paramOne : 'current' } ] ;
249
249
const after = [
250
250
{
251
251
id : 1 ,
252
252
paramOne : 'future' ,
253
253
paramTwo : 'past' ,
254
- paramThree : { nested : 'some text' } ,
254
+ paramThree : { nested : 'some text' } ,
255
255
} ,
256
256
] ;
257
257
@@ -268,7 +268,7 @@ describe('a generate json patch function', () => {
268
268
id : 1 ,
269
269
paramOne : 'future' ,
270
270
paramTwo : 'past' ,
271
- paramThree : { nested : 'some text' } ,
271
+ paramThree : { nested : 'some text' } ,
272
272
} ,
273
273
] ) ;
274
274
} ) ;
@@ -487,7 +487,7 @@ describe('a generate json patch function', () => {
487
487
objectHash : function ( obj : any ) {
488
488
return `${ obj . id } ` ;
489
489
} ,
490
- array : { ignoreMove : true } ,
490
+ array : { ignoreMove : true } ,
491
491
} ) ;
492
492
493
493
const patched = doPatch ( before , patch ) ;
@@ -514,10 +514,10 @@ describe('a generate json patch function', () => {
514
514
type : 'Granada' ,
515
515
colors : [ 'red' , 'silver' , 'yellow' ] ,
516
516
engine : [
517
- { name : 'Cologne V6 2.6' , hp : 125 } ,
518
- { name : 'Cologne V6 2.0' , hp : 90 } ,
519
- { name : 'Cologne V6 2.3' , hp : 108 } ,
520
- { name : 'Essex V6 3.0' , hp : 150 } ,
517
+ { name : 'Cologne V6 2.6' , hp : 125 } ,
518
+ { name : 'Cologne V6 2.0' , hp : 90 } ,
519
+ { name : 'Cologne V6 2.3' , hp : 108 } ,
520
+ { name : 'Essex V6 3.0' , hp : 150 } ,
521
521
] ,
522
522
} ;
523
523
@@ -526,16 +526,16 @@ describe('a generate json patch function', () => {
526
526
type : 'Granada' ,
527
527
colors : [ 'red' , 'silver' , 'yellow' ] ,
528
528
engine : [
529
- { name : 'Essex V6 3.0' , hp : 138 } ,
530
- { name : 'Cologne V6 2.6' , hp : 125 } ,
531
- { name : 'Cologne V6 2.0' , hp : 90 } ,
532
- { name : 'Cologne V6 2.3' , hp : 108 } ,
529
+ { name : 'Essex V6 3.0' , hp : 138 } ,
530
+ { name : 'Cologne V6 2.6' , hp : 125 } ,
531
+ { name : 'Cologne V6 2.0' , hp : 90 } ,
532
+ { name : 'Cologne V6 2.3' , hp : 108 } ,
533
533
] ,
534
534
} ;
535
535
536
536
const patch = generateJSONPatch ( before , after , {
537
537
objectHash : function ( value : JsonValue , context : ObjectHashContext ) {
538
- const { length, last } = pathInfo ( context . path ) ;
538
+ const { length, last} = pathInfo ( context . path ) ;
539
539
if ( length === 2 && last === 'engine' ) {
540
540
// @ts -ignore
541
541
return value ?. name ;
@@ -548,8 +548,8 @@ describe('a generate json patch function', () => {
548
548
expect ( patched ) . to . be . eql ( after ) ;
549
549
550
550
expect ( patch ) . to . be . eql ( [
551
- { op : 'replace' , path : '/engine/3/hp' , value : 138 } ,
552
- { op : 'move' , from : '/engine/3' , path : '/engine/0' } ,
551
+ { op : 'replace' , path : '/engine/3/hp' , value : 138 } ,
552
+ { op : 'move' , from : '/engine/3' , path : '/engine/0' } ,
553
553
] ) ;
554
554
} ) ;
555
555
} ) ;
@@ -584,7 +584,7 @@ describe('a generate json patch function', () => {
584
584
expect ( patched ) . to . be . eql ( {
585
585
id : 1 ,
586
586
paramOne : 'after' ,
587
- paramTwo : { ignoreMe : 'before' , doNotIgnoreMe : 'after' } ,
587
+ paramTwo : { ignoreMe : 'before' , doNotIgnoreMe : 'after' } ,
588
588
} ) ;
589
589
} ) ;
590
590
@@ -627,14 +627,14 @@ describe('a generate json patch function', () => {
627
627
paramTwo : {
628
628
ignoreMe : 'before' ,
629
629
doNotIgnoreMe : 'after' ,
630
- two : { ignoreMe : 'after' } ,
630
+ two : { ignoreMe : 'after' } ,
631
631
} ,
632
632
} ) ;
633
633
634
634
expect ( patch ) . to . eql ( [
635
- { op : 'replace' , path : '/paramOne' , value : 'after' } ,
636
- { op : 'replace' , path : '/paramTwo/doNotIgnoreMe' , value : 'after' } ,
637
- { op : 'replace' , path : '/paramTwo/two/ignoreMe' , value : 'after' } ,
635
+ { op : 'replace' , path : '/paramOne' , value : 'after' } ,
636
+ { op : 'replace' , path : '/paramTwo/doNotIgnoreMe' , value : 'after' } ,
637
+ { op : 'replace' , path : '/paramTwo/two/ignoreMe' , value : 'after' } ,
638
638
] ) ;
639
639
} ) ;
640
640
@@ -648,8 +648,8 @@ describe('a generate json patch function', () => {
648
648
expect ( patched ) . to . be . eql ( [ 1 ] ) ;
649
649
650
650
expect ( patch ) . to . eql ( [
651
- { op : 'remove' , path : '/2' } ,
652
- { op : 'remove' , path : '/1' } ,
651
+ { op : 'remove' , path : '/2' } ,
652
+ { op : 'remove' , path : '/1' } ,
653
653
] ) ;
654
654
} ) ;
655
655
} ) ;
@@ -680,7 +680,7 @@ describe('a generate json patch function', () => {
680
680
} ;
681
681
682
682
it ( 'detects changes as a given depth of 3' , ( ) => {
683
- const patch = generateJSONPatch ( before , after , { maxDepth : 3 } ) ;
683
+ const patch = generateJSONPatch ( before , after , { maxDepth : 3 } ) ;
684
684
expect ( patch ) . to . eql ( [
685
685
{
686
686
op : 'replace' ,
@@ -696,10 +696,45 @@ describe('a generate json patch function', () => {
696
696
] ) ;
697
697
} ) ;
698
698
699
+ it ( 'creates empty patch for arrays with object hash' , ( ) => {
700
+ const before = {
701
+ obj : {
702
+ arrayField : [ { nested : { id : 'one' , value : 'hello' } } , {
703
+ nested : {
704
+ id : 'two' ,
705
+ value : 'world'
706
+ }
707
+ } ]
708
+ }
709
+ } ;
710
+ const after = {
711
+ obj : {
712
+ arrayField : [ { nested : { value : 'hello' , id : 'one' , } } , {
713
+ nested : {
714
+ id : 'two' ,
715
+ value : 'world'
716
+ }
717
+ } ]
718
+ }
719
+ } ;
720
+
721
+ const patch = generateJSONPatch ( before , after , {
722
+ maxDepth : 3 ,
723
+ objectHash : function ( obj , context ) {
724
+ if ( context . path === '/obj/arrayField' ) {
725
+ // @ts -ignore
726
+ return obj . nested . id ;
727
+ }
728
+ return context . index . toString ( ) ;
729
+ } ,
730
+ } ) ;
731
+ expect ( patch ) . to . eql ( [ ] ) ;
732
+ } ) ;
733
+
699
734
it ( 'detects changes as a given depth of 4' , ( ) => {
700
735
const afterModified = structuredClone ( after ) ;
701
736
afterModified . firstLevel . secondLevel . thirdLevelTwo = 'hello-world' ;
702
- const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
737
+ const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
703
738
expect ( patch ) . to . eql ( [
704
739
{
705
740
op : 'replace' ,
@@ -719,7 +754,7 @@ describe('a generate json patch function', () => {
719
754
it ( 'detects changes as a given depth of 4 for an array value' , ( ) => {
720
755
const afterModified = structuredClone ( before ) ;
721
756
afterModified . firstLevel . secondLevel . thirdLevelThree = [ 'test' ] ;
722
- const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
757
+ const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
723
758
expect ( patch ) . to . eql ( [
724
759
{
725
760
op : 'replace' ,
@@ -733,7 +768,7 @@ describe('a generate json patch function', () => {
733
768
const afterModified = structuredClone ( before ) ;
734
769
// @ts -ignore
735
770
delete afterModified . firstLevel . secondLevel . thirdLevelThree ;
736
- const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
771
+ const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
737
772
expect ( patch ) . to . eql ( [
738
773
{
739
774
op : 'remove' ,
@@ -746,7 +781,7 @@ describe('a generate json patch function', () => {
746
781
const afterModified = structuredClone ( before ) ;
747
782
// @ts -ignore
748
783
afterModified . firstLevel . secondLevel . thirdLevelThree = null ;
749
- const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
784
+ const patch = generateJSONPatch ( before , afterModified , { maxDepth : 4 } ) ;
750
785
expect ( patch ) . to . eql ( [
751
786
{
752
787
op : 'replace' ,
0 commit comments