Repository: asyncapi/modelina (ts)

Issue:
Exclude `_` from TypeScript key enum rendering
#### Reason/Context
Given an enum key such as `something_something` the enum renderer creates the following key `SOMETHING_UNDERSCORE_SOMETHING` even when TS fully supports `SOMETHING_SOMETHING`


#### Description

We need to add `_` to the exclusion list here: https://github.com/asyncapi/modelina/blob/71ac65a5aafd44731f7d421aec88c2ccfbfc2cad/src/generators/typescript/renderers/EnumRenderer.ts#L53

Pull request description:
feat: exclude underscore from TS key enum rendering
**Description**

- Added `_` to the exclusion list inside TS key enum renderer.
- Added tests for EnumRenderer.

**Related issue(s)**
Resolves #609 

Patch:
diff --git a/package-lock.json b/package-lock.json
index 5ddc32fc..0e4f8d6d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26301,4 +26301,4 @@
       }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/generators/typescript/renderers/EnumRenderer.ts b/src/generators/typescript/renderers/EnumRenderer.ts
index 045fb134..024cc41a 100644
--- a/src/generators/typescript/renderers/EnumRenderer.ts
+++ b/src/generators/typescript/renderers/EnumRenderer.ts
@@ -50,7 +50,7 @@ ${this.indent(this.renderBlock(content, 2))}
       break;
     }
     default: {
-      key = FormatHelpers.replaceSpecialCharacters(String(value), { exclude: [' '], separator: '_' });
+      key = FormatHelpers.replaceSpecialCharacters(String(value), { exclude: [' ','_'], separator: '_' });
       //Ensure no special char can be the beginning letter 
       if (!(/^[a-zA-Z]+$/).test(key.charAt(0))) {
         key = `String_${key}`;


Test patch:
diff --git a/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap b/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap
index 31631df5..f79f1786 100644
--- a/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap
+++ b/test/generators/javascript/preset/__snapshots__/MarshallingPreset.spec.ts.snap
@@ -40,51 +40,51 @@ exports[`Marshalling preset should render un/marshal code 1`] = `
   marshal(){
     let json = '{'
     if(this.stringProp !== undefined) {
-      json += `"string prop": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; 
+      json += \`\\"string prop\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; 
     }
     if(this.numberProp !== undefined) {
-      json += `"numberProp": ${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},`; 
+      json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; 
     }
     if(this.objectProp !== undefined) {
-      json += `"objectProp": ${this.objectProp.marshal()},`; 
+      json += \`\\"objectProp\\": \${this.objectProp.marshal()},\`; 
     }
     if(this.sTestPatternProperties !== undefined) { 
       for (const [key, value] of this.sTestPatternProperties.entries()) {
         //Only render pattern properties which are not already a property
         if(Object.keys(this).includes(String(key))) continue;
-        json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`;
+        json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`;
       }
     }if(this.sAnotherTestPatternProperties !== undefined) { 
       for (const [key, value] of this.sAnotherTestPatternProperties.entries()) {
         //Only render pattern properties which are not already a property
         if(Object.keys(this).includes(String(key))) continue;
-        json += `"${key}": ${value.marshal()},`;
+        json += \`\\"\${key}\\": \${value.marshal()},\`;
       }
     }
     if(this.additionalProperties !== undefined) { 
       for (const [key, value] of this.additionalProperties.entries()) {
         //Only render additionalProperties which are not already a property
         if(Object.keys(this).includes(String(key))) continue;
-        json += `"${key}": ${value.marshal()},`;
+        json += \`\\"\${key}\\": \${value.marshal()},\`;
       }
     }
 
     //Remove potential last comma 
-    return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`;
+    return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`;
   }
 
   unmarshal(json){
-    const obj = typeof json === "object" ? json : JSON.parse(json);
+    const obj = typeof json === \\"object\\" ? json : JSON.parse(json);
     const instance = new Test({});
 
-    if (obj["string prop"] !== undefined) {
-      instance.stringProp = obj["string prop"];
+    if (obj[\\"string prop\\"] !== undefined) {
+      instance.stringProp = obj[\\"string prop\\"];
     }
-    if (obj["numberProp"] !== undefined) {
-      instance.numberProp = obj["numberProp"];
+    if (obj[\\"numberProp\\"] !== undefined) {
+      instance.numberProp = obj[\\"numberProp\\"];
     }
-    if (obj["objectProp"] !== undefined) {
-      instance.objectProp = NestedTest.unmarshal(obj["objectProp"]);
+    if (obj[\\"objectProp\\"] !== undefined) {
+      instance.objectProp = NestedTest.unmarshal(obj[\\"objectProp\\"]);
     }
 
     //Not part of core properties
@@ -92,7 +92,7 @@ exports[`Marshalling preset should render un/marshal code 1`] = `
   if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();}
 
     if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();}
-    for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["string prop","numberProp","objectProp"].includes(key);}))) {
+    for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"numberProp\\",\\"objectProp\\"].includes(key);}))) {
       //Check all pattern properties
       if (key.match(new RegExp('^S(.?)test'))) {
         instance.sTestPatternProperties.set(key, value);
@@ -129,33 +129,33 @@ exports[`Marshalling preset should render un/marshal code 2`] = `
   marshal(){
     let json = '{'
     if(this.stringProp !== undefined) {
-      json += `"stringProp": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; 
+      json += \`\\"stringProp\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; 
     }
   
     if(this.additionalProperties !== undefined) { 
       for (const [key, value] of this.additionalProperties.entries()) {
         //Only render additionalProperties which are not already a property
         if(Object.keys(this).includes(String(key))) continue;
-        json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`;
+        json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`;
       }
     }
 
     //Remove potential last comma 
-    return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`;
+    return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`;
   }
 
   unmarshal(json){
-    const obj = typeof json === "object" ? json : JSON.parse(json);
+    const obj = typeof json === \\"object\\" ? json : JSON.parse(json);
     const instance = new NestedTest({});
 
-    if (obj["stringProp"] !== undefined) {
-      instance.stringProp = obj["stringProp"];
+    if (obj[\\"stringProp\\"] !== undefined) {
+      instance.stringProp = obj[\\"stringProp\\"];
     }
 
     //Not part of core properties
   
     if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();}
-    for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["stringProp"].includes(key);}))) {
+    for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\"].includes(key);}))) {
     
       instance.additionalProperties.set(key, value);
     }
diff --git a/test/generators/typescript/renderers/EnumRenderer.spec.ts b/test/generators/typescript/renderers/EnumRenderer.spec.ts
new file mode 100644
index 00000000..e305031f
--- /dev/null
+++ b/test/generators/typescript/renderers/EnumRenderer.spec.ts
@@ -0,0 +1,21 @@
+import { TypeScriptGenerator } from '../../../../src/generators';
+import { EnumRenderer } from '../../../../src/generators/typescript/renderers/EnumRenderer';
+import { CommonInputModel, CommonModel } from '../../../../src/models';
+
+describe('EnumRenderer', () => {
+  let renderer: EnumRenderer;
+  beforeEach(() => {
+    renderer = new EnumRenderer(TypeScriptGenerator.defaultOptions, new TypeScriptGenerator(), [], new CommonModel(), new CommonInputModel());
+  });
+
+  describe('normalizeKey()', () => {
+    test('should correctly format " " to correct key', () => {
+      const key = renderer.normalizeKey('something something');
+      expect(key).toEqual('SOMETHING_SOMETHING');
+    });
+    test('should correctly format "_" to correct key', () => {
+      const key = renderer.normalizeKey('something_something');
+      expect(key).toEqual('SOMETHING_SOMETHING');
+    });
+  });
+});