Browse Source

feat: add granular output

pull/10/head
DoneDeal0 2 years ago
parent
commit
24faa25315
  1. 74
      README.md
  2. 19
      dist/index.d.ts
  3. 231
      dist/index.js
  4. 229
      dist/index.mjs
  5. 53
      src/list-diff.ts
  6. 64
      src/model.ts
  7. 89
      src/object-diff.ts
  8. 6
      src/utils.ts
  9. 60
      test/list-diff.test.ts
  10. 150
      test/object-diff.test.ts
  11. 20
      test/utils.test.ts

74
README.md

@ -97,25 +97,25 @@ const objectB = {
+ status: "updated", + status: "updated",
subPropertiesDiff: [ subPropertiesDiff: [
{ {
name: "name", property: "name",
previousValue: "joe", previousValue: "joe",
currentValue: "joe", currentValue: "joe",
status: "equal", status: "equal",
}, },
+ { + {
+ name: "member", + property: "member",
+ previousValue: true, + previousValue: true,
+ currentValue: false, + currentValue: false,
+ status: "updated", + status: "updated",
+ }, + },
+ { + {
+ name: "hobbies", + property: "hobbies",
+ previousValue: ["golf", "football"], + previousValue: ["golf", "football"],
+ currentValue: ["golf", "chess"], + currentValue: ["golf", "chess"],
+ status: "updated", + status: "updated",
+ }, + },
{ {
name: "age", property: "age",
previousValue: 66, previousValue: 66,
currentValue: 66, currentValue: 66,
status: "equal", status: "equal",
@ -156,17 +156,37 @@ type ObjectDiff = {
status: "added" | "deleted" | "equal" | "moved" | "updated"; status: "added" | "deleted" | "equal" | "moved" | "updated";
// only appears if some subproperties have been added/deleted/updated // only appears if some subproperties have been added/deleted/updated
subPropertiesDiff?: { subPropertiesDiff?: {
name: string; property: string;
previousValue: any; previousValue: any;
currentValue: any; currentValue: any;
status: "added" | "deleted" | "equal" | "moved" | "updated"; status: "added" | "deleted" | "equal" | "moved" | "updated";
// subDiff is a recursive diff in case of nested subproperties // subDiff is a recursive diff in case of nested subproperties
subDiff?: Subproperties[]; subDiff?: SubProperties[];
}[]; }[];
}[]; }[];
}; };
``` ```
**Options**
```ts
{
ignoreArrayOrder?: boolean // false by default,
showOnly?: {
statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
granularity?: "basic" | "deep" // basic by default
}
}
```
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
- `showOnly`: gives you the option to only return the values whose status interest you. It has two parameters:
- `statuses`: status you want to see in the output (ex: `["added", "equal"]`)
- `granularity`:
- `basic` only returns the main properties whose status match your request, without taking into account their eventual subproperties.
- `deep` return main properties whose status match your request but also their relevant subproperties.
### getListDiff() ### getListDiff()
```js ```js
@ -197,6 +217,16 @@ type ListDiff = {
}; };
``` ```
**Options**
```ts
{
showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
}
```
- `showOnly` gives you the option to only return the values whose status interest you (ex: `["added", "equal"]`).
### isEqual() ### isEqual()
```js ```js
@ -205,6 +235,16 @@ import { isEqual } from "@donedeal0/superdiff";
Checks if two values are equal. Checks if two values are equal.
**Options**
```ts
{
ignoreArrayOrder?: boolean // false by default,
}
```
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
### isObject() ### isObject()
```js ```js
@ -329,25 +369,25 @@ output
+ status: "updated", + status: "updated",
subPropertiesDiff: [ subPropertiesDiff: [
{ {
name: "name", property: "name",
previousValue: "joe", previousValue: "joe",
currentValue: "joe", currentValue: "joe",
status: "equal", status: "equal",
}, },
+ { + {
+ name: "member", + property: "member",
+ previousValue: true, + previousValue: true,
+ currentValue: false, + currentValue: false,
+ status: "updated", + status: "updated",
+ }, + },
+ { + {
+ name: "hobbies", + property: "hobbies",
+ previousValue: ["golf", "football"], + previousValue: ["golf", "football"],
+ currentValue: ["golf", "chess"], + currentValue: ["golf", "chess"],
+ status: "updated", + status: "updated",
+ }, + },
{ {
name: "age", property: "age",
previousValue: 66, previousValue: 66,
currentValue: 66, currentValue: 66,
status: "equal", status: "equal",
@ -397,25 +437,13 @@ More examples are availble in the tests of the source code.
<hr/> <hr/>
### OPTIONS
`getObjectDiff()` and `isEqual()` accept a facultative `options` parameter:
```ts
{
discardArrayOrder?: boolean // false by default
}
```
If `discardArrayOrder` is set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
## CREDITS ## CREDITS
DoneDeal0 DoneDeal0
## SUPPORT ## SUPPORT
If you use Superdiff, please show your support by buying me coffee: If you or your company use Superdiff, please show your support by buying me coffee:
https://www.buymeacoffee.com/donedeal0 https://www.buymeacoffee.com/donedeal0
<br/> <br/>

19
dist/index.d.ts vendored

@ -2,7 +2,7 @@ type DiffStatus = "added" | "equal" | "moved" | "deleted" | "updated";
type ObjectData = Record<string, any> | undefined | null; type ObjectData = Record<string, any> | undefined | null;
type ListData = any; type ListData = any;
type Options = { type Options = {
discardArrayOrder?: boolean; ignoreArrayOrder?: boolean;
}; };
type ListDiff = { type ListDiff = {
type: "list"; type: "list";
@ -15,12 +15,12 @@ type ListDiff = {
status: DiffStatus; status: DiffStatus;
}[]; }[];
}; };
type Subproperties = { type SubProperties = {
name: string; name: string;
previousValue: any; previousValue: any;
currentValue: any; currentValue: any;
status: DiffStatus; status: DiffStatus;
subDiff?: Subproperties[]; subDiff?: SubProperties[];
}; };
type ObjectDiff = { type ObjectDiff = {
type: "object"; type: "object";
@ -30,13 +30,20 @@ type ObjectDiff = {
previousValue: any; previousValue: any;
currentValue: any; currentValue: any;
status: DiffStatus; status: DiffStatus;
subPropertiesDiff?: Subproperties[]; subPropertiesDiff?: SubProperties[];
}[]; }[];
}; };
declare function getObjectDiff(prevData: ObjectData, nextData: ObjectData, options?: Options): ObjectDiff; declare function getObjectDiff(
prevData: ObjectData,
nextData: ObjectData,
options?: Options
): ObjectDiff;
declare const getListDiff: (prevList: ListData[] | undefined | null, nextList: ListData[] | undefined | null) => ListDiff; declare const getListDiff: (
prevList: ListData[] | undefined | null,
nextList: ListData[] | undefined | null
) => ListDiff;
declare function isEqual(a: any, b: any, options?: Options): boolean; declare function isEqual(a: any, b: any, options?: Options): boolean;
declare function isObject(value: any): value is Record<string, any>; declare function isObject(value: any): value is Record<string, any>;

231
dist/index.js vendored

@ -1,6 +1,233 @@
'use strict'; "use strict";
var r={ADDED:"added",EQUAL:"equal",MOVED:"moved",DELETED:"deleted",UPDATED:"updated"};function d(e,t,n={discardArrayOrder:!1}){return typeof e!=typeof t?!1:Array.isArray(e)?e.length!==t.length?!1:n.discardArrayOrder?e.every(i=>t.some(s=>JSON.stringify(s)===JSON.stringify(i))):e.every((i,s)=>JSON.stringify(i)===JSON.stringify(t[s])):typeof e=="object"?JSON.stringify(e)===JSON.stringify(t):e===t}function p(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function A(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}function E(e,t){if(!e)return {type:"object",status:r.isEqual,diff:[]};let n=[];return Object.entries(e).forEach(([i,s])=>{if(p(s)){let f=[];return Object.entries(s).forEach(([u,o])=>{f.push({name:u,previousValue:t===r.ADDED?void 0:o,currentValue:t===r.ADDED?o:void 0,status:t});}),n.push({property:i,previousValue:t===r.ADDED?void 0:e[i],currentValue:t===r.ADDED?s:void 0,status:t,subPropertiesDiff:f})}return n.push({property:i,previousValue:t===r.ADDED?void 0:e[i],currentValue:t===r.ADDED?s:void 0,status:t})}),{type:"object",status:t,diff:n}}function S(e,t,n){if(!e)return;let i=Object.entries(e).find(([s])=>d(s,t,n));return i?i[1]:void 0}function l(e,t,n){return d(e,t,n)?r.EQUAL:r.UPDATED}function j(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}function y(e,t){if(!e)return;let n=Object.keys(e),i=Object.keys(t),s=n.filter(f=>!i.includes(f));if(s.length>0)return s.map(f=>({property:f,value:e[f]}))}function b(e,t,n){let i=[],s,f=y(e,t);return f&&f.forEach(u=>{i.push({name:u.property,previousValue:u.value,currentValue:void 0,status:r.DELETED});}),Object.entries(t).forEach(([u,o])=>{let D=S(e,u,n);if(!D)return i.push({name:u,previousValue:D,currentValue:o,status:!e||!(u in e)?r.ADDED:D===o?r.EQUAL:r.UPDATED});if(p(o)){let a=b(D,o,n);a&&a.length>0&&(s=a);}D&&i.push({name:u,previousValue:D,currentValue:o,status:l(D,o,n),...!!s&&{subDiff:s}});}),i}function g(e,t,n){if(!e&&!t)return {type:"object",status:r.EQUAL,diff:[]};if(!e)return E(t,r.ADDED);if(!t)return E(e,r.DELETED);let i=[];Object.entries(t).forEach(([f,u])=>{let o=e[f];if(!o)return i.push({property:f,previousValue:o,currentValue:u,status:f in e?o===u?r.EQUAL:r.UPDATED:r.ADDED});if(p(u)){let D=b(o,u,n),a=j(D);return i.push({property:f,previousValue:o,currentValue:u,status:a,...a!==r.EQUAL&&{subPropertiesDiff:D}})}return i.push({property:f,previousValue:o,currentValue:u,status:l(o,u,n)})});let s=y(e,t);return s&&s.forEach(f=>{i.push({property:f.property,previousValue:f.value,currentValue:void 0,status:r.DELETED});}),{type:"object",status:A(i),diff:i}}function O(e,t){return {type:"list",status:t,diff:e.map((n,i)=>({value:n,prevIndex:t===r.ADDED?null:i,newIndex:t===r.ADDED?i:null,indexDiff:null,status:t}))}}function L(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}var m=(e,t)=>{if(!e&&!t)return {type:"list",status:r.EQUAL,diff:[]};if(!e)return O(t,r.ADDED);if(!t)return O(e,r.DELETED);let n=[],i=[];return t.forEach((s,f)=>{let u=e.findIndex((D,a)=>d(D,s)&&!i.includes(a));u>-1&&i.push(u);let o=u===-1?null:f-u;return o===0?n.push({value:s,prevIndex:u,newIndex:f,indexDiff:o,status:r.EQUAL}):u===-1?n.push({value:s,prevIndex:null,newIndex:f,indexDiff:o,status:r.ADDED}):n.push({value:s,prevIndex:u,newIndex:f,indexDiff:o,status:r.MOVED})}),e.forEach((s,f)=>{if(!i.includes(f))return n.splice(f,0,{value:s,prevIndex:f,newIndex:null,indexDiff:null,status:r.DELETED})}),{type:"list",status:L(n),diff:n}}; var r = {
ADDED: "added",
EQUAL: "equal",
MOVED: "moved",
DELETED: "deleted",
UPDATED: "updated",
};
function d(e, t, n = { ignoreArrayOrder: !1 }) {
return typeof e != typeof t
? !1
: Array.isArray(e)
? e.length !== t.length
? !1
: n.ignoreArrayOrder
? e.every((i) => t.some((s) => JSON.stringify(s) === JSON.stringify(i)))
: e.every((i, s) => JSON.stringify(i) === JSON.stringify(t[s]))
: typeof e == "object"
? JSON.stringify(e) === JSON.stringify(t)
: e === t;
}
function p(e) {
return !!e && typeof e == "object" && !Array.isArray(e);
}
function A(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
function E(e, t) {
if (!e) return { type: "object", status: r.isEqual, diff: [] };
let n = [];
return (
Object.entries(e).forEach(([i, s]) => {
if (p(s)) {
let f = [];
return (
Object.entries(s).forEach(([u, o]) => {
f.push({
name: u,
previousValue: t === r.ADDED ? void 0 : o,
currentValue: t === r.ADDED ? o : void 0,
status: t,
});
}),
n.push({
property: i,
previousValue: t === r.ADDED ? void 0 : e[i],
currentValue: t === r.ADDED ? s : void 0,
status: t,
subPropertiesDiff: f,
})
);
}
return n.push({
property: i,
previousValue: t === r.ADDED ? void 0 : e[i],
currentValue: t === r.ADDED ? s : void 0,
status: t,
});
}),
{ type: "object", status: t, diff: n }
);
}
function S(e, t, n) {
if (!e) return;
let i = Object.entries(e).find(([s]) => d(s, t, n));
return i ? i[1] : void 0;
}
function l(e, t, n) {
return d(e, t, n) ? r.EQUAL : r.UPDATED;
}
function j(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
function y(e, t) {
if (!e) return;
let n = Object.keys(e),
i = Object.keys(t),
s = n.filter((f) => !i.includes(f));
if (s.length > 0) return s.map((f) => ({ property: f, value: e[f] }));
}
function b(e, t, n) {
let i = [],
s,
f = y(e, t);
return (
f &&
f.forEach((u) => {
i.push({
name: u.property,
previousValue: u.value,
currentValue: void 0,
status: r.DELETED,
});
}),
Object.entries(t).forEach(([u, o]) => {
let D = S(e, u, n);
if (!D)
return i.push({
name: u,
previousValue: D,
currentValue: o,
status: !e || !(u in e) ? r.ADDED : D === o ? r.EQUAL : r.UPDATED,
});
if (p(o)) {
let a = b(D, o, n);
a && a.length > 0 && (s = a);
}
D &&
i.push({
name: u,
previousValue: D,
currentValue: o,
status: l(D, o, n),
...(!!s && { subDiff: s }),
});
}),
i
);
}
function g(e, t, n) {
if (!e && !t) return { type: "object", status: r.EQUAL, diff: [] };
if (!e) return E(t, r.ADDED);
if (!t) return E(e, r.DELETED);
let i = [];
Object.entries(t).forEach(([f, u]) => {
let o = e[f];
if (!o)
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: f in e ? (o === u ? r.EQUAL : r.UPDATED) : r.ADDED,
});
if (p(u)) {
let D = b(o, u, n),
a = j(D);
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: a,
...(a !== r.EQUAL && { subPropertiesDiff: D }),
});
}
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: l(o, u, n),
});
});
let s = y(e, t);
return (
s &&
s.forEach((f) => {
i.push({
property: f.property,
previousValue: f.value,
currentValue: void 0,
status: r.DELETED,
});
}),
{ type: "object", status: A(i), diff: i }
);
}
function O(e, t) {
return {
type: "list",
status: t,
diff: e.map((n, i) => ({
value: n,
prevIndex: t === r.ADDED ? null : i,
newIndex: t === r.ADDED ? i : null,
indexDiff: null,
status: t,
})),
};
}
function L(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
var m = (e, t) => {
if (!e && !t) return { type: "list", status: r.EQUAL, diff: [] };
if (!e) return O(t, r.ADDED);
if (!t) return O(e, r.DELETED);
let n = [],
i = [];
return (
t.forEach((s, f) => {
let u = e.findIndex((D, a) => d(D, s) && !i.includes(a));
u > -1 && i.push(u);
let o = u === -1 ? null : f - u;
return o === 0
? n.push({
value: s,
prevIndex: u,
newIndex: f,
indexDiff: o,
status: r.EQUAL,
})
: u === -1
? n.push({
value: s,
prevIndex: null,
newIndex: f,
indexDiff: o,
status: r.ADDED,
})
: n.push({
value: s,
prevIndex: u,
newIndex: f,
indexDiff: o,
status: r.MOVED,
});
}),
e.forEach((s, f) => {
if (!i.includes(f))
return n.splice(f, 0, {
value: s,
prevIndex: f,
newIndex: null,
indexDiff: null,
status: r.DELETED,
});
}),
{ type: "list", status: L(n), diff: n }
);
};
exports.getListDiff = m; exports.getListDiff = m;
exports.getObjectDiff = g; exports.getObjectDiff = g;

229
dist/index.mjs vendored

@ -1,3 +1,230 @@
var r={ADDED:"added",EQUAL:"equal",MOVED:"moved",DELETED:"deleted",UPDATED:"updated"};function d(e,t,n={discardArrayOrder:!1}){return typeof e!=typeof t?!1:Array.isArray(e)?e.length!==t.length?!1:n.discardArrayOrder?e.every(i=>t.some(s=>JSON.stringify(s)===JSON.stringify(i))):e.every((i,s)=>JSON.stringify(i)===JSON.stringify(t[s])):typeof e=="object"?JSON.stringify(e)===JSON.stringify(t):e===t}function p(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function S(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}function l(e,t){if(!e)return {type:"object",status:r.isEqual,diff:[]};let n=[];return Object.entries(e).forEach(([i,s])=>{if(p(s)){let f=[];return Object.entries(s).forEach(([u,o])=>{f.push({name:u,previousValue:t===r.ADDED?void 0:o,currentValue:t===r.ADDED?o:void 0,status:t});}),n.push({property:i,previousValue:t===r.ADDED?void 0:e[i],currentValue:t===r.ADDED?s:void 0,status:t,subPropertiesDiff:f})}return n.push({property:i,previousValue:t===r.ADDED?void 0:e[i],currentValue:t===r.ADDED?s:void 0,status:t})}),{type:"object",status:t,diff:n}}function j(e,t,n){if(!e)return;let i=Object.entries(e).find(([s])=>d(s,t,n));return i?i[1]:void 0}function y(e,t,n){return d(e,t,n)?r.EQUAL:r.UPDATED}function g(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}function b(e,t){if(!e)return;let n=Object.keys(e),i=Object.keys(t),s=n.filter(f=>!i.includes(f));if(s.length>0)return s.map(f=>({property:f,value:e[f]}))}function O(e,t,n){let i=[],s,f=b(e,t);return f&&f.forEach(u=>{i.push({name:u.property,previousValue:u.value,currentValue:void 0,status:r.DELETED});}),Object.entries(t).forEach(([u,o])=>{let D=j(e,u,n);if(!D)return i.push({name:u,previousValue:D,currentValue:o,status:!e||!(u in e)?r.ADDED:D===o?r.EQUAL:r.UPDATED});if(p(o)){let a=O(D,o,n);a&&a.length>0&&(s=a);}D&&i.push({name:u,previousValue:D,currentValue:o,status:y(D,o,n),...!!s&&{subDiff:s}});}),i}function L(e,t,n){if(!e&&!t)return {type:"object",status:r.EQUAL,diff:[]};if(!e)return l(t,r.ADDED);if(!t)return l(e,r.DELETED);let i=[];Object.entries(t).forEach(([f,u])=>{let o=e[f];if(!o)return i.push({property:f,previousValue:o,currentValue:u,status:f in e?o===u?r.EQUAL:r.UPDATED:r.ADDED});if(p(u)){let D=O(o,u,n),a=g(D);return i.push({property:f,previousValue:o,currentValue:u,status:a,...a!==r.EQUAL&&{subPropertiesDiff:D}})}return i.push({property:f,previousValue:o,currentValue:u,status:y(o,u,n)})});let s=b(e,t);return s&&s.forEach(f=>{i.push({property:f.property,previousValue:f.value,currentValue:void 0,status:r.DELETED});}),{type:"object",status:S(i),diff:i}}function A(e,t){return {type:"list",status:t,diff:e.map((n,i)=>({value:n,prevIndex:t===r.ADDED?null:i,newIndex:t===r.ADDED?i:null,indexDiff:null,status:t}))}}function m(e){return e.some(t=>t.status!==r.EQUAL)?r.UPDATED:r.EQUAL}var h=(e,t)=>{if(!e&&!t)return {type:"list",status:r.EQUAL,diff:[]};if(!e)return A(t,r.ADDED);if(!t)return A(e,r.DELETED);let n=[],i=[];return t.forEach((s,f)=>{let u=e.findIndex((D,a)=>d(D,s)&&!i.includes(a));u>-1&&i.push(u);let o=u===-1?null:f-u;return o===0?n.push({value:s,prevIndex:u,newIndex:f,indexDiff:o,status:r.EQUAL}):u===-1?n.push({value:s,prevIndex:null,newIndex:f,indexDiff:o,status:r.ADDED}):n.push({value:s,prevIndex:u,newIndex:f,indexDiff:o,status:r.MOVED})}),e.forEach((s,f)=>{if(!i.includes(f))return n.splice(f,0,{value:s,prevIndex:f,newIndex:null,indexDiff:null,status:r.DELETED})}),{type:"list",status:m(n),diff:n}}; var r = {
ADDED: "added",
EQUAL: "equal",
MOVED: "moved",
DELETED: "deleted",
UPDATED: "updated",
};
function d(e, t, n = { ignoreArrayOrder: !1 }) {
return typeof e != typeof t
? !1
: Array.isArray(e)
? e.length !== t.length
? !1
: n.ignoreArrayOrder
? e.every((i) => t.some((s) => JSON.stringify(s) === JSON.stringify(i)))
: e.every((i, s) => JSON.stringify(i) === JSON.stringify(t[s]))
: typeof e == "object"
? JSON.stringify(e) === JSON.stringify(t)
: e === t;
}
function p(e) {
return !!e && typeof e == "object" && !Array.isArray(e);
}
function S(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
function l(e, t) {
if (!e) return { type: "object", status: r.isEqual, diff: [] };
let n = [];
return (
Object.entries(e).forEach(([i, s]) => {
if (p(s)) {
let f = [];
return (
Object.entries(s).forEach(([u, o]) => {
f.push({
name: u,
previousValue: t === r.ADDED ? void 0 : o,
currentValue: t === r.ADDED ? o : void 0,
status: t,
});
}),
n.push({
property: i,
previousValue: t === r.ADDED ? void 0 : e[i],
currentValue: t === r.ADDED ? s : void 0,
status: t,
subPropertiesDiff: f,
})
);
}
return n.push({
property: i,
previousValue: t === r.ADDED ? void 0 : e[i],
currentValue: t === r.ADDED ? s : void 0,
status: t,
});
}),
{ type: "object", status: t, diff: n }
);
}
function j(e, t, n) {
if (!e) return;
let i = Object.entries(e).find(([s]) => d(s, t, n));
return i ? i[1] : void 0;
}
function y(e, t, n) {
return d(e, t, n) ? r.EQUAL : r.UPDATED;
}
function g(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
function b(e, t) {
if (!e) return;
let n = Object.keys(e),
i = Object.keys(t),
s = n.filter((f) => !i.includes(f));
if (s.length > 0) return s.map((f) => ({ property: f, value: e[f] }));
}
function O(e, t, n) {
let i = [],
s,
f = b(e, t);
return (
f &&
f.forEach((u) => {
i.push({
name: u.property,
previousValue: u.value,
currentValue: void 0,
status: r.DELETED,
});
}),
Object.entries(t).forEach(([u, o]) => {
let D = j(e, u, n);
if (!D)
return i.push({
name: u,
previousValue: D,
currentValue: o,
status: !e || !(u in e) ? r.ADDED : D === o ? r.EQUAL : r.UPDATED,
});
if (p(o)) {
let a = O(D, o, n);
a && a.length > 0 && (s = a);
}
D &&
i.push({
name: u,
previousValue: D,
currentValue: o,
status: y(D, o, n),
...(!!s && { subDiff: s }),
});
}),
i
);
}
function L(e, t, n) {
if (!e && !t) return { type: "object", status: r.EQUAL, diff: [] };
if (!e) return l(t, r.ADDED);
if (!t) return l(e, r.DELETED);
let i = [];
Object.entries(t).forEach(([f, u]) => {
let o = e[f];
if (!o)
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: f in e ? (o === u ? r.EQUAL : r.UPDATED) : r.ADDED,
});
if (p(u)) {
let D = O(o, u, n),
a = g(D);
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: a,
...(a !== r.EQUAL && { subPropertiesDiff: D }),
});
}
return i.push({
property: f,
previousValue: o,
currentValue: u,
status: y(o, u, n),
});
});
let s = b(e, t);
return (
s &&
s.forEach((f) => {
i.push({
property: f.property,
previousValue: f.value,
currentValue: void 0,
status: r.DELETED,
});
}),
{ type: "object", status: S(i), diff: i }
);
}
function A(e, t) {
return {
type: "list",
status: t,
diff: e.map((n, i) => ({
value: n,
prevIndex: t === r.ADDED ? null : i,
newIndex: t === r.ADDED ? i : null,
indexDiff: null,
status: t,
})),
};
}
function m(e) {
return e.some((t) => t.status !== r.EQUAL) ? r.UPDATED : r.EQUAL;
}
var h = (e, t) => {
if (!e && !t) return { type: "list", status: r.EQUAL, diff: [] };
if (!e) return A(t, r.ADDED);
if (!t) return A(e, r.DELETED);
let n = [],
i = [];
return (
t.forEach((s, f) => {
let u = e.findIndex((D, a) => d(D, s) && !i.includes(a));
u > -1 && i.push(u);
let o = u === -1 ? null : f - u;
return o === 0
? n.push({
value: s,
prevIndex: u,
newIndex: f,
indexDiff: o,
status: r.EQUAL,
})
: u === -1
? n.push({
value: s,
prevIndex: null,
newIndex: f,
indexDiff: o,
status: r.ADDED,
})
: n.push({
value: s,
prevIndex: u,
newIndex: f,
indexDiff: o,
status: r.MOVED,
});
}),
e.forEach((s, f) => {
if (!i.includes(f))
return n.splice(f, 0, {
value: s,
prevIndex: f,
newIndex: null,
indexDiff: null,
status: r.DELETED,
});
}),
{ type: "list", status: m(n), diff: n }
);
};
export { h as getListDiff, L as getObjectDiff, d as isEqual, p as isObject }; export { h as getListDiff, L as getObjectDiff, d as isEqual, p as isObject };

