Skip to content

Commit a04610d

Browse files
authored
test(oxc-unshadowed-visitor): add unit tests for internal functions (#45)
1 parent 5b018d1 commit a04610d

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { describe, test, expect } from 'vitest'
2+
import { parseSync, type ESTree } from 'rolldown/utils'
3+
import { extractBindingNames } from './bindingNames.ts'
4+
5+
function parse(code: string) {
6+
return parseSync('test.js', code).program
7+
}
8+
9+
describe('extractBindingNames', () => {
10+
function extractFromParam(code: string): string[] {
11+
const program = parse(`function f(${code}) {}`)
12+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
13+
const fn = program.body[0] as ESTree.Function
14+
const names: string[] = []
15+
for (const param of fn.params) {
16+
extractBindingNames(param, names)
17+
}
18+
return names
19+
}
20+
21+
function extractFromDecl(code: string): string[] {
22+
const program = parse(code)
23+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
24+
const decl = program.body[0] as ESTree.VariableDeclaration
25+
const names: string[] = []
26+
for (const declarator of decl.declarations) {
27+
extractBindingNames(declarator.id, names)
28+
}
29+
return names
30+
}
31+
32+
test('simple identifier', () => {
33+
expect(extractFromParam('a')).toEqual(['a'])
34+
})
35+
36+
test('multiple params', () => {
37+
expect(extractFromParam('a, b, c')).toEqual(['a', 'b', 'c'])
38+
})
39+
40+
test('rest param', () => {
41+
expect(extractFromParam('a, ...rest')).toEqual(['a', 'rest'])
42+
})
43+
44+
test('array destructuring', () => {
45+
expect(extractFromDecl('const [a, b] = arr')).toEqual(['a', 'b'])
46+
})
47+
48+
test('array destructuring with holes', () => {
49+
expect(extractFromDecl('const [a, , b] = arr')).toEqual(['a', 'b'])
50+
})
51+
52+
test('object destructuring', () => {
53+
expect(extractFromDecl('const { a, b } = obj')).toEqual(['a', 'b'])
54+
})
55+
56+
test('renamed object destructuring', () => {
57+
expect(extractFromDecl('const { x: a, y: b } = obj')).toEqual(['a', 'b'])
58+
})
59+
60+
test('rest element in array', () => {
61+
expect(extractFromDecl('const [a, ...rest] = arr')).toEqual(['a', 'rest'])
62+
})
63+
64+
test('rest element in object', () => {
65+
expect(extractFromDecl('const { a, ...rest } = obj')).toEqual(['a', 'rest'])
66+
})
67+
68+
test('assignment pattern', () => {
69+
expect(extractFromParam('a = 1, b = 2')).toEqual(['a', 'b'])
70+
})
71+
72+
test('nested destructuring', () => {
73+
expect(extractFromDecl('const { a: { b, c }, d } = obj')).toEqual(['b', 'c', 'd'])
74+
})
75+
76+
test('deeply nested mixed destructuring', () => {
77+
expect(extractFromDecl('const { a: [b, { c: d }], ...e } = obj')).toEqual(['b', 'd', 'e'])
78+
})
79+
})
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// oxlint-disable unicorn/consistent-function-scoping
2+
import { describe, test, expect } from 'vitest'
3+
import { mergeVisitors } from './mergeVisitors.ts'
4+
import type { VisitorContext } from './types.ts'
5+
import type { ESTree } from 'rolldown/utils'
6+
7+
describe('mergeVisitors', () => {
8+
const dummyIdentifierNode: ESTree.IdentifierReference = {
9+
type: 'Identifier',
10+
name: 'foo',
11+
start: 0,
12+
end: 3,
13+
}
14+
function makeCtx(): VisitorContext<string> {
15+
return {
16+
record() {},
17+
}
18+
}
19+
20+
test('user enter runs after internal enter', () => {
21+
const order: string[] = []
22+
const merged = mergeVisitors(
23+
{
24+
Identifier: () => order.push('user-enter'),
25+
},
26+
makeCtx(),
27+
{ Identifier: () => order.push('internal-enter') },
28+
{},
29+
)
30+
merged.Identifier(dummyIdentifierNode)
31+
expect(order).toEqual(['internal-enter', 'user-enter'])
32+
})
33+
34+
test('user exit runs before internal exit', () => {
35+
const order: string[] = []
36+
const merged = mergeVisitors(
37+
{
38+
'Identifier:exit': () => order.push('user-exit'),
39+
},
40+
makeCtx(),
41+
{},
42+
{ 'Identifier:exit': () => order.push('internal-exit') },
43+
)
44+
merged['Identifier:exit'](dummyIdentifierNode)
45+
expect(order).toEqual(['user-exit', 'internal-exit'])
46+
})
47+
48+
test('internal-only visitors are included', () => {
49+
const ctx = makeCtx()
50+
const enterFn = () => {}
51+
const exitFn = () => {}
52+
const merged = mergeVisitors({}, ctx, { Identifier: enterFn }, { 'Identifier:exit': exitFn })
53+
expect(merged.Identifier).toBe(enterFn)
54+
expect(merged['Identifier:exit']).toBe(exitFn)
55+
})
56+
57+
test('user-only visitors are included', () => {
58+
const called: string[] = []
59+
const ctx = makeCtx()
60+
const merged = mergeVisitors(
61+
{
62+
Identifier: () => called.push('identifier'),
63+
'Identifier:exit': () => called.push('identifier-exit'),
64+
},
65+
ctx,
66+
{},
67+
{},
68+
)
69+
merged.Identifier(dummyIdentifierNode)
70+
merged['Identifier:exit'](dummyIdentifierNode)
71+
expect(called).toEqual(['identifier', 'identifier-exit'])
72+
})
73+
74+
test('ctx is passed to user visitor functions', () => {
75+
let receivedCtx: unknown
76+
const ctx = makeCtx()
77+
const merged = mergeVisitors(
78+
{
79+
Identifier: (_node, c) => {
80+
receivedCtx = c
81+
},
82+
},
83+
ctx,
84+
{},
85+
{},
86+
)
87+
merged.Identifier(dummyIdentifierNode)
88+
expect(receivedCtx).toBe(ctx)
89+
})
90+
91+
test('ctx is passed to user exit visitor functions', () => {
92+
let receivedCtx: unknown
93+
const ctx = makeCtx()
94+
const merged = mergeVisitors(
95+
{
96+
'Identifier:exit': (_node, c) => {
97+
receivedCtx = c
98+
},
99+
},
100+
ctx,
101+
{},
102+
{},
103+
)
104+
merged['Identifier:exit'](dummyIdentifierNode)
105+
expect(receivedCtx).toBe(ctx)
106+
})
107+
})

0 commit comments

Comments
 (0)