Skip to content

Commit 48bf88e

Browse files
committed
test: negative cases, cleanup a bit, add mockFetch helpers for clarity
1 parent c2270d0 commit 48bf88e

2 files changed

Lines changed: 111 additions & 30 deletions

File tree

test/unit/file-tree.spec.ts

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@ import { convertToFileTree, fetchFileTree, getPackageFileTree } from '../../serv
44

55
const getChildren = (node?: PackageFileTree): PackageFileTree[] => node?.children ?? []
66

7+
const mockFetchOk = <T>(body: T) => {
8+
const fetchMock = vi.fn().mockResolvedValue({
9+
ok: true,
10+
json: async () => body,
11+
})
12+
vi.stubGlobal('fetch', fetchMock)
13+
return fetchMock
14+
}
15+
16+
const mockFetchError = (status: number) => {
17+
const fetchMock = vi.fn().mockResolvedValue({
18+
ok: false,
19+
status,
20+
})
21+
vi.stubGlobal('fetch', fetchMock)
22+
return fetchMock
23+
}
24+
25+
const mockCreateError = () => {
26+
const createErrorMock = vi.fn((opts: { statusCode: number; message: string }) => opts)
27+
vi.stubGlobal('createError', createErrorMock)
28+
return createErrorMock
29+
}
30+
731
describe('convertToFileTree', () => {
832
it('converts jsDelivr nodes to a sorted tree with directories first', () => {
933
const input: JsDelivrFileNode[] = [
@@ -72,6 +96,20 @@ describe('convertToFileTree', () => {
7296
const empty: PackageFileTree[] = []
7397
expect(tree).toEqual(empty)
7498
})
99+
100+
it('handles directories without a files property', () => {
101+
const input: JsDelivrFileNode[] = [
102+
{
103+
type: 'directory',
104+
name: 'src',
105+
},
106+
]
107+
108+
const tree = convertToFileTree(input)
109+
110+
expect(tree[0]?.type).toBe('directory')
111+
expect(tree[0]?.children).toEqual([])
112+
})
75113
})
76114

77115
describe('fetchFileTree', () => {
@@ -84,12 +122,7 @@ describe('fetchFileTree', () => {
84122
files: [],
85123
}
86124

87-
const fetchMock = vi.fn().mockResolvedValue({
88-
ok: true,
89-
json: async () => body,
90-
})
91-
92-
vi.stubGlobal('fetch', fetchMock)
125+
mockFetchOk(body)
93126

94127
try {
95128
const result = await fetchFileTree('pkg', '1.0.0')
@@ -100,15 +133,8 @@ describe('fetchFileTree', () => {
100133
})
101134

102135
it('throws a 404 error when package is not found', async () => {
103-
const fetchMock = vi.fn().mockResolvedValue({
104-
ok: false,
105-
status: 404,
106-
})
107-
108-
const createErrorMock = vi.fn((opts: { statusCode: number; message: string }) => opts)
109-
110-
vi.stubGlobal('fetch', fetchMock)
111-
vi.stubGlobal('createError', createErrorMock)
136+
mockFetchError(404)
137+
mockCreateError()
112138

113139
try {
114140
await expect(fetchFileTree('pkg', '1.0.0')).rejects.toMatchObject({ statusCode: 404 })
@@ -118,15 +144,8 @@ describe('fetchFileTree', () => {
118144
})
119145

120146
it('throws a 502 error for non-404 failures', async () => {
121-
const fetchMock = vi.fn().mockResolvedValue({
122-
ok: false,
123-
status: 500,
124-
})
125-
126-
const createErrorMock = vi.fn((opts: { statusCode: number; message: string }) => opts)
127-
128-
vi.stubGlobal('fetch', fetchMock)
129-
vi.stubGlobal('createError', createErrorMock)
147+
mockFetchError(500)
148+
mockCreateError()
130149

131150
try {
132151
await expect(fetchFileTree('pkg', '1.0.0')).rejects.toMatchObject({ statusCode: 502 })
@@ -152,12 +171,7 @@ describe('getPackageFileTree', () => {
152171
],
153172
}
154173

155-
const fetchMock = vi.fn().mockResolvedValue({
156-
ok: true,
157-
json: async () => body,
158-
})
159-
160-
vi.stubGlobal('fetch', fetchMock)
174+
mockFetchOk(body)
161175

162176
try {
163177
const result = await getPackageFileTree('pkg', '1.0.0')
@@ -170,4 +184,22 @@ describe('getPackageFileTree', () => {
170184
vi.unstubAllGlobals()
171185
}
172186
})
187+
188+
it('returns undefined when default is missing', async () => {
189+
const body = {
190+
type: 'npm',
191+
name: 'pkg',
192+
version: '1.0.0',
193+
files: [],
194+
}
195+
196+
mockFetchOk(body)
197+
198+
try {
199+
const result = await getPackageFileTree('pkg', '1.0.0')
200+
expect(result.default).toBeUndefined()
201+
} finally {
202+
vi.unstubAllGlobals()
203+
}
204+
})
173205
})

test/unit/import-resolver.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ describe('flattenFileTree', () => {
3131
expect(files.has('dist/utils/format.js')).toBe(true)
3232
expect(files.has('dist/utils')).toBe(false)
3333
})
34+
35+
it('returns an empty set for an empty tree', () => {
36+
const files = flattenFileTree([])
37+
expect(files.size).toBe(0)
38+
})
39+
40+
it('includes root-level files', () => {
41+
const tree: PackageFileTree[] = [
42+
{ name: 'index.js', path: 'index.js', type: 'file', size: 5 },
43+
{ name: 'cli.js', path: 'cli.js', type: 'file', size: 3 },
44+
]
45+
46+
const files = flattenFileTree(tree)
47+
48+
expect(files.has('index.js')).toBe(true)
49+
expect(files.has('cli.js')).toBe(true)
50+
})
3451
})
3552

3653
describe('resolveRelativeImport', () => {
@@ -55,6 +72,20 @@ describe('resolveRelativeImport', () => {
5572
expect(resolved?.path).toBe('dist/types.d.ts')
5673
})
5774

75+
it('resolves an exact extension match', () => {
76+
const files = new Set<string>(['src/utils.ts', 'src/utils.js'])
77+
const resolved = resolveRelativeImport('./utils.ts', 'src/index.ts', files)
78+
79+
expect(resolved?.path).toBe('src/utils.ts')
80+
})
81+
82+
it('resolves a quoted specifier', () => {
83+
const files = new Set<string>(['dist/utils.js'])
84+
const resolved = resolveRelativeImport("'./utils'", 'dist/index.js', files)
85+
86+
expect(resolved?.path).toBe('dist/utils.js')
87+
})
88+
5889
it('resolves a relative import with extension priority for MTS files', () => {
5990
const files = new Set<string>(['src/utils.mts', 'src/utils.mjs', 'src/utils.ts'])
6091
const resolved = resolveRelativeImport('./utils', 'src/index.mts', files)
@@ -128,4 +159,22 @@ describe('createImportResolver', () => {
128159

129160
expect(url).toBe('/code/pkg-name/v/1.2.3/dist/utils.js')
130161
})
162+
163+
it('returns null when the import cannot be resolved', () => {
164+
const files = new Set<string>(['dist/utils.js'])
165+
const resolver = createImportResolver(files, 'dist/index.js', 'pkg-name', '1.2.3')
166+
167+
const url = resolver('./missing')
168+
169+
expect(url).toBeNull()
170+
})
171+
172+
it('handles scoped package names in URLs', () => {
173+
const files = new Set<string>(['dist/utils.js'])
174+
const resolver = createImportResolver(files, 'dist/index.js', '@scope/pkg', '1.2.3')
175+
176+
const url = resolver('./utils')
177+
178+
expect(url).toBe('/code/@scope/pkg/v/1.2.3/dist/utils.js')
179+
})
131180
})

0 commit comments

Comments
 (0)