forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2import type { JsrPackageInfo } from '#shared/types/jsr'
3
4describe('useInstallCommand', () => {
5 beforeEach(() => {
6 // Reset localStorage and package manager state before each test
7 localStorage.clear()
8 // Reset the shared composable state to default 'npm'
9 const pm = useSelectedPackageManager()
10 pm.value = 'npm'
11 })
12
13 afterEach(() => {
14 vi.unstubAllGlobals()
15 })
16
17 describe('basic install commands', () => {
18 it('should generate npm install command by default', () => {
19 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
20 'vue',
21 null,
22 null,
23 null,
24 )
25
26 expect(selectedPM.value).toBe('npm')
27 expect(installCommand.value).toBe('npm install vue')
28 expect(installCommandParts.value).toEqual(['npm', 'install', 'vue'])
29 })
30
31 it('should include version when specified', () => {
32 const { installCommand, installCommandParts } = useInstallCommand('vue', '3.5.0', null, null)
33
34 expect(installCommand.value).toBe('npm install vue@3.5.0')
35 expect(installCommandParts.value).toEqual(['npm', 'install', 'vue@3.5.0'])
36 })
37
38 it('should handle scoped packages', () => {
39 const { installCommand, installCommandParts } = useInstallCommand(
40 '@nuxt/kit',
41 null,
42 null,
43 null,
44 )
45
46 expect(installCommand.value).toBe('npm install @nuxt/kit')
47 expect(installCommandParts.value).toEqual(['npm', 'install', '@nuxt/kit'])
48 })
49
50 it('should handle null packageName', () => {
51 const { installCommand, installCommandParts } = useInstallCommand(null, null, null, null)
52
53 expect(installCommand.value).toBe('')
54 expect(installCommandParts.value).toEqual([])
55 })
56 })
57
58 describe('package manager selection', () => {
59 it('should use pnpm when selected', () => {
60 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
61 'vue',
62 null,
63 null,
64 null,
65 )
66
67 selectedPM.value = 'pnpm'
68 expect(installCommand.value).toBe('pnpm add vue')
69 expect(installCommandParts.value).toEqual(['pnpm', 'add', 'vue'])
70 })
71
72 it('should use yarn when selected', () => {
73 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
74 'vue',
75 null,
76 null,
77 null,
78 )
79
80 selectedPM.value = 'yarn'
81 expect(installCommand.value).toBe('yarn add vue')
82 expect(installCommandParts.value).toEqual(['yarn', 'add', 'vue'])
83 })
84
85 it('should use bun when selected', () => {
86 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
87 'vue',
88 null,
89 null,
90 null,
91 )
92
93 selectedPM.value = 'bun'
94 expect(installCommand.value).toBe('bun add vue')
95 expect(installCommandParts.value).toEqual(['bun', 'add', 'vue'])
96 })
97
98 it('should use deno with npm: prefix when selected', () => {
99 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
100 'vue',
101 null,
102 null,
103 null,
104 )
105
106 selectedPM.value = 'deno'
107 expect(installCommand.value).toBe('deno add npm:vue')
108 expect(installCommandParts.value).toEqual(['deno', 'add', 'npm:vue'])
109 })
110
111 it('should use vlt when selected', () => {
112 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
113 'vue',
114 null,
115 null,
116 null,
117 )
118
119 selectedPM.value = 'vlt'
120 expect(installCommand.value).toBe('vlt install vue')
121 expect(installCommandParts.value).toEqual(['vlt', 'install', 'vue'])
122 })
123 })
124
125 describe('deno with JSR', () => {
126 it('should use jsr: prefix when package exists on JSR', () => {
127 const jsrInfo: JsrPackageInfo = {
128 exists: true,
129 scope: 'std',
130 name: 'path',
131 url: 'https://jsr.io/@std/path',
132 }
133
134 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
135 '@std/path',
136 null,
137 jsrInfo,
138 null,
139 )
140
141 selectedPM.value = 'deno'
142 expect(installCommand.value).toBe('deno add jsr:@std/path')
143 expect(installCommandParts.value).toEqual(['deno', 'add', 'jsr:@std/path'])
144 })
145
146 it('should use npm: prefix for deno when package is not on JSR', () => {
147 const jsrInfo: JsrPackageInfo = { exists: false }
148
149 const { installCommand, installCommandParts, selectedPM } = useInstallCommand(
150 'lodash',
151 null,
152 jsrInfo,
153 null,
154 )
155
156 selectedPM.value = 'deno'
157 expect(installCommand.value).toBe('deno add npm:lodash')
158 expect(installCommandParts.value).toEqual(['deno', 'add', 'npm:lodash'])
159 })
160 })
161
162 describe('@types packages', () => {
163 it('should generate @types install command parts', () => {
164 const { typesInstallCommandParts, showTypesInInstall } = useInstallCommand(
165 'express',
166 null,
167 null,
168 '@types/express',
169 )
170
171 expect(showTypesInInstall.value).toBe(true)
172 expect(typesInstallCommandParts.value).toEqual(['npm', 'install', '-D', '@types/express'])
173 })
174
175 it('should use -d flag for bun', () => {
176 const { typesInstallCommandParts, selectedPM } = useInstallCommand(
177 'express',
178 null,
179 null,
180 '@types/express',
181 )
182
183 selectedPM.value = 'bun'
184 expect(typesInstallCommandParts.value).toEqual(['bun', 'add', '-d', '@types/express'])
185 })
186
187 it('should use npm: prefix for deno @types', () => {
188 const { typesInstallCommandParts, selectedPM } = useInstallCommand(
189 'express',
190 null,
191 null,
192 '@types/express',
193 )
194
195 selectedPM.value = 'deno'
196 expect(typesInstallCommandParts.value).toEqual(['deno', 'add', '-D', 'npm:@types/express'])
197 })
198
199 it('should not show @types when typesPackageName is null', () => {
200 const { showTypesInInstall, typesInstallCommandParts } = useInstallCommand(
201 'express',
202 null,
203 null,
204 null,
205 )
206
207 expect(showTypesInInstall.value).toBe(false)
208 expect(typesInstallCommandParts.value).toEqual([])
209 })
210 })
211
212 describe('fullInstallCommand with @types', () => {
213 it('should include both commands when @types enabled', () => {
214 const { fullInstallCommand } = useInstallCommand('express', null, null, '@types/express')
215
216 expect(fullInstallCommand.value).toBe('npm install express; npm install -D @types/express')
217 })
218
219 it('should only include main command when @types disabled via settings', () => {
220 // Get settings and disable includeTypesInInstall directly
221 const { settings } = useSettings()
222 settings.value.includeTypesInInstall = false
223
224 const { fullInstallCommand, showTypesInInstall } = useInstallCommand(
225 'express',
226 null,
227 null,
228 '@types/express',
229 )
230
231 expect(showTypesInInstall.value).toBe(false)
232 expect(fullInstallCommand.value).toBe('npm install express')
233 })
234 })
235
236 describe('reactive updates', () => {
237 it('should update command when package manager changes', () => {
238 const { installCommand, selectedPM } = useInstallCommand('vue', null, null, null)
239
240 expect(installCommand.value).toBe('npm install vue')
241
242 selectedPM.value = 'pnpm'
243 expect(installCommand.value).toBe('pnpm add vue')
244
245 selectedPM.value = 'yarn'
246 expect(installCommand.value).toBe('yarn add vue')
247 })
248
249 it('should update when using ref values', () => {
250 const packageName = shallowRef<string | null>('vue')
251 const version = shallowRef<string | null>(null)
252
253 const { installCommand } = useInstallCommand(packageName, version, null, null)
254
255 expect(installCommand.value).toBe('npm install vue')
256
257 packageName.value = 'react'
258 expect(installCommand.value).toBe('npm install react')
259
260 version.value = '18.2.0'
261 expect(installCommand.value).toBe('npm install react@18.2.0')
262 })
263
264 it('should prefer installVersionOverride when provided', () => {
265 const requestedVersion = shallowRef<string | null>(null)
266 const installVersionOverride = shallowRef<string | null>('1.0.0')
267
268 const { installCommand } = useInstallCommand(
269 'foo',
270 requestedVersion,
271 null,
272 null,
273 installVersionOverride,
274 )
275
276 expect(installCommand.value).toBe('npm install foo@1.0.0')
277
278 installVersionOverride.value = null
279 requestedVersion.value = '2.0.0'
280 expect(installCommand.value).toBe('npm install foo@2.0.0')
281 })
282 })
283
284 describe('copyInstallCommand', () => {
285 it('should copy command to clipboard and set copied state', async () => {
286 vi.useFakeTimers()
287
288 const { copyInstallCommand, copied, fullInstallCommand } = useInstallCommand(
289 'vue',
290 null,
291 null,
292 null,
293 )
294
295 expect(fullInstallCommand.value).toBe('npm install vue')
296 expect(copied.value).toBe(false)
297
298 await copyInstallCommand()
299
300 // useClipboard sets copied to true after successful copy
301 expect(copied.value).toBe(true)
302
303 // Advance timers to reset copied (copiedDuring: 2000)
304 await vi.advanceTimersByTimeAsync(2100)
305 expect(copied.value).toBe(false)
306
307 vi.useRealTimers()
308 })
309
310 it('should not copy when command is empty', async () => {
311 const { copyInstallCommand, copied } = useInstallCommand(null, null, null, null)
312
313 expect(copied.value).toBe(false)
314 await copyInstallCommand()
315
316 // Should remain false since there was nothing to copy
317 expect(copied.value).toBe(false)
318 })
319 })
320})