Coverage for tests/test_mi/test_typescript.py: 100%

1"""Tests for Maintainability Index (TypeScript)."""

2

3 from analyzers.typescript import analyze_source

4

5

6 def _mi(code: str) -> float:

7 """Return MI of the first function in code."""

8 result = analyze_source(code)

9 assert result.functions, f"No functions found in:\n{code}"

10 return result.functions[0].mi

11

12

13# --- Trivial function (high MI) ---

14

15 def test_trivial_function():

16 code = """

17 function f() {

18 return 1;

19}

20"""

21 mi = _mi(code)

22 assert mi > 50, f"Trivial function should have high MI, got {mi}"

23

24

25 def test_simple_add():

26 code = """

27 function add(a: number, b: number): number {

28 return a + b;

29}

30"""

31 mi = _mi(code)

32 assert mi > 50, f"Simple function should have high MI, got {mi}"

33

34

35# --- Complex function (low MI) ---

36

37 def test_complex_function():

38 code = """

39 function process(data: any[], flag: boolean, mode: boolean, opt: boolean, extra: boolean) {

40 let result: any[] = [];

41 let total = 0;

42 let count = 0;

43 for (let item of data) {

44 if (flag && mode) {

45 if (item > 0) {

46 for (let sub of item) {

47 if (sub < 100) {

48 if (opt || extra) {

49 result.push(sub * 2 + total);

50 count += 1;

51 } else {

52 result.push(sub - 1);

53 }

54 } else if (sub > 200) {

55 total += sub;

56 count -= 1;

57 }

58 }

59 } else if (item < 0) {

60 try {

61 const val = parseInt(item);

62 if (val > 0 && val < 50) {

63 result.push(val);

64 } else if (val >= 50 || val === 0) {

65 result.push(-val);

66 }

67 } catch (e) {

68 result.push(0);

69 }

70 }

71 } else if (mode || opt) {

72 for (let sub of data) {

73 if (sub !== item) {

74 result.push(sub + item);

75 total += sub;

76 }

77 }

78 }

79 }

80 if (count > 0) {

81 return result.slice(0, count);

82 }

83 return result;

84}

85"""

86 mi = _mi(code)

87 assert mi < 50, f"Complex function should have low MI, got {mi}"

88

89

90# --- Edge case: empty body ---

91

92 def test_empty_body():

93 code = """

94 function noop() {}

95"""

96 mi = _mi(code)

97 # With empty/minimal body, MI should be high

98 assert mi > 50, f"Empty body function should have high MI, got {mi}"

99

100

101# --- Edge case: no operators ---

102

103 def test_no_operators():

104 code = """

105 function f() {

106 const x = 1;

107}

108"""

109 mi = _mi(code)

110 assert mi > 50, f"No-operator function should have high MI, got {mi}"

111

112

113# --- MI is in valid range ---

114

115 def test_mi_range():

116 code = """

117 function f(x: boolean) {

118 if (x) {

119 return 1;

120 }

121 return 0;

122}

123"""

124 mi = _mi(code)

125 assert 0 <= mi <= 100, f"MI should be in [0, 100], got {mi}"

126

127

128# --- Multiple functions ---

129

130 def test_multiple_functions():

131 code = """

132 function simple() {

133 return 42;

134}

135

136 function moderate(x: number, y: number) {

137 if (x > y) {

138 return x;

139 } else if (x < y) {

140 return y;

141 } else {

142 return x + y;

143 }

144}

145"""

146 result = analyze_source(code)

147 assert len(result.functions) == 2

148 mi_simple = result.functions[0].mi

149 mi_moderate = result.functions[1].mi

150 assert mi_simple > mi_moderate, f"Simple function should have higher MI: {mi_simple} vs {mi_moderate}"