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

1"""Tests for NPath complexity (TypeScript)."""

2

3 from analyzers.typescript import analyze_source

4

5

6 def _npath(code: str) -> int:

7 """Return NPath 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].npath

11

12

13 def _npaths(code: str) -> dict[str, int]:

14 """Return {name: npath} for all functions in code."""

15 result = analyze_source(code)

16 return {f.name: f.npath for f in result.functions}

17

18

19# --- Simple ---

20

21 def test_simple_function():

22 assert _npath("function f() {}") == 1

23

24

25 def test_empty_source():

26 result = analyze_source("")

27 assert len(result.functions) == 0

28

29

30# --- If statements ---

31

32 def test_if_only():

33 code = """

34 function f(x: boolean) {

35 if (x) {

36 return 1;

37 }

38}

39"""

40 assert _npath(code) == 2

41

42

43 def test_if_else():

44 code = """

45 function f(x: boolean) {

46 if (x) {

47 return 1;

48 } else {

49 return 2;

50 }

51}

52"""

53 assert _npath(code) == 2

54

55

56 def test_if_elif_else():

57 code = """

58 function f(x: number) {

59 if (x > 0) {

60 return 1;

61 } else if (x < 0) {

62 return -1;

63 } else {

64 return 0;

65 }

66}

67"""

68 assert _npath(code) == 3

69

70

71 def test_if_elif_no_else():

72 code = """

73 function f(x: number) {

74 if (x > 0) {

75 return 1;

76 } else if (x < 0) {

77 return -1;

78 }

79}

80"""

81 assert _npath(code) == 3 # +1 for no terminal else

82

83

84# --- Bool ops ---

85

86 def test_bool_ops_in_if():

87 code = """

88 function f(a: boolean, b: boolean) {

89 if (a && b) {

90 return 1;

91 }

92}

93"""

94 assert _npath(code) == 3 # 1 + 1 + 1 bool op

95

96

97 def test_two_bool_ops():

98 code = """

99 function f(a: boolean, b: boolean, c: boolean) {

100 if (a && b || c) {

101 return 1;

102 }

103}

104"""

105 assert _npath(code) == 4 # 1 + 1 + 2 bool ops

106

107

108# --- Loops ---

109

110 def test_for_loop():

111 code = """

112 function f() {

113 for (let i = 0; i < 10; i++) {

114 console.log(i);

115 }

116}

117"""

118 assert _npath(code) == 2

119

120

121 def test_while_loop():

122 code = """

123 function f(x: boolean) {

124 while (x) {

125 break;

126 }

127}

128"""

129 assert _npath(code) == 2

130

131

132 def test_while_with_bool_cond():

133 code = """

134 function f(a: boolean, b: boolean) {

135 while (a && b) {

136 break;

137 }

138}

139"""

140 assert _npath(code) == 3

141

142

143 def test_do_while():

144 code = """

145 function f(x: boolean) {

146 do {

147 break;

148 } while (x);

149}

150"""

151 assert _npath(code) == 2

152

153

154 def test_do_while_with_bool_cond():

155 code = """

156 function f(a: boolean, b: boolean) {

157 do {

158 break;

159 } while (a || b);

160}

161"""

162 assert _npath(code) == 3

163

164

165# --- Try/catch ---

166

167 def test_try_catch():

168 code = """

169 function f() {

170 try {

171 foo();

172 } catch (e) {

173 bar();

174 }

175}

176"""

177 assert _npath(code) == 2

178

179

180# --- Switch ---

181

182 def test_switch_three_cases():

183 code = """

184 function f(x: number) {

185 switch (x) {

186 case 1: break;

187 case 2: break;

188 case 3: break;

189 }

190}

191"""

192 assert _npath(code) == 4 # 1+1+1 + 1 (no default)

193

194

195 def test_switch_with_default():

196 code = """

197 function f(x: number) {

198 switch (x) {

199 case 1: break;

200 case 2: break;

201 default: break;

202 }

203}

204"""

205 assert _npath(code) == 3 # 1+1+1, has default

206

207

208# --- Ternary ---

209

210 def test_ternary():

211 code = """

212 function f(c: boolean, x: number, y: number) {

213 return c ? x : y;

214}

215"""

216 assert _npath(code) == 2

217

218

219 def test_nested_ternary():

220 code = """

221 function f(c1: boolean, c2: boolean) {

222 return c1 ? 1 : (c2 ? 2 : 3);

223}

224"""

225 assert _npath(code) == 3

226

227

228# --- Sequential ifs (multiplicative) ---

229

230 def test_sequential_ifs():

231 code = """

232 function f(a: boolean, b: boolean) {

233 if (a) { foo(); }

234 if (b) { bar(); }

235}

236"""

237 assert _npath(code) == 4

238

239

240# --- Nested if ---

241

242 def test_nested_if():

243 code = """

244 function f(a: boolean, b: boolean) {

245 if (a) {

246 if (b) {

247 foo();

248 }

249 }

250}

251"""

252 assert _npath(code) == 3

253

254

255# --- Arrow functions ---

256

257 def test_named_arrow():

258 code = """

259 const f = (x: boolean) => {

260 if (x) {

261 return 1;

262 }

263 return 0;

264};

265"""

266 assert _npath(code) == 2

267

268

269 def test_nested_named_arrow():

270 code = """

271 function outer(x: boolean) {

272 if (x) { foo(); }

273 const inner = (y: boolean) => {

274 if (y) { bar(); }

275 };

276}

277"""

278 npaths = _npaths(code)

279 assert npaths["outer"] == 2 # inner arrow counts as 1

280 assert npaths["inner"] == 2

281

282

283# --- Method definition ---

284

285 def test_method_definition():

286 code = """

287 class Foo {

288 bar(x: boolean) {

289 if (x) {

290 return 1;

291 }

292 }

293}

294"""

295 assert _npath(code) == 2

296

297

298# --- For-in loop ---

299

300 def test_for_in_loop():

301 code = """

302 function f(obj: any) {

303 for (const key in obj) {

304 console.log(key);

305 }

306}

307"""

308 assert _npath(code) == 2