From ec1c7166b9b849beaee3152ad695e87e0ee9c465 Mon Sep 17 00:00:00 2001
From: DoneDeal0 <ap.lanoe@outlook.com>
Date: Fri, 23 Dec 2022 22:58:16 +0100
Subject: [PATCH] fix: deep equality check

---
 src/object-diff.ts       |  33 ++--
 src/utils.ts             |   3 +
 test/object-diff.test.ts | 329 +++++++++++++++++----------------------
 test/utils.test.ts       |   1 +
 4 files changed, 167 insertions(+), 199 deletions(-)

diff --git a/src/object-diff.ts b/src/object-diff.ts
index 3893619..d5189ff 100644
--- a/src/object-diff.ts
+++ b/src/object-diff.ts
@@ -62,11 +62,12 @@ function getPreviousMatch(
   prevSubValues: [string, any][] | null,
   nextSubProperty: any
 ): any | undefined {
-  const previousMatch =
-    prevSubValues &&
-    prevSubValues.find(([subPreviousKey]) =>
-      isEqual(subPreviousKey, nextSubProperty)
-    );
+  if (!prevSubValues) {
+    return undefined;
+  }
+  const previousMatch = prevSubValues.find(([subPreviousKey]) =>
+    isEqual(subPreviousKey, nextSubProperty)
+  );
   return previousMatch ? previousMatch[1] : undefined;
 }
 
@@ -88,18 +89,16 @@ function getSubPropertiesDiff(
         subDiff = data;
       }
     }
-    if (prevSubValues) {
-      if (previousMatch) {
-        subPropertiesDiff.push({
-          name: nextSubProperty,
-          previousValue: previousMatch,
-          currentValue: nextSubValue,
-          status: isEqual(previousMatch, nextSubValue)
-            ? STATUS.EQUAL
-            : STATUS.UPDATED,
-          ...(!!subDiff && { subDiff }),
-        });
-      }
+    if (previousMatch) {
+      subPropertiesDiff.push({
+        name: nextSubProperty,
+        previousValue: previousMatch,
+        currentValue: nextSubValue,
+        status: isEqual(previousMatch, nextSubValue)
+          ? STATUS.EQUAL
+          : STATUS.UPDATED,
+        ...(!!subDiff && { subDiff }),
+      });
     }
   });
   return subPropertiesDiff;
