phodal/chapi

View on GitHub
chapi-ast-typescript/src/test/kotlin/chapi/ast/typescriptast/TypeScriptFullIdentListenerTest.kt

Summary

Maintainability
F
1 wk
Test Coverage
package chapi.ast.typescriptast

import chapi.domain.core.DataStructType
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

internal class TypeScriptFullIdentListenerTest {

    @Test
    internal fun shouldIdentTypeScriptHelloWorldFunCall() {
        val content = """
let greeting = function() {
    console.log("Hello TypeScript!");
};
        """

        val codeFile = TypeScriptAnalyser().analysis(content, "")

        val firstFunc = codeFile.DataStructures[0].Functions[0]
        Assertions.assertEquals(firstFunc.FunctionCalls.size, 1)
        Assertions.assertEquals(firstFunc.FunctionCalls[0].NodeName, "console")
        Assertions.assertEquals(firstFunc.FunctionCalls[0].FunctionName, "log")
    }

    @Test
    internal fun shouldIndentTypeScriptDecorator() {
        val content = """
import { Injectable } from '@angular/core';

@Injectable()
export class LedgeStorageService {
  get storage() {
    return window.localStorage;
  }
}
        """

        val codeFile = TypeScriptAnalyser().analysis(content, "")
        val annotations = codeFile.DataStructures[0].Annotations
        Assertions.assertEquals(annotations.size, 1)
        Assertions.assertEquals(annotations[0].Name, "Injectable")

        val firstFunc = codeFile.DataStructures[0].Functions[0]
        Assertions.assertEquals(firstFunc.Name, "get")
    }


    @Test
    internal fun shouldIdentClassPosition() {
        val content = """
import { Injectable } from '@angular/core';

@Injectable()
export class LedgeStorageService {
  get storage() {
    return window.localStorage;
  }
}
        """

        val codeFile = TypeScriptAnalyser().analysis(content, "")
        val ds = codeFile.DataStructures[0]
        val annotations = ds.Annotations
        Assertions.assertEquals(annotations.size, 1)
        Assertions.assertEquals(ds.Position.StartLine, 5)
        Assertions.assertEquals(ds.Position.StopLine, 9)
    }

    @Test
    internal fun shouldIndentTypeScriptDecoratorKeyValues() {
        val content = """
import { Component } from '@angular/core';

@Component({
  selector: 'app-design',
  templateUrl: './design.component.html',
  styleUrls: ['./design.component.scss'],
})
export class DesignComponent {

}

        """

        val codeFile = TypeScriptAnalyser().analysis(content, "")
        val annotations = codeFile.DataStructures[0].Annotations
        Assertions.assertEquals(annotations.size, 1)
    }

