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

107 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1"""Tests for TypeScript cognitive complexity.""" 

2 

3from analyzers.typescript import analyze_source 

4 

5 

6def _score(code: str, tsx: bool = False) -> int: 

7 """Helper: return total complexity of the first function in code.""" 

8 result = analyze_source(code, tsx=tsx) 

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

10 return result.functions[0].complexity 

11 

12 

13def _scores(code: str, tsx: bool = False) -> dict[str, int]: 

14 """Helper: return {name: complexity} for all functions in code.""" 

15 result = analyze_source(code, tsx=tsx) 

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

17 

18 

19# --- Basic control flow --- 

20 

21 

22def test_empty_function(): 

23 assert _score("function f() {}") == 0 

24 

25 

26def test_single_if(): 

27 code = """ 

28function f(x) { 

29 if (x) { // +1 

30 } 

31} 

32""" 

33 assert _score(code) == 1 

34 

35 

36def test_if_else(): 

37 code = """ 

38function f(x) { 

39 if (x) { // +1 

40 } else { // +1 

41 } 

42} 

43""" 

44 assert _score(code) == 2 

45 

46 

47def test_if_else_if_else(): 

48 code = """ 

49function f(x) { 

50 if (x > 0) { // +1 

51 } else if (x < 0) { // +1 

52 } else { // +1 

53 } 

54} 

55""" 

56 assert _score(code) == 3 

57 

58 

59# --- Loops --- 

60 

61 

62def test_for_loop(): 

63 code = """ 

64function f(items) { 

65 for (let i = 0; i < items.length; i++) { // +1 

66 } 

67} 

68""" 

69 assert _score(code) == 1 

70 

71 

72def test_for_in_loop(): 

73 code = """ 

74function f(obj) { 

75 for (const key in obj) { // +1 

76 } 

77} 

78""" 

79 assert _score(code) == 1 

80 

81 

82def test_while_loop(): 

83 code = """ 

84function f(x) { 

85 while (x > 0) { // +1 

86 x--; 

87 } 

88} 

89""" 

90 assert _score(code) == 1 

91 

92 

93def test_do_while_loop(): 

94 code = """ 

95function f(x) { 

96 do { // +1 

97 x--; 

98 } while (x > 0); 

99} 

100""" 

101 assert _score(code) == 1 

102 

103 

104# --- Nesting --- 

105 

106 

107def test_nested_if(): 

108 code = """ 

109function f(a, b) { 

110 if (a) { // +1 (nesting 0) 

111 if (b) { // +1 + 1 (nesting 1) 

112 } 

113 } 

114} 

115""" 

116 assert _score(code) == 3 

117 

118 

119def test_deeply_nested(): 

120 code = """ 

121function f(a, b, c) { 

122 if (a) { // +1 (nesting 0) 

123 for (let x of b) { // +1 + 1 (nesting 1) 

124 if (c) { // +1 + 2 (nesting 2) 

125 } 

126 } 

127 } 

128} 

129""" 

130 assert _score(code) == 6 

131 

132 

133def test_if_else_nesting(): 

134 code = """ 

135function f(a, b) { 

136 if (a) { // +1 (nesting 0) 

137 } else { // +1 (no nesting bonus) 

138 if (b) { // +1 + 1 (nesting 1, inside else body) 

139 } 

140 } 

141} 

142""" 

143 assert _score(code) == 4 

144 

145 

146# --- Try/Catch --- 

147 

148 

149def test_try_catch(): 

150 code = """ 

151function f() { 

152 try { 

153 } catch (e) { // +1 

154 } 

155} 

156""" 

157 assert _score(code) == 1 

158 

159 

160def test_try_catch_nested(): 

161 code = """ 

162function f(x) { 

163 if (x) { // +1 (nesting 0) 

164 try { 

165 } catch (e) { // +1 + 1 (nesting 1) 

166 } 

167 } 

168} 

169""" 

170 assert _score(code) == 3 

171 

172 

173# --- Boolean operators --- 

174 

175 

176def test_single_and(): 

177 code = """ 

178function f(a, b) { 

179 if (a && b) { // +1 (if) + 1 (boolean) 

180 } 

181} 

182""" 

183 assert _score(code) == 2 

184 

185 

186def test_chained_same_op(): 

187 code = """ 

188function f(a, b, c) { 

189 if (a && b && c) { // +1 (if) + 1 (boolean chain, same op) 

190 } 

191} 

192""" 

193 assert _score(code) == 2 

194 

195 

196def test_mixed_boolean(): 

197 code = """ 

198function f(a, b, c) { 

199 if (a && b || c) { // +1 (if) + 2 (&&->|| switch) 

200 } 

201} 

202""" 

203 assert _score(code) == 3 

204 

205 

206def test_complex_boolean(): 

