浏览代码

Merge pull request #1514 from Avaiga/test/formatConversion

Unit test for format conversion
Nam Nguyen 10 月之前
父节点
当前提交
03b95d0782
共有 2 个文件被更改,包括 129 次插入21 次删除
  1. 20 21
      frontend/taipy-gui/src/utils/formatConversion.ts
  2. 109 0
      frontend/taipy-gui/src/utils/utils.spec.ts

+ 20 - 21
frontend/taipy-gui/src/utils/formatConversion.ts

@@ -15,8 +15,8 @@
  * Regular expressions used for parsing sprintf format strings.
  */
 const re = {
-    text: /^[^\x25]+/,                         // Matches non-placeholder text
-    modulo: /^\x25{2}/,                        // Matches the '%%' escape sequence
+    text: /^[^\x25]+/, // Matches non-placeholder text
+    modulo: /^\x25{2}/, // Matches the '%%' escape sequence
     placeholder: /^\x25?(?:\.(\d+))?([b-giostuvxX])/, // Matches placeholders
 };
 
@@ -24,16 +24,16 @@ const re = {
  * This function formats a precision specifier for a number. It takes an optional precision and specifier string.
  * If no precision is provided, it defaults to 2. The function returns a string that represents the formatted precision.
  */
-const precisionFormat = (precision?: string, specifier?: string): string => {
+export const precisionFormat = (precision?: string, specifier?: string): string => {
     // Default to precision of 2 if not specified
     return "." + (precision?.slice(1) ?? "2") + specifier;
-}
+};
 
 /*
  * This function parses a sprintf format string and returns an array of strings and objects. Each object has a single
  * key, 'placeholder', that contains the placeholder string.
  */
-const sprintfParse = (fmt?: string): (string | { placeholder: string; })[] => {
+export const sprintfParse = (fmt?: string): (string | { placeholder: string })[] => {
     let _fmt = fmt;
     let match;
     const parseTree = [];
@@ -44,7 +44,7 @@ const sprintfParse = (fmt?: string): (string | { placeholder: string; })[] => {
             parseTree.push(match[0]);
         } else if ((match = re.modulo.exec(_fmt)) !== null) {
             // '%%' escape sequence
-            parseTree.push('%');
+            parseTree.push("%");
         } else if ((match = re.placeholder.exec(_fmt)) !== null) {
             // Placeholder
             if (match && match[0]) {
@@ -52,15 +52,17 @@ const sprintfParse = (fmt?: string): (string | { placeholder: string; })[] => {
                     placeholder: match[0],
                 });
             }
+        } else {
+            // If none of the conditions are met, break the loop
+            break;
         }
 
         if (match) {
             _fmt = _fmt.substring(match[0].length);
         }
     }
-
     return parseTree;
-}
+};
 
 /*
  * This function converts a sprintf format string to a D3 format string. It takes an optional sprintf format string and
@@ -68,10 +70,10 @@ const sprintfParse = (fmt?: string): (string | { placeholder: string; })[] => {
  */
 export const sprintfToD3Converter = (fmt?: string): string => {
     const sprintfFmtArr = sprintfParse(fmt);
-    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === 'object');
+    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === "object");
     let placeholderValue;
 
-    if (typeof sprintfFmtArr[objectIndex] === 'object' && sprintfFmtArr[objectIndex] !== null) {
+    if (typeof sprintfFmtArr[objectIndex] === "object" && sprintfFmtArr[objectIndex] !== null) {
         placeholderValue = (sprintfFmtArr[objectIndex] as { placeholder: string }).placeholder;
     }
 
@@ -94,12 +96,10 @@ export const sprintfToD3Converter = (fmt?: string): string => {
             case "g":
                 return precisionFormat(precision, type);
             case "u":
-                return "("
-            default:
-                return "";
+                return "(";
         }
     });