    @Test
    internal fun shouldIdentifyInterfaceName() {
        val code = """
export interface IPerson {
    name: string;
    gender: string;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "iperson.ts")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].NodeName, "IPerson")
        assertEquals(codeFile.DataStructures[0].Type, DataStructType.INTERFACE)
        assertEquals(codeFile.DataStructures[0].FilePath, "iperson.ts")
    }

    @Test
    internal fun shouldIdentifyInterfacesWithMethod() {
        val code = """
export interface ProfileConfig {
  layer: LayerKeys;
  quota: string;
  operator: "BIGGER" | "LESS" | "EQUAL";
  value: number;
}

export interface Profile {
  id?: number;
  name: string;
  config: ProfileConfig[];
}

const QualityGateProfile = () => {

}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "iperson.ts")
        val structs = codeFile.DataStructures
        assertEquals(structs.size, 3)
        assertEquals("ProfileConfig", structs[0].NodeName)
        assertEquals("Profile", structs[1].NodeName)
        assertEquals("default", structs[2].NodeName)
    }

    @Test
    internal fun shouldIdentifyInnerClass() {
        val code = """
class Foo {
    static Bar = class {

    }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Type, DataStructType.CLASS)
        assertEquals(codeFile.DataStructures[0].NodeName, "Foo")
    }

    @Test
    internal fun shouldIdentifyMultipleNode() {
        val code = """
interface IPerson {
    name: string;
}

class Person implements IPerson {
    public publicString: string;
    private privateString: string;
    protected protectedString: string;
    readonly readonlyString: string;
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 2)
        assertEquals(codeFile.DataStructures[0].NodeName, "IPerson")
        assertEquals(codeFile.DataStructures[1].NodeName, "Person")
    }

    @Test
    internal fun shouldIdentifyImplements() {
        val code = """
class Person implements IPerson {
    public publicString: string;
    private privateString: string;
    protected protectedString: string;
    readonly readonlyString: string;
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Implements[0], "IPerson")
    }

    @Test
    internal fun shouldIdentifyClassExtends() {
        val code = """
class Employee extends Person {

}"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Extend, "Person")
    }

    // Todo:
    @Test
    internal fun shouldIdentifyInterfaceExtends() {
        val code = """
interface IEmployee extends IPerson {
    empCode: number;
    readonly empName: string;
    empDept?:string;
    getSalary: (number) => number; // arrow function
    getManagerName(number): string;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Extend, "IPerson")
    }

    @Test
    internal fun shouldIdentifyBlockImportsSource() {
        val code = """
import { ZipCodeValidator } from "./ZipCodeValidator";

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 1)
        assertEquals(codeFile.Imports[0].Source, "./ZipCodeValidator")
        assertEquals(codeFile.Imports[0].UsageName[0], "ZipCodeValidator")
    }

    @Test
    internal fun shouldIdentifyMultipleBlockImportsSource() {
        val code = """
import { ZipCodeValidator, ZipCodeGenerator } from "./ZipCodeValidator";

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 1)
        assertEquals(codeFile.Imports[0].Source, "./ZipCodeValidator")
        assertEquals(codeFile.Imports[0].UsageName.size, 2)
        assertEquals(codeFile.Imports[0].UsageName[0], "ZipCodeValidator")
        assertEquals(codeFile.Imports[0].UsageName[1], "ZipCodeGenerator")
    }

    @Test
    internal fun shouldIdentifyRequireImport() {
        val code = """
import zip = require("./ZipCodeValidator");

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 1)
        assertEquals(codeFile.Imports[0].Source, "./ZipCodeValidator")
        assertEquals(codeFile.Imports[0].UsageName[0], "zip")
    }

    @Test
    internal fun shouldIdentifyImportAll() {
        val code = """
import "./module.js";

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 1)
        assertEquals(codeFile.Imports[0].Source, "./module.js")
    }

    @Test
    internal fun shouldIdentifySpecificSymbol() {
        val code = """
import $ from "jquery";
import _ from "lodash";

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 2)
        assertEquals(codeFile.Imports[0].Source, "jquery")
        assertEquals(codeFile.Imports[0].UsageName[0], "$")
        assertEquals(codeFile.Imports[1].Source, "lodash")
        assertEquals(codeFile.Imports[1].UsageName[0], "_")
    }

    @Test
    internal fun shouldIdentifyImportAs() {
        val code = """
import * as validator from "./ZipCodeValidator";

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 1)
        assertEquals(codeFile.Imports[0].Source, "./ZipCodeValidator")
        assertEquals(codeFile.Imports[0].UsageName[0], "*")
        assertEquals(codeFile.Imports[0].AsName, "validator")
    }

    @Test
    internal fun shouldIdentStyleImports() {
        val code = """
import { updateSystemInfo } from '@/api/addition/systemInfo';
import { BadSmellOption, useBadSmellOption } from "@/api/module/badSmellThresholds";
import useSystemList from '@/store/global-cache-state/useSystemList';
import { storage } from '@/store/storage/sessionStorage';
import { Button, Collapse, Form, notification, Radio } from "antd";
import { useForm } from 'antd/lib/form/Form';
import { Store } from 'antd/lib/form/interface';
import React, { useEffect } from "react";
import styles from "./BadSmellThreshold.less";
import BadSmellThresholdTable from "./components/BadSmellThresholdTable";
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.Imports.size, 10)
    }

    private val personClassCode = """class Person implements IPerson {
        public publicString: string;
        private privateString: string;
        protected protectedString: string;
        readonly readonlyString: string;
        name: string;
    
        constructor(name: string) {
            this.name = name;
        }
    }"""

    @Test
    internal fun shouldIdentifyClassConstructorMethod() {
        val codeFile = TypeScriptAnalyser().analysis(personClassCode, "")
        assertEquals(codeFile.DataStructures[0].Functions.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions[0].Name, "constructor")
        val parameters = codeFile.DataStructures[0].Functions[0].Parameters
        assertEquals(parameters.size, 1)
        assertEquals(parameters[0].TypeValue, "name")
        assertEquals(parameters[0].TypeType, "string")
    }

    @Test
    internal fun shouldIdentifyClassFields() {
        val codeFile = TypeScriptAnalyser().analysis(personClassCode, "")
        assertEquals(codeFile.DataStructures[0].Fields.size, 5)
        assertEquals(codeFile.DataStructures[0].Fields[0].Modifiers[0], "public")
        assertEquals(codeFile.DataStructures[0].Fields[0].TypeKey, "publicString")
        assertEquals(codeFile.DataStructures[0].Fields[0].TypeType, "string")
        assertEquals(codeFile.DataStructures[0].Fields[1].TypeType, "string")
        assertEquals(codeFile.DataStructures[0].Fields[4].Modifiers.size, 0)
    }

    @Test
    internal fun shouldIdentifyNormalClassFunction() {
        val normalClassFunction = """
class Employee extends Person {
    empCode: number;
    static pi: number = 3.14;

    constructor(empcode: number, name:string) {
        super(name);
        this.empCode = empcode;
    }

    displayName():void {
        console.log("Name = " + this.name +  ", Employee Code = " + this.empCode);
    }
} 
        """

        val codeFile = TypeScriptAnalyser().analysis(normalClassFunction, "")
        assertEquals(codeFile.DataStructures[0].Functions.size, 2)
        assertEquals(codeFile.DataStructures[0].Functions[1].Name, "displayName")
        assertEquals(codeFile.DataStructures[0].Functions[1].ReturnType, "void")
    }

    @Test
    internal fun shouldIdentifyNormalPureFunction() {
        val normalClassFunction = """
function Sum(x: number, y: number) : void {
    console.log('processNumKeyPairs: key = ' + key + ', value = ' + value)
    return x + y;
}
        """

        val codeFile = TypeScriptAnalyser().analysis(normalClassFunction, "demo.ts")
        assertEquals(codeFile.DataStructures[0].NodeName, "default")
        assertEquals(codeFile.DataStructures[0].FilePath, "demo.ts")
        assertEquals(codeFile.DataStructures[0].Functions[0].Name, "Sum")
        assertEquals(codeFile.DataStructures[0].Functions[0].Parameters.size, 2)
        assertEquals(codeFile.DataStructures[0].Functions[0].MultipleReturns.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions[0].MultipleReturns[0].TypeType, "void")
    }

    @Test
    internal fun shouldIdentifyInterfaceProperty() {
        val code = """
export interface IPerson {
    name: string;
    gender: string;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Fields.size, 2)
    }

    @Test
    internal fun shouldIdentifyInterfacePropertyFunction() {
        val code = """
interface IEmployee extends IPerson {
    empCode: number;
    readonly empName: string;
    empDept?:string;
    getSalary: (number) => number; // arrow type
    getManagerName(number): string;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")

        assertEquals(codeFile.DataStructures[0].Fields.size, 4)
        assertEquals(codeFile.DataStructures[0].Functions.size, 1)

        val secondFunc = codeFile.DataStructures[0].Functions[0]
        assertEquals(secondFunc.Name, "getManagerName")
        assertEquals(secondFunc.MultipleReturns[0].TypeType, "string")
    }

    @Test
    internal fun shouldIdentifyFunctionExpressionFunction() {
        val code = """
let greeting = function() {
    console.log("Hello TypeScript!");
};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions[0].Name, "greeting")
    }

    @Test
    internal fun shouldIdentifyFunctionExpressionParameterAndReturnType() {
        val code = """
let SumAnon = function(x: number, y: number) : number
{
    return x + y;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        val functions = codeFile.DataStructures[0].Functions
        assertEquals(functions[0].Parameters.size, 2)
        assertEquals(functions[0].Parameters[0].TypeType, "number")
        assertEquals(functions[0].Parameters[0].TypeValue, "x")
        assertEquals(functions[0].Parameters[1].TypeType, "number")
        assertEquals(functions[0].Parameters[1].TypeValue, "y")
        assertEquals(functions[0].MultipleReturns[0].TypeType, "number")
    }

    @Test
    internal fun shouldIdentifyArrayFunction() {
        val code = """
let Print = () => console.log("Hello TypeScript");
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        val functions = codeFile.DataStructures[0].Functions
        assertEquals(functions.size, 1)
        assertEquals(functions[0].Name, "Print")
    }

    @Test
    internal fun shouldIdentifyArrayFunctionParameters() {
        val code = """
let sumArrow = (x: number, y: number): number => {
    return x + y
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        val functions = codeFile.DataStructures[0].Functions
        assertEquals(functions.size, 1)
        assertEquals(functions[0].Name, "sumArrow")
        assertEquals(functions[0].Parameters.size, 2)
        assertEquals(functions[0].Parameters[1].TypeType, "number")
        assertEquals(functions[0].Parameters[1].TypeValue, "y")
        assertEquals(functions[0].MultipleReturns[0].TypeType, "number")
    }

    @Test
    internal fun shouldIdentifyNameSpaceAsPackage() {
        val code = """
export namespace Polygons {
    export class Triangle { }
    export class Square { }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "src/Polygons/Polygons.ts")
        assertEquals(codeFile.DataStructures.size, 2)
        assertEquals(codeFile.DataStructures[0].Package, "@.Polygons.Polygons")
        assertEquals(codeFile.DataStructures[1].Package, "@.Polygons.Polygons")
    }

    @Test
    internal fun shouldIdentifyHelloWorldFunctionCall() {
        val code = """
function helloworld() {
  console.log("hello, world")
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        val functionCalls = codeFile.DataStructures[0].Functions[0].FunctionCalls
        assertEquals(functionCalls.size, 1)
        assertEquals(functionCalls[0].FunctionName, "log")
        assertEquals(functionCalls[0].NodeName, "console")
        assertEquals(functionCalls[0].Parameters[0].TypeValue, "\"hello, world\"")
    }

    @Test
    internal fun shouldIdentifyApiFunctionCallWithObjectAndGeneric() {
        val code = """
export function queryScannerConfig() {
  return axios<ScannerConfigType>({
    baseURL,
    url: '/config',
    method: "GET",
  });
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)

        val firstCall = codeFile.DataStructures[0].Functions[0].FunctionCalls

        assertEquals(1, firstCall.size)
        assertEquals("axios", firstCall[0].FunctionName);

        val parameters = firstCall[0].Parameters
        assertEquals(1, parameters.size);
        assertEquals(3, parameters[0].ObjectValue.size)
    }

    @Test
    internal fun shouldIdentifyApiFunctionCallWithString() {
        val code = """
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)

        val codeCalls = codeFile.DataStructures[0].Functions[0].FunctionCalls

        assertEquals(3, codeCalls.size)
        assertEquals("axios.get", codeCalls[0].FunctionName);
    }

    @Test
    internal fun shouldIdentifyForType() {
        val code = """
export function queryModule() {
  return axios<Module[]>({
    baseURL,
    url: "/logic-modules",
    method: "GET",
  }).then((res) => _.orderBy(res, ["status", "name"], ["desc", "asc"]));
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)

        val calls = codeFile.DataStructures[0].Functions[0].FunctionCalls

        assertEquals(3, calls.size)
        assertEquals("axios->then", calls[0].FunctionName);
        assertEquals("axios->then", calls[1].FunctionName);
    }

    @Test
    internal fun shouldIdentifyLocalVars() {
        val code = """
const systemInfoApi = "/system-info";

export function querySystemInfo() {
  return axios<SystemInfo[]>({
    baseURL,
    url: systemInfoApi,
    method: "GET",
  });
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)
        val dataStruct = codeFile.DataStructures[0]
        val calls = dataStruct.Functions[0].FunctionCalls

        assertEquals(1, calls.size)
        assertEquals(1, dataStruct.Fields.size);
        assertEquals("systemInfoApi", dataStruct.Fields[0].TypeKey);
        assertEquals("/system-info", dataStruct.Fields[0].TypeValue);
    }

    @Test
    internal fun shouldIdentifyUmiRequest() {
        val code = """
import request from 'umi-request';

export function demo() {
  return request
    .get('/api/v1/xxx?id=1')
    .then(function(response) {
      console.log(response);
    })
    .catch(function(error) {
      console.log(error);
    });
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "")
        assertEquals(codeFile.DataStructures.size, 1)

        assertEquals(1, codeFile.DataStructures[0].Functions.size)
        val calls = codeFile.DataStructures[0].Functions[0].FunctionCalls

        assertEquals(3, calls.size)
        assertEquals("request->get->then->catch", calls[0].FunctionName);
    }

    @Test
    internal fun shouldSetExportConstantsToContainer() {
        val content = """
export const baseURL = '/api'
        """

        val codeFile = TypeScriptAnalyser().analysis(content, "config.ts")
        assertEquals(1, codeFile.DataStructures.size)
        val defaultStruct = codeFile.DataStructures[0]

        assertEquals("config.ts", defaultStruct.FilePath)
        assertEquals("default", defaultStruct.NodeName)
        assertEquals(1, defaultStruct.Fields.size)
        assertEquals(1, defaultStruct.Exports.size)
        assertEquals("/api", defaultStruct.Fields[0].TypeValue)
    }

    @Test
    internal fun shouldExportFieldForAlias() {
        val content = """

import createCacheState from "@/utils/utils";
import { queryAllQualityGateProfile } from "@/api/module/profile";

const useQualityGate = createCacheState(queryAllQualityGateProfile, []);
export default useQualityGate;

"""

        val codeFile = TypeScriptAnalyser().analysis(content, "config.ts")
        val defaultStruct = codeFile.DataStructures[0]

        assertEquals("default", defaultStruct.NodeName)
        assertEquals(1, defaultStruct.Fields.size)
    }

    @Test
    internal fun shouldIdentExportFunctions() {
        val content = """
export const baseURL = '/api'
        """

        val codeFile = TypeScriptAnalyser().analysis(content, "config.ts")
        val defaultStruct = codeFile.DataStructures[0]

        assertEquals(1, defaultStruct.Exports.size)
        assertEquals("baseURL", defaultStruct.Exports[0].Name)
    }

    @Test
    internal fun supportOptionalCall() {
        val code = """
function tryApplyUpdates(onHotUpdateSuccess?: Function) {
    function handleApplyUpdates(err: Error | null, updatedModules: any) {
        onHotUpdateSuccess?.();
    }
}"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)
        assertEquals("tryApplyUpdates", defaultStruct.Functions[0].Name)

        assertEquals(1, defaultStruct.Functions[0].InnerFunctions.size)
        assertEquals("handleApplyUpdates", defaultStruct.Functions[0].InnerFunctions[0].Name)
    }

    @Test
    internal fun supportSomeIssue() {
        val code = """
function tryApplyUpdates(onHotUpdateSuccess: Function) {
  if (!module.hot) {
    window.location.reload();
  }
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)
        assertEquals("tryApplyUpdates", defaultStruct.Functions[0].Name)
    }

    @Test
    fun variableCallInNumber() {
        val code = """
function reload() {
  const currentSystemId = Number(storage.getSystemId());
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)


        assertEquals(1, defaultStruct.Functions[0].FunctionCalls.size)
        assertEquals("storage->getSystemId", defaultStruct.Functions[0].FunctionCalls[0].FunctionName)
    }

    @Test
    internal fun supportInterfaceMethodGeneric() {
        val code = """
const createJMethodNode = (jMethod: JMethod): TreeNode<JMethod> => {
  const { id, name, module, clazz } = jMethod;
  return {
    id,
    name,
    module,
    parents: [],
    children: [],
  };
};
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)
    }

    @Test
    internal fun cleanTemplateString() {
        val code = """
function hello2() {
  console.log(`template-string`)
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)
        assertEquals(1, defaultStruct.Functions[0].FunctionCalls.size)
        assertEquals("template-string", defaultStruct.Functions[0].FunctionCalls[0].Parameters[0].TypeValue)
    }

    @Test
    fun optionCheckAfterData() {
        val code = """
const QualityGateProfile = () => {
  updateQualityGateProfile(profile.id!, profile).then(() => {
    notification.success({
      message: "更新成功!",
    });
  });
};
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val defaultStruct = codeFile.DataStructures[0]
        assertEquals(1, defaultStruct.Functions.size)
        assertEquals(3, defaultStruct.Functions[0].FunctionCalls.size)
        assertEquals("updateQualityGateProfile", defaultStruct.Functions[0].FunctionCalls[0].FunctionName)
        assertEquals("", defaultStruct.Functions[0].FunctionCalls[1].FunctionName)
        assertEquals("success", defaultStruct.Functions[0].FunctionCalls[2].FunctionName)
    }

    @Test
    internal fun supportForLogCodeCallToField() {
        val code = """
const useQualityGate = createCacheState(queryAllQualityGateProfile, []);
export default useQualityGate;
        """

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
        assertEquals("createCacheState", codeFile.DataStructures[0].Fields[0].Calls[0].FunctionName)
        assertEquals(2, codeFile.DataStructures[0].Fields[0].Calls[0].Parameters.size)
        assertEquals(
            "queryAllQualityGateProfile",
            codeFile.DataStructures[0].Fields[0].Calls[0].Parameters[0].TypeValue
        )
    }

    @Test
    internal fun undefinedAndUnusedComma() {
        val code = """
export const getLayout = (options: LayoutOptions) => {
  const getSize = (
    size: number | number[] | undefined,
    index: number,
    defaultVaue = 16,
  ): number =>  { }
};

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun enumInUndefined() {
        val code = """
interface CollapsibleCardProps {
  collapsed?: boolean;
  title?: string | ReactNode;
  size?: "default" | "small" | undefined;
  extra?: ReactNode;
  children?: any;
  className?: string;
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
        assertEquals(6, codeFile.DataStructures[0].Fields.size)
    }

    @Test
    internal fun handleNullCoalesce() {
        val code = """
const handleError = (error: any) => {
  const message = error?.response?.data?.message ?? error.message;
};
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun handleFunctionDeclEndWithCommon() {
        val code = """
export function queryDFMSMetricBy(by: "module" | "package" | "class", params: MetricParams, ) {

}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun moduleKeyword() {
        val code = """
const getModulesOption = (modules?: Module[]): FormItemOption[] => {
  return modules.map(module => {
    return { label: module.name, value: module.name }
  })
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun AsunknownIssue() {
        val code = """
export default function axiosAgent<T>(config: AxiosRequestConfig) {
  return (axiosInstance(config) as unknown) as Promise<T>;
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun classPropCheckIssue() {
        val code = """
export class ELKLayout {
  elk!: ELK;
  options!: LayoutOptions;
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    fun forOfStatement() {
        val code = """
function dfs() {
  if (node in graph) {
    for (const n of graph[node]) {
    }
  }
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun defaultInObjectLiteral() {
        val code = """
const options: Partial<GraphOptions> = {
  modes: {
    default: ["drag-canvas", "drag-node", "zoom-canvas"],
  },
  defaultNode: {
    type: "deps-node",
  }
};

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun optionalParameterBeforeRequiredParamter() {
        val code = """
export function initCytoscape(id = "cy", onEvent: { cxttap: () => MessageType }) {

}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun queryBeforeIdentify() {
        val code = """
export const switchFunc = (pagination, filters, sorter, {action}) => {
  switch (action) {
    case "paginate":
      return onPaginationChange?.(pagination);
    case "filter":
      return onFilterChange?.(filters);
    case "sort":
      return onSortChange?.(sorter);
  }
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun typeDefineIssue() {
        val code = """
function useUrlQuery<T = { [key: string]: string }>() {
  const location = useLocation() as { query: T; };
  return location.query ?? ({} as T);
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun nestedGeneric() {
        val code = """
function ModuleDependenceGraph() {
  const [graphData, setGraphData] = useState<GraphData<JavaItem>>();
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun anyKeywordInType() {
        val code = """
export default function createCacheState<T = any>(): () => [CacheState<T> | undefined, () => void] {
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun typeAlias() {
        val code = """
type State = { [key in ThresholdKey]: any };
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun declareClass() {
        val code = """
declare class ELK {
  constructor(options: any) {}
  layout(data: any): Promise<any>;
}
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun lodashIssueInObjectMethod() {
        val code = """
export default function CouplingList(props: CouplingListProps) {
  const props = firstItem.props.map((prop, index) => {
    return {
      render(_: any, item: CouplingRecord) {
        return <Text type={qualified ? undefined : "danger"}>{value}</Text>;
      },
    };
  })
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun symbolKeywordInObjectLiteral() {
        val code = """
const getMarkLineOpt = (coordX: number, coordY: number) => {
  return {
    data: [[
      { coord: [coordY, coordX], symbol: 'none' }
    ]]
  }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun typeByObject() {
        val code = """
const onStabilityChange = (value: DFMS["stability"]) => {

};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun lazyRequiredInObject() {
        val code = """
const docs = {
  index: require("./docs/index.md").default,
  "module-coupling": require("./docs/module-coupling.md").default,
};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun keyofTypeOf() {
        val code = """
const getClazzColumnsBy = (type: keyof typeof columnCount) => {}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun anyInObjecLiteralType() {
        val code = """
export function getChartsOption(data?: number[]): EChartOption {
  return {
      series: !data ? defaultSeriesMarkLineOpt :
      [...defaultSeriesMarkLineOpt, {
        data: [data],
        tooltip: {
          formatter: ({ value }: any) => {}
        }
      },
    ]
  }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun reactDefaultNode() {
        val code = """
const SystemInfoForm = (props: SystemInfoFormProps, ref: any) => {
  return (
    <>
    </>
  );
};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun typeInStrings() {
        val code = """
type ThresholdKey = | "oversizedMethodByLine" | "oversizedMethodByCount"
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun thisIsInObjectType() {
        val code = """
interface SingularData {
  isNode(): this is NodeSingular;
}
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun isAsFunctionName() {
        val code = """
interface SingularData {
   is(selector: Selector): boolean;
}
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun typeAnnotationForObjectLiteralGenerateMethod() {
        val code = """
const store: IOptions<State, string, string, string> = {
  mutations: {
    updateThresholdState(state: State, payload): State {
      return Object.assign({}, state, { ...payload });
    }
  }
};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun namespaceAsParameter() {
        val code = """
interface CoreGraphManipulationExt {
    scratch(namespace?: string): Scratchpad;
    scratch(namespace: string, value: any): this;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun typeEnumList() {
        val code = """
interface CytoscapeOptions {
  elements?:
    | ElementsDefinition
    | ElementDefinition[]
    | Promise<ElementsDefinition>
    | Promise<ElementDefinition[]>;
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    fun multipleReturnTypeWithBoolean() {
        val code = """
type SearchVisitFunction = (
 v: NodeSingular,
 e: EdgeSingular,
 u: NodeSingular,
 i: number,
 depth: number,
) => boolean | void;
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun supportForGenericMap() {
        val code = """
export type Model<T extends keyof typeof models> = {
  [key in keyof typeof models]: ReturnType<typeof models[T]>;
};
"""

        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun parametersForString() {
        val code = """
export const querySystemInfo = (data) => {
    return request(`/api/system-info`, {
        method: 'GET',
    });
};

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)

        val parameters = codeFile.DataStructures[0].Functions[0].FunctionCalls[0].Parameters
        assertEquals(parameters.size, 2)
        assertEquals(parameters[0].TypeValue, "/api/system-info")
    }

    @Test
    internal fun objectLiteralListInParameterList() {
        val code = """
const service = {
  effects: {
    *addThing({ payload }, { call }){
      const response = yield call(createThingRequest, payload)
      return response
    }
  }  
};

"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        val ds = codeFile.DataStructures
        assertEquals(1, ds.size)
        assertEquals(1, ds[0].Fields[0].Calls.size)
    }

    @Test
    internal fun parameterObjectLiteral() {
        val code = """
const setting = {
  reducers: {
    changeSetting(_, { payload }) {
      return { ...defaultSettings, ...payload };
    },
  },
};
export default setting;
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }

    @Test
    internal fun default_in_default_export() {
        val code = """
export default defineConfig({
  nodeModulesTransform: {
    type: "none",
    exclude: [],
  },
  hash: true,
  antd: {},
  dva: false,
  locale: {
    default: 'zh-CN'
  }
})
"""

        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    internal fun multipleGeneric() {
        val code = """
const createTreeNode = <U = TreeNode, T extends JavaItem<T>>(): U => {};
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeFile.DataStructures.size)
    }
}