phodal/chapi

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

Summary

Maintainability
F
3 days
Test Coverage
package chapi.ast.typescriptast

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

class TypeScriptRegressionTest {

    @Test
    internal fun reggrestion1() {
        val code = """
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.test = void 0;
function test() {
    alert("test");
}
exports.test = test;
"""

        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "test")
    }

    @Test
    fun of_keyword_lost() {
        val code = """
import {EMPTY, Observable, of} from 'rxjs';

function demo() {
if (node in graph) {
  for (const n of graph[node]) {

  }
}

of(data)
}

"""

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

        assertEquals(codeContainer.DataStructures[0].Functions[0].Name, "demo")

    }

    @Test
    fun member_dot_issue() {
        val code = """export class DemoComponent implements OnInit, ControlValueAccessor {
    ngOnInit(): void {
        val data = this.onChange?.(userName || password || null);
    }
}"""
        val codeContainer = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(1, codeContainer.DataStructures.size)
        assertEquals(1, codeContainer.DataStructures[0].Functions.size)

        assertEquals(codeContainer.DataStructures[0].Functions[0].Name, "ngOnInit")
    }

    @Test
    fun end_with_dot() {
        val code = """export class SomeComponent implements OnInit, ControlValueAccessor {
  constructor(
    private fb: FormBuilder,
  ) { }
} """
        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "constructor")
    }

    @Test
    fun nested_type_type_error() {
        val code = """function demo() { Object.entries(values).forEach(([key, value]: [string, string[]]) => {
     
});}
"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "demo")
    }

    @Test
    fun array_in_for_loop() {
        val code = """function demo() {
    this.names.forEach((it, index) => {
      multiSelect('', this.accessor[index])
    })
}
"""
        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "demo")
    }

    @Test
    fun query_array_literal() {
        val code = """
export class DemoComponent implements OnInit, ControlValueAccessor {
  confirm(): void {
      return this.form.value?.id?.[0] || null
  }
}"""
        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "confirm")
    }

    @Test
    fun readonly_property() {
        val code = """class DataSource extends Data<Item> {
  readonly pageSize = 5;

  constructor(public readonly type: TaskEnum,) {
    super();
  }
}"""

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

    @Test
    fun type_definition() {
        val code = """export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };"""
        TypeScriptAnalyser().analysis(code, "index.tsx")

        val code2 =
            """export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };"""
        TypeScriptAnalyser().analysis(code2, "index.tsx")
    }

    @Test
    fun type_in_interface() {
        val code = """export abstract class ReactiveOptionProviderService<T extends SelectOption> {
  abstract create?(name: string): Observable<T>;
}"""

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

    @Test
    fun numeric_separators() {
        val code = """function demo() {
   if (+value > 1_000_000_000) {
   }
}
        """.trimMargin()

        val container = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(1, container.DataStructures.size)
        assertEquals(1, container.DataStructures[0].Functions.size)
        assertEquals(container.DataStructures[0].Functions[0].Name, "demo")
    }

    @Test
    fun nested_type() {
        val code = """export type Query = Array<Array<Maybe<Number>>>"""

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

        val code2 = """export type Query = Array<Array<Array<Maybe<Number>>>>"""
        TypeScriptAnalyser().analysis(code2, "index.tsx")
    }

    @Test
    fun nested_type2() {
        val code = """
export type CaseQuery = (
  { user?: Maybe<Array<Maybe<(
    UserQuery
  )>>> }
);"""
        TypeScriptAnalyser().analysis(code, "index.tsx")
    }

    @Test
    fun private_issue() {
        val code = """
export class PopupDirective {
  private openPopup(): void {
   
  }
}
"""
        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions[0].Name, "openPopup")
    }

    @Test
    fun this_issue() {
        val code = """
export class PopupDirective {
  get taskTitle(): string {
    return this.isSubTask ? this.subTask.name : this.commonTaskWorkOrder.title;
  }
}"""

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

    @Test
    fun enum_with_colon() {
        val code = """export class StatePipe implements PipeTransform {
  transform(state: Task): string {
    switch (state) {
      case Task.Created: {
        return '已创建';
      }
      case Task.Completed: {
        return '已完成';
      }
      default: {
        return '';a
      }
    }
  }
}"""

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

    @Test
    fun enum_with_colon2() {
        val code = """export class Demo {
 get filter(): FilterInput {
    return this.fields.value ? filter : {
      ...filter,
      orgId: [province.id]
    };
  }
}
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

        assertEquals(codeFile.DataStructures.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions.size, 1)
        assertEquals(codeFile.DataStructures[0].Functions[0].Name, "get")
    }

    @Test
    fun multiple_acc_modifier() {
        val code = """export class DataSource  {
    private readonly _subscription;           
    private readonly _dataStream = new BehaviorSubject<(string | undefined)[]>(this._cachedData);
}"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

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

    @Test
    fun const() {
        val code = """async function demo() {
   const source = edge.source!;
   const target = edge.target!;
}"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

        val dataStructures = codeFile.DataStructures
        assertEquals(dataStructures.size, 1)
        assertEquals(dataStructures[0].Functions.size, 1)
        assertEquals(dataStructures[0].Functions[0].Name, "demo")
    }

    @Test
//    @Disabled
    fun async() {
        val code = """test("unpaired wrapper", async () => {
  let tokens = lexer(`dep_name = '"\`/@`);
  expect(tokens.length).toBe(7);
});"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")
        println(Json.encodeToString(codeFile))

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

    @Test
    fun enum() {
        val code = """function valueTypeFromChar(char: string) {
  switch (char) {
    case SINGLE_QUOTE:
    case DOUBLE_QUOTE:
    case BACKTICK:
      return "string";
    case SLASH:
      return "regex";
    case AT:
      return "like";
    default:
      return "error";
  }
}"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

        val dataStructures = codeFile.DataStructures
        assertEquals(dataStructures.size, 1)
        assertEquals(dataStructures[0].Functions.size, 1)
        assertEquals(dataStructures[0].Functions[0].Name, "valueTypeFromChar")
    }

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

export const axiosWithBaseURL = (baseURL: string) => <T>(
  config: Omit<AxiosRequestConfig, "baseURL">,
) => (axiosAgent({ ...config, baseURL }) as unknown) as Promise<T>;
"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

        val dataStructures = codeFile.DataStructures
        assertEquals(dataStructures.size, 1)
        assertEquals(dataStructures[0].Functions.size, 2)
        assertEquals(dataStructures[0].Functions[0].Name, "axiosAgent")
        assertEquals(dataStructures[0].Functions[1].Name, "axiosWithBaseURL")
    }

    @Test
    fun type_before_function() {
        val code = """function useRequest<
  R = any,
  P extends any[] = any,
  U = any,
  UU extends U = any,
>(
  service: CombineService<R, P>,
  options: OptionsWithFormat<R, P, U, UU>,
): BaseResult<U, P>;"""

        val codeFile = TypeScriptAnalyser().analysis(code, "index.tsx")

        val dataStructures = codeFile.DataStructures
        assertEquals(dataStructures.size, 1)
        assertEquals(dataStructures[0].Functions.size, 1)
        assertEquals(dataStructures[0].Functions[0].Name, "useRequest")
    }

}