diff --git a/src/utils.ts b/src/utils.ts
index 55681d5..c526893 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,6 +1,9 @@
 export function isEqual(a: any, b: any): boolean {
   if (typeof a !== typeof b) return false;
   if (Array.isArray(a)) {
+    if (a.length !== b.length) {
+      return false;
+    }
     return a.every((v, i) => JSON.stringify(v) === JSON.stringify(b[i]));
   }
   if (typeof a === "object") {
diff --git a/test/object-diff.test.ts b/test/object-diff.test.ts
index a65b170..c561d9a 100644
--- a/test/object-diff.test.ts
+++ b/test/object-diff.test.ts
@@ -1,189 +1,154 @@
 import { getObjectDiff } from "../src/object-diff";
 
 describe("getObjectDiff", () => {
-  // it("returns an empty diff if no objects are provided", () => {
-  //   expect(getObjectDiff(null, null)).toStrictEqual({
-  //     type: "object",
-  //     status: "equal",
-  //     diff: [],
-  //   });
-  // });
-  // it("consider previous object as completely deleted if no next object is provided", () => {
-  //   expect(
-  //     getObjectDiff(
-  //       { name: "joe", age: 54, hobbies: ["golf", "football"] },
-  //       null
-  //     )
-  //   ).toStrictEqual({
-  //     type: "object",
-  //     status: "deleted",
-  //     diff: [
-  //       {
-  //         property: "name",
-  //         previousValue: "joe",
-  //         currentValue: undefined,
-  //         status: "deleted",
-  //       },
-  //       {
-  //         property: "age",
-  //         previousValue: 54,
-  //         currentValue: undefined,
-  //         status: "deleted",
-  //       },
-  //       {
-  //         property: "hobbies",
-  //         previousValue: ["golf", "football"],
-  //         currentValue: undefined,
-  //         status: "deleted",
-  //       },
-  //     ],
-  //   });
-  // });
-  // it("consider next object as completely added if no previous object is provided", () => {
-  //   expect(
-  //     getObjectDiff(null, {
-  //       name: "joe",
-  //       age: 54,
-  //       hobbies: ["golf", "football"],
-  //     })
-  //   ).toStrictEqual({
-  //     type: "object",
-  //     status: "added",
-  //     diff: [
-  //       {
-  //         property: "name",
-  //         previousValue: undefined,
-  //         currentValue: "joe",
-  //         status: "added",
-  //       },
-  //       {
-  //         property: "age",
-  //         previousValue: undefined,
-  //         currentValue: 54,
-  //         status: "added",
-  //       },
-  //       {
-  //         property: "hobbies",
-  //         previousValue: undefined,
-  //         currentValue: ["golf", "football"],
-  //         status: "added",
-  //       },
-  //     ],
-  //   });
-  // });
-  // it("detects changed between two objects", () => {
-  //   expect(
-  //     getObjectDiff(
-  //       {
-  //         id: 54,
-  //         user: {
-  //           name: "joe",
-  //           member: true,
-  //           hobbies: ["golf", "football"],
-  //           age: 66,
-  //         },
-  //       },
-  //       {
-  //         id: 54,
-  //         user: {
-  //           name: "joe",
-  //           member: false,
-  //           hobbies: ["golf", "chess"],
-  //           age: 66,
-  //         },
-  //       }
-  //     )
-  //   ).toStrictEqual({
-  //     type: "object",
-  //     status: "updated",
-  //     diff: [
-  //       {
-  //         property: "id",
-  //         previousValue: 54,
-  //         currentValue: 54,
-  //         status: "equal",
-  //       },
-  //       {
-  //         property: "user",
-  //         previousValue: {
-  //           name: "joe",
-  //           member: true,
-  //           hobbies: ["golf", "football"],
-  //           age: 66,
-  //         },
-  //         currentValue: {
-  //           name: "joe",
-  //           member: false,
-  //           hobbies: ["golf", "chess"],
-  //           age: 66,
-  //         },
-  //         status: "updated",
-  //         subPropertiesDiff: [
-  //           {
-  //             name: "name",
-  //             previousValue: "joe",
-  //             currentValue: "joe",
-  //             status: "equal",
-  //           },
-  //           {
-  //             name: "member",
-  //             previousValue: true,
-  //             currentValue: false,
-  //             status: "updated",
-  //           },
-  //           {
-  //             name: "hobbies",
-  //             previousValue: ["golf", "football"],
-  //             currentValue: ["golf", "chess"],
-  //             status: "updated",
-  //           },
-  //           {
-  //             name: "age",
-  //             previousValue: 66,
-  //             currentValue: 66,
-  //             status: "equal",
-  //           },
-  //         ],
-  //       },
-  //     ],
-  //   });
-  // });
-  it("detects changed between two deep nested objects", () => {
-    console.log(
-      "res",
-      JSON.stringify(
-        getObjectDiff(
-          {
-            id: 54,
-            user: {
-              name: "joe",
-              data: {
-                member: true,
-                hobbies: {
-                  football: ["psg"],
-                  rugby: ["france"],
-                },
-              },
-            },
+  it("returns an empty diff if no objects are provided", () => {
+    expect(getObjectDiff(null, null)).toStrictEqual({
+      type: "object",
+      status: "equal",
+      diff: [],
+    });
+  });
+  it("consider previous object as completely deleted if no next object is provided", () => {
+    expect(
+      getObjectDiff(
+        { name: "joe", age: 54, hobbies: ["golf", "football"] },
+        null
+      )
+    ).toStrictEqual({
+      type: "object",
+      status: "deleted",
+      diff: [
+        {
+          property: "name",
+          previousValue: "joe",
+          currentValue: undefined,
+          status: "deleted",
+        },
+        {
+          property: "age",
+          previousValue: 54,
+          currentValue: undefined,
+          status: "deleted",
+        },
+        {
+          property: "hobbies",
+          previousValue: ["golf", "football"],
+          currentValue: undefined,
+          status: "deleted",
+        },
+      ],
+    });
+  });
+  it("consider next object as completely added if no previous object is provided", () => {
+    expect(
+      getObjectDiff(null, {
+        name: "joe",
+        age: 54,
+        hobbies: ["golf", "football"],
+      })
+    ).toStrictEqual({
+      type: "object",
+      status: "added",
+      diff: [
+        {
+          property: "name",
+          previousValue: undefined,
+          currentValue: "joe",
+          status: "added",
+        },
+        {
+          property: "age",
+          previousValue: undefined,
+          currentValue: 54,
+          status: "added",
+        },
+        {
+          property: "hobbies",
+          previousValue: undefined,
+          currentValue: ["golf", "football"],
+          status: "added",
+        },
+      ],
+    });
+  });
+  it("detects changed between two objects", () => {
+    expect(
+      getObjectDiff(
+        {
+          id: 54,
+          user: {
+            name: "joe",
+            member: true,
+            hobbies: ["golf", "football"],
+            age: 66,
           },
-          {
-            id: 54,
-            user: {
-              name: "joe",
-              data: {
-                member: true,
-                hobbies: {
-                  football: ["psg", "nantes"],
-                  rugby: ["france"],
-                },
-              },
-            },
-          }
-        ),
-        null,
-        2
+        },
+        {
+          id: 54,
+          user: {
+            name: "joe",
+            member: false,
+            hobbies: ["golf", "chess"],
+            age: 66,
+          },
+        }
       )
-    );
+    ).toStrictEqual({
+      type: "object",
+      status: "updated",
+      diff: [
+        {
+          property: "id",
+          previousValue: 54,
+          currentValue: 54,
+          status: "equal",
+        },
+        {
+          property: "user",
+          previousValue: {
+            name: "joe",
+            member: true,
+            hobbies: ["golf", "football"],
+            age: 66,
+          },
+          currentValue: {
+            name: "joe",
+            member: false,
+            hobbies: ["golf", "chess"],
+            age: 66,
+          },
+          status: "updated",
+          subPropertiesDiff: [
+            {
+              name: "name",
+              previousValue: "joe",
+              currentValue: "joe",
+              status: "equal",
+            },
+            {
+              name: "member",
+              previousValue: true,
+              currentValue: false,
+              status: "updated",
+            },
+            {
+              name: "hobbies",
+              previousValue: ["golf", "football"],
+              currentValue: ["golf", "chess"],
+              status: "updated",
+            },
+            {
+              name: "age",
+              previousValue: 66,
+              currentValue: 66,
+              status: "equal",
+            },
+          ],
+        },
+      ],
+    });
+  });
+  it("detects changed between two deep nested objects", () => {
     expect(
       getObjectDiff(
         {
@@ -293,7 +258,7 @@ describe("getObjectDiff", () => {
                       name: "football",
                       previousValue: ["psg"],
                       currentValue: ["psg", "nantes"],
-                      status: "updated", // error the algo says it's equal...
+                      status: "updated",
                     },
                     {
                       name: "rugby",
diff --git a/test/utils.test.ts b/test/utils.test.ts
index b751386..babe049 100644
--- a/test/utils.test.ts
+++ b/test/utils.test.ts
@@ -38,6 +38,7 @@ describe("isEqual", () => {
         ]
       )
     ).toBeFalsy();
+    expect(isEqual(["psg"], ["psg", "nantes"])).toBeFalsy();
   });
 });