53
src/list-diff.ts

@ -1,45 +1,59 @@
import { STATUS, ListDiff, ListData, DiffStatus } from "./model"; import {
LIST_STATUS,
ListDiff,
ListData,
ListDiffStatus,
ListOptions,
} from "./model";
import { isEqual } from "./utils"; import { isEqual } from "./utils";
function getLeanDiff(
diff: ListDiff["diff"],
showOnly = [] as ListOptions["showOnly"]
): ListDiff["diff"] {
return diff.filter((value) => showOnly?.includes(value.status));
}
function formatSingleListDiff( function formatSingleListDiff(
listData: ListData[], listData: ListData[],
status: DiffStatus status: ListDiffStatus
): ListDiff { ): ListDiff {
return { return {
type: "list", type: "list",
status, status,
diff: listData.map((data: ListData, i) => ({ diff: listData.map((data: ListData, i) => ({
value: data, value: data,
prevIndex: status === STATUS.ADDED ? null : i, prevIndex: status === LIST_STATUS.ADDED ? null : i,
newIndex: status === STATUS.ADDED ? i : null, newIndex: status === LIST_STATUS.ADDED ? i : null,
indexDiff: null, indexDiff: null,
status, status,
})), })),
}; };
} }
function getListStatus(listDiff: ListDiff["diff"]): DiffStatus { function getListStatus(listDiff: ListDiff["diff"]): ListDiffStatus {
return listDiff.some((value) => value.status !== STATUS.EQUAL) return listDiff.some((value) => value.status !== LIST_STATUS.EQUAL)
? STATUS.UPDATED ? LIST_STATUS.UPDATED
: STATUS.EQUAL; : LIST_STATUS.EQUAL;
} }
export const getListDiff = ( export const getListDiff = (
prevList: ListData[] | undefined | null, prevList: ListData[] | undefined | null,
nextList: ListData[] | undefined | null nextList: ListData[] | undefined | null,
options: ListOptions = { showOnly: [] }
): ListDiff => { ): ListDiff => {
if (!prevList && !nextList) { if (!prevList && !nextList) {
return { return {
type: "list", type: "list",
status: STATUS.EQUAL, status: LIST_STATUS.EQUAL,
diff: [], diff: [],
}; };
} }
if (!prevList) { if (!prevList) {
return formatSingleListDiff(nextList as ListData, STATUS.ADDED); return formatSingleListDiff(nextList as ListData, LIST_STATUS.ADDED);
} }
if (!nextList) { if (!nextList) {
return formatSingleListDiff(prevList as ListData, STATUS.DELETED); return formatSingleListDiff(prevList as ListData, LIST_STATUS.DELETED);
} }
const diff: ListDiff["diff"] = []; const diff: ListDiff["diff"] = [];
const prevIndexMatches: number[] = []; const prevIndexMatches: number[] = [];
@ -58,7 +72,7 @@ export const getListDiff = (
prevIndex, prevIndex,
newIndex: i, newIndex: i,
indexDiff, indexDiff,
status: STATUS.EQUAL, status: LIST_STATUS.EQUAL,
}); });
} }
if (prevIndex === -1) { if (prevIndex === -1) {
@ -67,7 +81,7 @@ export const getListDiff = (
prevIndex: null, prevIndex: null,
newIndex: i, newIndex: i,
indexDiff, indexDiff,
status: STATUS.ADDED, status: LIST_STATUS.ADDED,
}); });
} }
return diff.push({ return diff.push({
@ -75,7 +89,7 @@ export const getListDiff = (
prevIndex, prevIndex,
newIndex: i, newIndex: i,
indexDiff, indexDiff,
status: STATUS.MOVED, status: LIST_STATUS.MOVED,
}); });
}); });
@ -86,10 +100,17 @@ export const getListDiff = (
prevIndex: i, prevIndex: i,
newIndex: null, newIndex: null,
indexDiff: null, indexDiff: null,
status: STATUS.DELETED, status: LIST_STATUS.DELETED,
}); });
} }
}); });
if (options.showOnly && options?.showOnly?.length > 0) {
return {
type: "list",
status: getListStatus(diff),
diff: getLeanDiff(diff, options.showOnly),
};
}
return { return {
type: "list", type: "list",
status: getListStatus(diff), status: getListStatus(diff),

64
src/model.ts

@ -1,45 +1,83 @@
export const STATUS: Record<string, DiffStatus> = { export const STATUS: Record<string, ObjectDiffStatus> = {
ADDED: "added", ADDED: "added",
EQUAL: "equal", EQUAL: "equal",
MOVED: "moved",
DELETED: "deleted", DELETED: "deleted",
UPDATED: "updated", UPDATED: "updated",
}; };
export type DiffStatus = "added" | "equal" | "moved" | "deleted" | "updated"; export const LIST_STATUS: Record<string, ListDiffStatus> = {
...STATUS,
MOVED: "moved",
};
export type ListDiffStatus =
| "added"
| "equal"
| "moved"
| "deleted"
| "updated";
export type ObjectDiffStatus = "added" | "equal" | "deleted" | "updated";
export type ObjectData = Record<string, any> | undefined | null; export type ObjectData = Record<string, any> | undefined | null;
export type ListData = any; export type ListData = any;
export type Options = { discardArrayOrder?: boolean };
export type ObjectStatusTuple = readonly [
"added",
"equal",
"deleted",
"updated"
];
export type ListStatusTuple = readonly [
"added",
"equal",
"deleted",
"moved" | "updated"
];
export type isEqualOptions = {
ignoreArrayOrder?: boolean;
};
export type ObjectOptions = {
ignoreArrayOrder?: boolean;
showOnly?: {
statuses: Array<ObjectStatusTuple[number]>;
granularity?: "basic" | "deep";
};
};
export type ListOptions = {
showOnly?: Array<ListStatusTuple[number]>;
};
export type ListDiff = { export type ListDiff = {
type: "list"; type: "list";
status: DiffStatus; status: ListDiffStatus;
diff: { diff: {
value: ListData; value: ListData;
prevIndex: number | null; prevIndex: number | null;
newIndex: number | null; newIndex: number | null;
indexDiff: number | null; indexDiff: number | null;
status: DiffStatus; status: ListDiffStatus;
}[]; }[];
}; };
export type Subproperties = { export type SubProperties = {
name: string; property: string;
previousValue: any; previousValue: any;
currentValue: any; currentValue: any;
status: DiffStatus; status: ObjectDiffStatus;
subDiff?: Subproperties[]; subPropertiesDiff?: SubProperties[];
}; };
export type ObjectDiff = { export type ObjectDiff = {
type: "object"; type: "object";
status: DiffStatus; status: ObjectDiffStatus;
diff: { diff: {
property: string; property: string;
previousValue: any; previousValue: any;
currentValue: any; currentValue: any;
status: DiffStatus; status: ObjectDiffStatus;
subPropertiesDiff?: Subproperties[]; subPropertiesDiff?: SubProperties[];
}[]; }[];
}; };

89
src/object-diff.ts

@ -1,14 +1,47 @@
import { import {
ObjectData, ObjectData,
ObjectDiff, ObjectDiff,
DiffStatus,
STATUS, STATUS,
Subproperties, SubProperties,
Options, ObjectOptions,
ObjectDiffStatus,
} from "./model"; } from "./model";
import { isObject, isEqual } from "./utils"; import { isObject, isEqual } from "./utils";
function getObjectStatus(diff: ObjectDiff["diff"]): DiffStatus { function getLeanDiff(
diff: ObjectDiff["diff"],
showOnly: ObjectOptions["showOnly"] = { statuses: [], granularity: "basic" }
): ObjectDiff["diff"] {
const { statuses, granularity } = showOnly;
return diff.reduce((acc, value) => {
if (statuses.includes(value.status)) {
return [...acc, value];
}
if (granularity === "deep" && value.subPropertiesDiff) {
const cleanSubPropertiesDiff = getLeanDiff(
value.subPropertiesDiff,
showOnly
);
if (cleanSubPropertiesDiff.length > 0) {
return [
...acc,
{ ...value, subPropertiesDiff: cleanSubPropertiesDiff },
];
}
}
// @ts-ignore
if (granularity === "deep" && value.subDiff) {
// @ts-ignore
const cleanSubDiff = getLeanDiff(value.subDiff, showOnly);
if (cleanSubDiff.length > 0) {
return [...acc, { ...value, subDiff: cleanSubDiff }];
}
}
return acc;
}, [] as ObjectDiff["diff"]);
}
function getObjectStatus(diff: ObjectDiff["diff"]): ObjectDiffStatus {
return diff.some((property) => property.status !== STATUS.EQUAL) return diff.some((property) => property.status !== STATUS.EQUAL)
? STATUS.UPDATED ? STATUS.UPDATED
: STATUS.EQUAL; : STATUS.EQUAL;
@ -16,22 +49,22 @@ function getObjectStatus(diff: ObjectDiff["diff"]): DiffStatus {
function formatSingleObjectDiff( function formatSingleObjectDiff(
data: ObjectData, data: ObjectData,
status: DiffStatus status: ObjectDiffStatus
): ObjectDiff { ): ObjectDiff {
if (!data) { if (!data) {
return { return {
type: "object", type: "object",
status: STATUS.isEqual, status: STATUS.EQUAL,
diff: [], diff: [],
}; };
} }
const diff: ObjectDiff["diff"] = []; const diff: ObjectDiff["diff"] = [];
Object.entries(data).forEach(([property, value]) => { Object.entries(data).forEach(([property, value]) => {
if (isObject(value)) { if (isObject(value)) {
const subPropertiesDiff: Subproperties[] = []; const subPropertiesDiff: SubProperties[] = [];
Object.entries(value).forEach(([subProperty, subValue]) => { Object.entries(value).forEach(([subProperty, subValue]) => {
subPropertiesDiff.push({ subPropertiesDiff.push({
name: subProperty, property: subProperty,
previousValue: status === STATUS.ADDED ? undefined : subValue, previousValue: status === STATUS.ADDED ? undefined : subValue,
currentValue: status === STATUS.ADDED ? subValue : undefined, currentValue: status === STATUS.ADDED ? subValue : undefined,
status, status,
@ -62,7 +95,7 @@ function formatSingleObjectDiff(
function getPreviousMatch( function getPreviousMatch(
previousValue: any | undefined, previousValue: any | undefined,
nextSubProperty: any, nextSubProperty: any,
options?: Options options?: ObjectOptions
): any | undefined { ): any | undefined {
if (!previousValue) { if (!previousValue) {
return undefined; return undefined;
@ -76,15 +109,17 @@ function getPreviousMatch(
function getValueStatus( function getValueStatus(
previousValue: any, previousValue: any,
nextValue: any, nextValue: any,
options?: Options options?: ObjectOptions
): DiffStatus { ): ObjectDiffStatus {
if (isEqual(previousValue, nextValue, options)) { if (isEqual(previousValue, nextValue, options)) {
return STATUS.EQUAL; return STATUS.EQUAL;
} }
return STATUS.UPDATED; return STATUS.UPDATED;
} }
function getPropertyStatus(subPropertiesDiff: Subproperties[]): DiffStatus { function getPropertyStatus(
subPropertiesDiff: SubProperties[]
): ObjectDiffStatus {
return subPropertiesDiff.some((property) => property.status !== STATUS.EQUAL) return subPropertiesDiff.some((property) => property.status !== STATUS.EQUAL)
? STATUS.UPDATED ? STATUS.UPDATED
: STATUS.EQUAL; : STATUS.EQUAL;
@ -110,10 +145,10 @@ function getDeletedProperties(
function getSubPropertiesDiff( function getSubPropertiesDiff(
previousValue: Record<string, any> | undefined, previousValue: Record<string, any> | undefined,
nextValue: Record<string, any>, nextValue: Record<string, any>,
options?: Options options?: ObjectOptions
): Subproperties[] { ): SubProperties[] {
const subPropertiesDiff: Subproperties[] = []; const subPropertiesDiff: SubProperties[] = [];
let subDiff: Subproperties[]; let subDiff: SubProperties[];
const deletedMainSubProperties = getDeletedProperties( const deletedMainSubProperties = getDeletedProperties(
previousValue, previousValue,
nextValue nextValue
@ -121,7 +156,7 @@ function getSubPropertiesDiff(
if (deletedMainSubProperties) { if (deletedMainSubProperties) {
deletedMainSubProperties.forEach((deletedProperty) => { deletedMainSubProperties.forEach((deletedProperty) => {
subPropertiesDiff.push({ subPropertiesDiff.push({
name: deletedProperty.property, property: deletedProperty.property,
previousValue: deletedProperty.value, previousValue: deletedProperty.value,
currentValue: undefined, currentValue: undefined,
status: STATUS.DELETED, status: STATUS.DELETED,
@ -136,7 +171,7 @@ function getSubPropertiesDiff(
); );
if (!!!previousMatch) { if (!!!previousMatch) {
return subPropertiesDiff.push({ return subPropertiesDiff.push({
name: nextSubProperty, property: nextSubProperty,
previousValue: previousMatch, previousValue: previousMatch,
currentValue: nextSubValue, currentValue: nextSubValue,
status: status:
@ -148,7 +183,7 @@ function getSubPropertiesDiff(
}); });
} }
if (isObject(nextSubValue)) { if (isObject(nextSubValue)) {
const data: Subproperties[] = getSubPropertiesDiff( const data: SubProperties[] = getSubPropertiesDiff(
previousMatch, previousMatch,
nextSubValue, nextSubValue,
options options
@ -159,7 +194,7 @@ function getSubPropertiesDiff(
} }
if (previousMatch) { if (previousMatch) {
subPropertiesDiff.push({ subPropertiesDiff.push({
name: nextSubProperty, property: nextSubProperty,
previousValue: previousMatch, previousValue: previousMatch,
currentValue: nextSubValue, currentValue: nextSubValue,
status: getValueStatus(previousMatch, nextSubValue, options), status: getValueStatus(previousMatch, nextSubValue, options),
@ -173,7 +208,10 @@ function getSubPropertiesDiff(
export function getObjectDiff( export function getObjectDiff(
prevData: ObjectData, prevData: ObjectData,
nextData: ObjectData, nextData: ObjectData,
options?: Options options: ObjectOptions = {
ignoreArrayOrder: false,
showOnly: { statuses: [], granularity: "basic" },
}
): ObjectDiff { ): ObjectDiff {
if (!prevData && !nextData) { if (!prevData && !nextData) {
return { return {
@ -204,7 +242,7 @@ export function getObjectDiff(
}); });
} }
if (isObject(nextValue)) { if (isObject(nextValue)) {
const subPropertiesDiff: Subproperties[] = getSubPropertiesDiff( const subPropertiesDiff: SubProperties[] = getSubPropertiesDiff(
previousValue, previousValue,
nextValue, nextValue,
options options
@ -236,6 +274,13 @@ export function getObjectDiff(
}); });
}); });
} }
if (options.showOnly && options.showOnly.statuses.length > 0) {
return {
type: "object",
status: getObjectStatus(diff),
diff: getLeanDiff(diff, options.showOnly),
};
}
return { return {
type: "object", type: "object",
status: getObjectStatus(diff), status: getObjectStatus(diff),

6
src/utils.ts

@ -1,16 +1,16 @@
import { Options } from "./model"; import { isEqualOptions } from "./model";
export function isEqual( export function isEqual(
a: any, a: any,
b: any, b: any,
options: Options = { discardArrayOrder: false } options: isEqualOptions = { ignoreArrayOrder: false }
): boolean { ): boolean {
if (typeof a !== typeof b) return false; if (typeof a !== typeof b) return false;
if (Array.isArray(a)) { if (Array.isArray(a)) {
if (a.length !== b.length) { if (a.length !== b.length) {
return false; return false;
} }
if (options.discardArrayOrder) { if (options.ignoreArrayOrder) {
return a.every((v) => return a.every((v) =>
b.some((nextV: any) => JSON.stringify(nextV) === JSON.stringify(v)) b.some((nextV: any) => JSON.stringify(nextV) === JSON.stringify(v))
); );

60
test/list-diff.test.ts

@ -393,4 +393,64 @@ describe("getListDiff", () => {
], ],
}); });
}); });
it("showOnly added and deleted values", () => {
expect(
getListDiff(
[
false,
true,
true,
undefined,
"hello",
{ name: "joe", age: 88 },
false,
13,
],
[
false,
false,
true,
undefined,
"hello",
{ name: "joe", age: 88 },
false,
{ name: "joe", age: 88 },
],
{ showOnly: ["added", "deleted"] }
)
).toStrictEqual({
type: "list",
status: "updated",
diff: [
{
value: true,
prevIndex: 2,
newIndex: null,
indexDiff: null,
status: "deleted",
},
{
value: 13,
prevIndex: 7,
newIndex: null,
indexDiff: null,
status: "deleted",
},
{
value: false,
prevIndex: null,
newIndex: 6,
indexDiff: null,
status: "added",
},
{
value: { name: "joe", age: 88 },
prevIndex: null,
newIndex: 7,
indexDiff: null,
status: "added",
},
],
});
});
}); });

150
test/object-diff.test.ts

@ -191,31 +191,31 @@ describe("getObjectDiff", () => {
status: "updated", status: "updated",
subPropertiesDiff: [ subPropertiesDiff: [
{ {
name: "age", property: "age",
previousValue: 66, previousValue: 66,
currentValue: undefined, currentValue: undefined,
status: "deleted", status: "deleted",
}, },
{ {
name: "name", property: "name",
previousValue: "joe", previousValue: "joe",
currentValue: "joe", currentValue: "joe",
status: "equal", status: "equal",
}, },
{ {
name: "member", property: "member",
previousValue: true, previousValue: true,
currentValue: false, currentValue: false,
status: "updated", status: "updated",
}, },
{ {
name: "hobbies", property: "hobbies",
previousValue: ["golf", "football"], previousValue: ["golf", "football"],
currentValue: ["golf", "chess"], currentValue: ["golf", "chess"],
status: "updated", status: "updated",
}, },
{ {
name: "nickname", property: "nickname",
previousValue: undefined, previousValue: undefined,
currentValue: "super joe", currentValue: "super joe",
status: "added", status: "added",
@ -296,13 +296,13 @@ describe("getObjectDiff", () => {
status: "updated", status: "updated",
subPropertiesDiff: [ subPropertiesDiff: [
{ {
name: "name", property: "name",
previousValue: "joe", previousValue: "joe",
currentValue: "joe", currentValue: "joe",
status: "equal", status: "equal",
}, },
{ {
name: "data", property: "data",
previousValue: { previousValue: {
member: true, member: true,
hobbies: { hobbies: {
@ -320,13 +320,13 @@ describe("getObjectDiff", () => {
status: "updated", status: "updated",
subDiff: [ subDiff: [
{ {
name: "member", property: "member",
previousValue: true, previousValue: true,
currentValue: true, currentValue: true,
status: "equal", status: "equal",
}, },
{ {
name: "hobbies", property: "hobbies",
previousValue: { previousValue: {
football: ["psg"], football: ["psg"],
rugby: ["france"], rugby: ["france"],
@ -338,19 +338,19 @@ describe("getObjectDiff", () => {
status: "updated", status: "updated",
subDiff: [ subDiff: [
{ {
name: "rugby", property: "rugby",
previousValue: ["france"], previousValue: ["france"],
currentValue: undefined, currentValue: undefined,
status: "deleted", status: "deleted",
}, },
{ {
name: "football", property: "football",
previousValue: ["psg"], previousValue: ["psg"],
currentValue: ["psg", "nantes"], currentValue: ["psg", "nantes"],
status: "updated", status: "updated",
}, },
{ {
name: "golf", property: "golf",
previousValue: undefined, previousValue: undefined,
currentValue: ["st andrews"], currentValue: ["st andrews"],
status: "added", status: "added",
@ -364,7 +364,7 @@ describe("getObjectDiff", () => {
], ],
}); });
}); });
it("detects changed between two objects BUT doesn't care about array order as long as all values are preserved when discardArrayOrder option is activated", () => { it("detects changed between two objects BUT doesn't care about array order as long as all values are preserved when ignoreArrayOrder option is activated", () => {
expect( expect(
getObjectDiff( getObjectDiff(
{ {
@ -387,7 +387,7 @@ describe("getObjectDiff", () => {
nickname: "super joe", nickname: "super joe",
}, },
}, },
{ discardArrayOrder: true } { ignoreArrayOrder: true }
) )
).toStrictEqual({ ).toStrictEqual({
type: "object", type: "object",
@ -422,31 +422,143 @@ describe("getObjectDiff", () => {
status: "updated", status: "updated",
subPropertiesDiff: [ subPropertiesDiff: [
{ {
name: "age", property: "age",
previousValue: 66, previousValue: 66,
currentValue: undefined, currentValue: undefined,
status: "deleted", status: "deleted",
}, },
{ {
name: "name", property: "name",
previousValue: "joe", previousValue: "joe",
currentValue: "joe", currentValue: "joe",
status: "equal", status: "equal",
}, },
{ {
name: "member", property: "member",
previousValue: true, previousValue: true,
currentValue: false, currentValue: false,
status: "updated", status: "updated",
}, },
{ {
name: "hobbies", property: "hobbies",
previousValue: ["golf", "football"], previousValue: ["golf", "football"],
currentValue: ["football", "golf"], currentValue: ["football", "golf"],
status: "equal", status: "equal",
}, },
{ {
name: "nickname", property: "nickname",
previousValue: undefined,
currentValue: "super joe",
status: "added",
},
],
},
{
property: "type",
previousValue: "sport",
currentValue: undefined,
status: "deleted",
},
],
});
});
it("showOnly main added values", () => {
expect(
getObjectDiff(
{
id: 54,
type: "sport",
user: {
name: "joe",
member: true,
hobbies: ["golf", "football"],
age: 66,
},
},
{
id: 54,
country: "us",
user: {
name: "joe",
member: false,
hobbies: ["golf", "chess"],
nickname: "super joe",
},
},
{ showOnly: { statuses: ["added"] } }
)
).toStrictEqual({
type: "object",
status: "updated",
diff: [
{
property: "country",
previousValue: undefined,
currentValue: "us",
status: "added",
},
],
});
});
it("showOnly added and deleted values in depth", () => {
expect(
getObjectDiff(
{
id: 54,
type: "sport",
user: {
name: "joe",
member: true,
hobbies: ["golf", "football"],
age: 66,
},
},
{
id: 54,
country: "us",
user: {
name: "joe",
member: false,
hobbies: ["golf", "chess"],
nickname: "super joe",
},
},
{ showOnly: { statuses: ["added", "deleted"], granularity: "deep" } }
)
).toStrictEqual({
type: "object",
status: "updated",
diff: [
{
property: "country",
previousValue: undefined,
currentValue: "us",
status: "added",
},
{
property: "user",
previousValue: {
name: "joe",
member: true,
hobbies: ["golf", "football"],
age: 66,
},
currentValue: {
name: "joe",
member: false,
hobbies: ["golf", "chess"],
nickname: "super joe",
},
status: "updated",
subPropertiesDiff: [
{
property: "age",
previousValue: 66,
currentValue: undefined,
status: "deleted",
},
{
property: "nickname",
previousValue: undefined, previousValue: undefined,
currentValue: "super joe", currentValue: "super joe",
status: "added", status: "added",

20
test/utils.test.ts

@ -40,14 +40,14 @@ describe("isEqual", () => {
).toBeFalsy(); ).toBeFalsy();
expect(isEqual(["psg"], ["psg", "nantes"])).toBeFalsy(); expect(isEqual(["psg"], ["psg", "nantes"])).toBeFalsy();
}); });
it("return true if discardArrayOrder option is activated and arrays contains the same values regardless of their positions", () => { it("return true if ignoreArrayOrder option is activated and arrays contains the same values regardless of their positions", () => {
expect( expect(
isEqual(["hello", "world"], ["world", "hello"], { isEqual(["hello", "world"], ["world", "hello"], {
discardArrayOrder: true, ignoreArrayOrder: true,
}) })
).toBeTruthy(); ).toBeTruthy();
expect( expect(
isEqual([44, 45, "world"], [45, "world", 44], { discardArrayOrder: true }) isEqual([44, 45, "world"], [45, "world", 44], { ignoreArrayOrder: true })
).toBeTruthy(); ).toBeTruthy();
expect( expect(
isEqual( isEqual(
@ -60,24 +60,24 @@ describe("isEqual", () => {
{ name: "joe", age: 88 }, { name: "joe", age: 88 },
], ],
{ {
discardArrayOrder: true, ignoreArrayOrder: true,
} }
) )
).toBeTruthy(); ).toBeTruthy();
expect( expect(
isEqual([true, 55, "hello"], ["hello", 55, true], { isEqual([true, 55, "hello"], ["hello", 55, true], {
discardArrayOrder: true, ignoreArrayOrder: true,
}) })
).toBeTruthy(); ).toBeTruthy();
}); });
it("return false if discardArrayOrder option is activated but the arrays don't contain the same values", () => { it("return false if ignoreArrayOrder option is activated but the arrays don't contain the same values", () => {
expect( expect(
isEqual(["hello"], ["world", "hello"], { isEqual(["hello"], ["world", "hello"], {
discardArrayOrder: true, ignoreArrayOrder: true,
}) })
).toBeFalsy(); ).toBeFalsy();
expect( expect(
isEqual([44, 47, "world"], [45, "world", 44], { discardArrayOrder: true }) isEqual([44, 47, "world"], [45, "world", 44], { ignoreArrayOrder: true })
).toBeFalsy(); ).toBeFalsy();
expect( expect(
isEqual( isEqual(
@ -90,13 +90,13 @@ describe("isEqual", () => {
{ name: "joe", age: 88 }, { name: "joe", age: 88 },
], ],
{ {
discardArrayOrder: true, ignoreArrayOrder: true,
} }
) )
).toBeFalsy(); ).toBeFalsy();
expect( expect(
isEqual([false, 55, "hello"], ["hello", 55, true], { isEqual([false, 55, "hello"], ["hello", 55, true], {
discardArrayOrder: true, ignoreArrayOrder: true,
}) })
).toBeFalsy(); ).toBeFalsy();
}); });

Loading…
Cancel
Save