Skip to content

Commit 9cd4f43

Browse files
committed
test(ast-utils): add comprehensive unit tests for utility functions
- Add unit tests for isDefineStoreCall, getStoreId, extractDeclarations, and extractReturnProperties - Ensure support for optional chaining, template literals, and nested structures in AST utilities - Improve test coverage to handle edge cases and various argument forms
1 parent bf944aa commit 9cd4f43

File tree

4 files changed

+378
-36
lines changed

4 files changed

+378
-36
lines changed

packages/eslint-plugin/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@
2828
"sideEffects": false,
2929
"exports": {
3030
"types": "./dist/index.d.ts",
31-
"require": "./dist/index.js",
31+
"require": "./dist/index.cjs",
3232
"import": "./dist/index.mjs"
3333
},
34-
"main": "./dist/index.js",
34+
"main": "./dist/index.cjs",
3535
"module": "./dist/index.mjs",
3636
"types": "./dist/index.d.ts",
3737
"files": [
3838
"dist/*.js",
39+
"dist/*.cjs",
3940
"dist/*.mjs",
4041
"dist/*.d.ts",
4142
"dist/*.d.mts",
@@ -61,7 +62,7 @@
6162
"node": ">=18.18.0"
6263
},
6364
"peerDependencies": {
64-
"eslint": ">=8.0.0"
65+
"eslint": ">=8.57.0 || ^9.0.0"
6566
},
6667
"publishConfig": {
6768
"access": "public"
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/**
2+
* @fileoverview Tests for AST utilities
3+
*/
4+
5+
import { describe, it, expect } from 'vitest'
6+
import { parseForESLint } from '@typescript-eslint/parser'
7+
import type { TSESTree } from '@typescript-eslint/utils'
8+
import {
9+
isDefineStoreCall,
10+
getStoreId,
11+
extractDeclarations,
12+
extractReturnProperties,
13+
} from '../ast-utils'
14+
15+
function parseCode(code: string): TSESTree.Program {
16+
const result = parseForESLint(code, {
17+
ecmaVersion: 2020,
18+
sourceType: 'module',
19+
})
20+
return result.ast
21+
}
22+
23+
function findCallExpression(ast: TSESTree.Program): TSESTree.CallExpression {
24+
let callExpression: TSESTree.CallExpression | null = null
25+
26+
function traverse(node: any): void {
27+
if (node.type === 'CallExpression') {
28+
callExpression = node
29+
return
30+
}
31+
for (const key in node) {
32+
if (node[key] && typeof node[key] === 'object') {
33+
if (Array.isArray(node[key])) {
34+
node[key].forEach(traverse)
35+
} else {
36+
traverse(node[key])
37+
}
38+
}
39+
}
40+
}
41+
42+
traverse(ast)
43+
return callExpression!
44+
}
45+
46+
describe('isDefineStoreCall', () => {
47+
it('should detect direct defineStore calls', () => {
48+
const code = 'defineStore("test", () => {})'
49+
const ast = parseCode(code)
50+
const callExpr = findCallExpression(ast)
51+
expect(isDefineStoreCall(callExpr)).toBe(true)
52+
})
53+
54+
it('should detect member expression defineStore calls', () => {
55+
const code = 'pinia.defineStore("test", () => {})'
56+
const ast = parseCode(code)
57+
const callExpr = findCallExpression(ast)
58+
expect(isDefineStoreCall(callExpr)).toBe(true)
59+
})
60+
61+
it('should detect optional chaining defineStore calls', () => {
62+
const code = 'pinia?.defineStore("test", () => {})'
63+
const ast = parseCode(code)
64+
const callExpr = findCallExpression(ast)
65+
expect(isDefineStoreCall(callExpr)).toBe(true)
66+
})
67+
68+
it('should not detect non-defineStore calls', () => {
69+
const code = 'someOtherFunction("test", () => {})'
70+
const ast = parseCode(code)
71+
const callExpr = findCallExpression(ast)
72+
expect(isDefineStoreCall(callExpr)).toBe(false)
73+
})
74+
})
75+
76+
describe('getStoreId', () => {
77+
it('should extract string literal IDs', () => {
78+
const code = 'defineStore("user", () => {})'
79+
const ast = parseCode(code)
80+
const callExpr = findCallExpression(ast)
81+
expect(getStoreId(callExpr)).toBe('user')
82+
})
83+
84+
it('should extract template literal IDs without interpolations', () => {
85+
const code = 'defineStore(`user`, () => {})'
86+
const ast = parseCode(code)
87+
const callExpr = findCallExpression(ast)
88+
expect(getStoreId(callExpr)).toBe('user')
89+
})
90+
91+
it('should extract IDs from object expressions with string literals', () => {
92+
const code = 'defineStore({ id: "user" }, () => {})'
93+
const ast = parseCode(code)
94+
const callExpr = findCallExpression(ast)
95+
expect(getStoreId(callExpr)).toBe('user')
96+
})
97+
98+
it('should extract IDs from object expressions with template literals', () => {
99+
const code = 'defineStore({ id: `user` }, () => {})'
100+
const ast = parseCode(code)
101+
const callExpr = findCallExpression(ast)
102+
expect(getStoreId(callExpr)).toBe('user')
103+
})
104+
105+
it('should return null for template literals with interpolations', () => {
106+
const code = 'defineStore(`user-${suffix}`, () => {})'
107+
const ast = parseCode(code)
108+
const callExpr = findCallExpression(ast)
109+
expect(getStoreId(callExpr)).toBe(null)
110+
})
111+
})
112+
113+
describe('extractDeclarations', () => {
114+
it('should extract variable declarations and deduplicate', () => {
115+
const code = `
116+
function setup() {
117+
const name = ref('test')
118+
let count = 0
119+
const name = ref('duplicate') // This should be deduplicated
120+
return { name, count }
121+
}
122+
`
123+
const ast = parseCode(code)
124+
const func = ast.body[0] as TSESTree.FunctionDeclaration
125+
const result = extractDeclarations(func.body!)
126+
127+
expect(result.variables).toEqual(['name', 'count'])
128+
})
129+
130+
it('should extract loop initializer declarations', () => {
131+
const code = `
132+
function setup() {
133+
for (let i = 0; i < 10; i++) {
134+
console.log(i)
135+
}
136+
for (const item of items) {
137+
console.log(item)
138+
}
139+
return {}
140+
}
141+
`
142+
const ast = parseCode(code)
143+
const func = ast.body[0] as TSESTree.FunctionDeclaration
144+
const result = extractDeclarations(func.body!)
145+
146+
expect(result.variables).toContain('i')
147+
expect(result.variables).toContain('item')
148+
})
149+
150+
it('should extract catch clause parameters', () => {
151+
const code = `
152+
function setup() {
153+
try {
154+
doSomething()
155+
} catch (error) {
156+
console.log(error)
157+
}
158+
return {}
159+
}
160+
`
161+
const ast = parseCode(code)
162+
const func = ast.body[0] as TSESTree.FunctionDeclaration
163+
const result = extractDeclarations(func.body!)
164+
165+
expect(result.variables).toContain('error')
166+
})
167+
})
168+
169+
describe('extractReturnProperties', () => {
170+
it('should extract identifier property keys', () => {
171+
const code = `
172+
function setup() {
173+
return { name, count, total }
174+
}
175+
`
176+
const ast = parseCode(code)
177+
const func = ast.body[0] as TSESTree.FunctionDeclaration
178+
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
179+
const result = extractReturnProperties(returnStmt)
180+
181+
expect(result).toEqual(['name', 'count', 'total'])
182+
})
183+
184+
it('should extract quoted string property keys', () => {
185+
const code = `
186+
function setup() {
187+
return { "name": value, 'count': value2 }
188+
}
189+
`
190+
const ast = parseCode(code)
191+
const func = ast.body[0] as TSESTree.FunctionDeclaration
192+
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
193+
const result = extractReturnProperties(returnStmt)
194+
195+
expect(result).toEqual(['name', 'count'])
196+
})
197+
198+
it('should extract template literal property keys without interpolations', () => {
199+
const code = `
200+
function setup() {
201+
return { [\`name\`]: value, [\`count\`]: value2 }
202+
}
203+
`
204+
const ast = parseCode(code)
205+
const func = ast.body[0] as TSESTree.FunctionDeclaration
206+
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
207+
const result = extractReturnProperties(returnStmt)
208+
209+
expect(result).toEqual(['name', 'count'])
210+
})
211+
212+
it('should handle MemberExpression returns', () => {
213+
const code = `
214+
function setup() {
215+
return someObject.property
216+
}
217+
`
218+
const ast = parseCode(code)
219+
const func = ast.body[0] as TSESTree.FunctionDeclaration
220+
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
221+
const result = extractReturnProperties(returnStmt)
222+
223+
expect(result).toEqual([])
224+
})
225+
})

0 commit comments

Comments
 (0)