-}
+};
 
 /*
  * This function extracts the prefix from a sprintf format string. It takes an optional sprintf format string and returns
@@ -108,9 +108,9 @@ export const sprintfToD3Converter = (fmt?: string): string => {
 export const extractPrefix = (fmt?: string): string => {
     if (!fmt) return "";
     const sprintfFmtArr = sprintfParse(fmt);
-    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === 'object');
-    return sprintfFmtArr.slice(0, objectIndex).join('');
-}
+    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === "object");
+    return sprintfFmtArr.slice(0, objectIndex).join("");
+};
 
 /*
  * This function extracts the suffix from a sprintf format string. It takes an optional sprintf format string and returns
@@ -119,7 +119,6 @@ export const extractPrefix = (fmt?: string): string => {
 export const extractSuffix = (fmt?: string): string => {
     if (!fmt) return "";
     const sprintfFmtArr = sprintfParse(fmt);
-    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === 'object');
-    return sprintfFmtArr.slice(objectIndex + 1).join('');
-}
-
+    const objectIndex = sprintfFmtArr.findIndex((element) => typeof element === "object");
+    return sprintfFmtArr.slice(objectIndex + 1).join("");
+};

+ 109 - 0
frontend/taipy-gui/src/utils/utils.spec.ts

@@ -0,0 +1,109 @@
+/*
+ * Copyright 2021-2024 Avaiga Private Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+import { extractPrefix, extractSuffix, precisionFormat, sprintfParse, sprintfToD3Converter } from "./formatConversion";
+
+function extractSuffixWrapper(formatString: string): string {
+    let result = extractSuffix(formatString);
+    result = result.replace("[object Object]", "%d");
+    return result;
+}
+
+describe("format conversion", () => {
+    it("returns formatted precision with provided precision and specifier", () => {
+        expect(precisionFormat(".5", "f")).toBe(".5f");
+    });
+    it("returns formatted precision with default precision when none is provided", () => {
+        expect(precisionFormat(undefined, "f")).toBe(".2f");
+    });
+    it("returns empty string when no format string is provided", () => {
+        expect(sprintfToD3Converter()).toBe("");
+    });
+    it("should parse non-placeholder text", () => {
+        const result = sprintfParse("Hello, World!");
+        expect(result).toEqual(["Hello, World!"]);
+    });
+    it('should parse the "%%" escape sequence', () => {
+        const result = sprintfParse("%%");
+        expect(result).toEqual(["%"]);
+    });
+    it("should parse placeholders", () => {
+        const result = sprintfParse("%d");
+        expect(result).toEqual([{ placeholder: "%d" }]);
+    });
+    it("should parse complex format strings", () => {
+        const result = sprintfParse("Hello, %s. You have %d new messages.");
+        expect(result).toEqual([
+            "Hello, ",
+            { placeholder: "%s" },
+            ". You have ",
+            { placeholder: "%d" },
+            " new messages.",
+        ]);
+    });
+    it("should extract placeholder value", () => {
+        const result = sprintfToD3Converter("%d");
+        expect(result).toBe("d");
+    });
+    it("should extract prefix from format string", () => {
+        const result = extractPrefix("Hello, %s. You have %d new messages.");
+        expect(result).toBe("Hello, ");
+    });
+    it("should extract suffix from format string", () => {
+        const result = extractSuffixWrapper("Hello, %s. You have %d new messages.");
+        expect(result).toBe(". You have %d new messages.");
+    });
+    it("should return empty string when no format string is provided to extractPrefix", () => {
+        const result = extractPrefix();
+        expect(result).toBe("");
+    });
+    it("should return empty string when no format string is provided to extractSuffix", () => {
+        const result = extractSuffix();
+        expect(result).toBe("");
+    });
+    it("should break the loop for invalid placeholder", () => {
+        const result = sprintfParse("Hello, %z");
+        expect(result).toEqual(["Hello, "]);
+    });
+    it("should return 'b' for '%b'", () => {
+        expect(sprintfToD3Converter("%b")).toBe("b");
+    });
+    it("should return 'e' for '%e'", () => {
+        expect(sprintfToD3Converter("%e")).toBe("e");
+    });
+
+    it("should return 'o' for '%o'", () => {
+        expect(sprintfToD3Converter("%o")).toBe("o");
+    });
+    it("should return 'x' for '%x'", () => {
+        expect(sprintfToD3Converter("%x")).toBe("x");
+    });
+    it("should return 'X' for '%X'", () => {
+        expect(sprintfToD3Converter("%X")).toBe("X");
+    });
+    it("should return 'd' for '%i'", () => {
+        expect(sprintfToD3Converter("%i")).toBe("d");
+    });
+    it("should return '.2f' for '%f'", () => {
+        expect(sprintfToD3Converter("%f")).toBe(".2f");
+    });
+    it("should return '.2g' for '%g'", () => {
+        expect(sprintfToD3Converter("%g")).toBe(".2g");
+    });
+    it("should return '(' for '%u'", () => {
+        expect(sprintfToD3Converter("%u")).toBe("(");
+    });
+    it("should return '' for unsupported converter", () => {
+        expect(sprintfToD3Converter("hi")).toBe("");
+    });
+});