From 3ae9d121a161da0c93a891a901e5296bbfe6e577 Mon Sep 17 00:00:00 2001
From: "Jamie V." <jamie.j.vigliotta@nasa.gov>
Date: Mon, 9 Dec 2024 12:34:07 -0800
Subject: [PATCH] =?UTF-8?q?modified=20the=20sanitizeForSerialization=20met?=
 =?UTF-8?q?hod=20to=20remove=20unnecessary=20re=E2=80=A6=20(#7950)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

modified the sanitizeForSerialization method to remove unnecessary recursion, update e2e test to CORRECTLY test the functionality
---
 .../displayLayout/displayLayout.e2e.spec.js   | 59 ++++++++-----------
 src/api/telemetry/TelemetryAPI.js             | 32 +++++-----
 2 files changed, 39 insertions(+), 52 deletions(-)

diff --git a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js
index 8b380dd6c1..a2e8fd42a7 100644
--- a/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js
+++ b/e2e/tests/functional/plugins/displayLayout/displayLayout.e2e.spec.js
@@ -599,46 +599,33 @@ test.describe('Display Layout', () => {
 
     // Verify filtering is working correctly
 
-    // Create a promise that resolves when we've seen enough new rows added
-    const rowsMutationPromise = page.evaluate(() => {
-      return new Promise((resolve) => {
-        const targetTable = document.querySelector(
-          'table[aria-label="Table Filter Off Value table content"]'
-        );
-        const config = { childList: true, subtree: true };
-        let changeCount = 0;
-        const requiredChanges = 20; // Number of changes to wait for
+    // Check that no filtered values appear for at least 2 seconds
+    const VERIFICATION_TIME = 2000; // 2 seconds
+    const CHECK_INTERVAL = 100; // Check every 100ms
 
-        const observer = new MutationObserver((mutations) => {
-          mutations.forEach((mutation) => {
-            if (mutation.type === 'childList') {
-              // Count added nodes
-              changeCount += mutation.addedNodes.length;
-            }
-          });
+    // Create a promise that will check for filtered values periodically
+    const checkForCorrectValues = new Promise((resolve, reject) => {
+      const interval = setInterval(async () => {
+        const offCount = await tableFilterOn.locator('td[title="OFF"]').count();
+        const onCount = await tableFilterOff.locator('td[title="ON"]').count();
+        if (offCount > 0 || onCount > 0) {
+          clearInterval(interval);
+          reject(
+            new Error(
+              `Found ${offCount} OFF and ${onCount} ON values when expecting 0 OFF and 0 ON`
+            )
+          );
+        }
+      }, CHECK_INTERVAL);
 
-          // Check if the required number of changes has been met
-          if (changeCount >= requiredChanges) {
-            observer.disconnect(); // Disconnect observer after the required changes
-            resolve();
-          }
-        });
-
-        observer.observe(targetTable, config);
-      });
+      // After VERIFICATION_TIME, if no filtered values were found, resolve successfully
+      setTimeout(() => {
+        clearInterval(interval);
+        resolve();
+      }, VERIFICATION_TIME);
     });
 
-    await rowsMutationPromise;
-
-    // Check ON table doesn't have any OFF values
-    await expect(tableFilterOn.locator('td[title="OFF"]')).toHaveCount(0);
-    const onCount = await tableFilterOn.locator('td[title="ON"]').count();
-    await expect(onCount).toBeGreaterThan(0);
-
-    // Check OFF table doesn't have any ON values
-    await expect(tableFilterOff.locator('td[title="ON"]')).toHaveCount(0);
-    const offCount = await tableFilterOff.locator('td[title="OFF"]').count();
-    await expect(offCount).toBeGreaterThan(0);
+    await expect(checkForCorrectValues).resolves.toBeUndefined();
   });
 });
 
diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js
index 2b23a6ab19..e5be70590f 100644
--- a/src/api/telemetry/TelemetryAPI.js
+++ b/src/api/telemetry/TelemetryAPI.js
@@ -254,12 +254,6 @@ export default class TelemetryAPI {
    * Sanitizes objects for consistent serialization by:
    * 1. Removing non-plain objects (class instances) and functions
    * 2. Sorting object keys alphabetically to ensure consistent ordering
-   * 3. Recursively processing nested objects
-   *
-   * Note: When used as a JSON.stringify replacer, this function will process objects
-   * twice - once for the initial sorting and again when JSON.stringify processes the
-   * sorted result. This is acceptable for small options objects, which is the
-   * intended use case.
    */
   sanitizeForSerialization(key, value) {
     // Handle null and primitives directly
@@ -267,21 +261,27 @@ export default class TelemetryAPI {
       return value;
     }
 
-    // Remove functions and non-plain objects by returning undefined
-    if (typeof value === 'function' || Object.getPrototypeOf(value) !== Object.prototype) {
+    // Remove functions and non-plain objects (except arrays)
+    if (
+      typeof value === 'function' ||
+      (Object.getPrototypeOf(value) !== Object.prototype && !Array.isArray(value))
+    ) {
       return undefined;
     }
 
-    // Handle plain objects
-    const sortedObject = {};
-    const keys = Object.keys(value).sort();
-    for (const objectKey of keys) {
-      const itemValue = value[objectKey];
-      const sanitizedValue = this.sanitizeForSerialization(objectKey, itemValue);
-      sortedObject[objectKey] = sanitizedValue;
+    // For plain objects, just sort the keys
+    if (!Array.isArray(value)) {
+      const sortedObject = {};
+      const sortedKeys = Object.keys(value).sort();
+
+      sortedKeys.forEach((objectKey) => {
+        sortedObject[objectKey] = value[objectKey];
+      });
+
+      return sortedObject;
     }
 
-    return sortedObject;
+    return value;
   }
 
   /**