207 code = """ 

208function f(a, b, c, d) { 

209 if (a || b || (c && d)) { // +1 (if) + 2 (|| sequence, then && subtree) 

210 } 

211} 

212""" 

213 assert _score(code) == 3 

214 

215 

216# --- Ternary --- 

217 

218 

219def test_ternary(): 

220 code = """ 

221function f(x) { 

222 return x > 0 ? x : -x; // +1 (ternary, nesting 0) 

223} 

224""" 

225 assert _score(code) == 1 

226 

227 

228def test_ternary_nested_in_if(): 

229 code = """ 

230function f(x) { 

231 if (x) { // +1 (nesting 0) 

232 return x > 0 ? x : -x; // +1 + 1 (ternary, nesting 1) 

233 } 

234} 

235""" 

236 assert _score(code) == 3 

237 

238 

239# --- Recursion --- 

240 

241 

242def test_recursion(): 

243 code = """ 

244function factorial(n) { 

245 if (n <= 1) { // +1 

246 return 1; 

247 } 

248 return n * factorial(n - 1); // +1 (recursion) 

249} 

250""" 

251 assert _score(code) == 2 

252 

253 

254def test_no_false_recursion(): 

255 code = """ 

256function f(x) { 

257 return g(x); 

258} 

259""" 

260 assert _score(code) == 0 

261 

262 

263# --- Nested functions --- 

264 

265 

266def test_nested_function_separate_scoring(): 

267 code = """ 

268function outer(x) { 

269 if (x) {} // +1 for outer 

270 function inner(y) { 

271 if (y) {} // +1 for inner 

272 } 

273} 

274""" 

275 scores = _scores(code) 

276 assert scores["outer"] == 1 

277 assert scores["inner"] == 1 

278 

279 

280def test_nested_function_nesting_reset(): 

281 code = """ 

282function outer(x) { 

283 if (x) { // +1 for outer 

284 function inner(y) { 

285 if (y) {} // +1 for inner (nesting 0, not 1) 

286 } 

287 } 

288} 

289""" 

290 scores = _scores(code) 

291 assert scores["outer"] == 1 

292 assert scores["inner"] == 1 

293 

294 

295# --- Arrow functions --- 

296 

297 

298def test_inline_arrow_increases_nesting(): 

299 code = """ 

300function f(items) { 

301 return items.map(x => x > 0 ? x : 0); 

302} 

303""" 

304 assert _score(code) == 2 

305 

306 

307def test_named_arrow_scored_separately(): 

308 code = """ 

309const f = (x) => { 

310 if (x) {} // +1 

311}; 

312const g = (a, b) => { 

313 if (a) { // +1 

314 if (b) {} // +1 + 1 

315 } 

316}; 

317""" 

318 scores = _scores(code) 

319 assert scores["f"] == 1 

320 assert scores["g"] == 3 

321 

322 

323# --- Switch --- 

324 

325 

326def test_switch_statement(): 

327 code = """ 

328function f(x) { 

329 switch (x) { // +1 (nesting 0) 

330 case 1: 

331 break; 

332 case 2: 

333 break; 

334 default: 

335 break; 

336 } 

337} 

338""" 

339 assert _score(code) == 1 

340 

341 

342# --- Method definition --- 

343 

344 

345def test_method_definition(): 

346 code = """ 

347class Foo { 

348 bar(x) { 

349 if (x) {} // +1 

350 } 

351} 

352""" 

353 scores = _scores(code) 

354 assert scores["bar"] == 1 

355 

356 

357# --- Comprehensive example --- 

358 

359 

360def test_comprehensive(): 

361 code = """ 

362function process(items, flag) { 

363 if (flag) { // +1 (nesting 0) 

364 for (let i = 0; i < items.length; i++) { // +1 +1 (nesting 1) 

365 if (items[i] > 0) { // +1 +2 (nesting 2) 

366 } else if (items[i] < 0) { // +1 (no nesting bonus) 

367 } else { // +1 (no nesting bonus) 

368 try { 

369 } catch (e) { // +1 +3 (nesting 3) 

370 } 

371 } 

372 } 

373 } else { // +1 (no nesting bonus) 

374 } 

375} 

376""" 

377 assert _score(code) == 13 

378 

379 

380# --- Edge cases --- 

381 

382 

383def test_not_operator_no_increment(): 

384 code = """ 

385function f(x) { 

386 if (!x) { // +1 (if only) 

387 } 

388} 

389""" 

390 assert _score(code) == 1 

391 

392 

393def test_async_function(): 

394 code = """ 

395async function f(x) { 

396 if (x) {} // +1 

397} 

398""" 

399 assert _score(code) == 1 

400 

401 

402def test_generator_function(): 

403 code = """ 

404function* gen(items) { 

405 for (const item of items) { // +1 

406 if (item) { // +1 + 1 

407 yield item; 

408 } 

409 } 

410} 

411""" 

412 assert _score(code) == 3