1const t = require('tap')
2const fs = require('fs/promises')
3const path = require('path')
4const { load: loadMockNpm } = require('../../fixtures/mock-npm.js')
5const MockRegistry = require('@npmcli/mock-registry')
6
7t.test('call with args', async t => {
8  const { npm } = await loadMockNpm(t, {
9    config: {
10      call: 'foo',
11    },
12  })
13
14  await t.rejects(
15    npm.exec('exec', ['bar']),
16    { code: 'EUSAGE' }
17  )
18})
19
20t.test('registry package', async t => {
21  const registry = new MockRegistry({
22    tap: t,
23    registry: 'https://registry.npmjs.org/',
24  })
25
26  const manifest = registry.manifest({ name: '@npmcli/npx-test' })
27  manifest.versions['1.0.0'].bin = { 'npx-test': 'index.js' }
28
29  const { npm } = await loadMockNpm(t, {
30    config: {
31      audit: false,
32      yes: true,
33    },
34    prefixDir: {
35      'npm-exec-test': {
36        'package.json': JSON.stringify(manifest),
37        'index.js': `#!/usr/bin/env node
38  require('fs').writeFileSync('npm-exec-test-success', '')`,
39      },
40    },
41  })
42
43  await registry.package({
44    times: 2,
45    manifest,
46    tarballs: {
47      '1.0.0': path.join(npm.prefix, 'npm-exec-test'),
48    } })
49
50  await npm.exec('exec', ['@npmcli/npx-test'])
51  const exists = await fs.stat(path.join(npm.prefix, 'npm-exec-test-success'))
52  t.ok(exists.isFile(), 'bin ran, creating file')
53})
54
55t.test('--prefix', async t => {
56  const registry = new MockRegistry({
57    tap: t,
58    registry: 'https://registry.npmjs.org/',
59  })
60
61  const manifest = registry.manifest({ name: '@npmcli/npx-test' })
62  manifest.versions['1.0.0'].bin = { 'npx-test': 'index.js' }
63
64  const { npm } = await loadMockNpm(t, {
65    config: {
66      audit: false,
67      yes: true,
68    },
69    prefixDir: {
70      'npm-exec-test': {
71        'package.json': JSON.stringify(manifest),
72        'index.js': `#!/usr/bin/env node
73  require('fs').writeFileSync('npm-exec-test-success', '')`,
74      },
75    },
76  })
77
78  // This is what `--prefix` does
79  npm.globalPrefix = npm.localPrefix
80
81  await registry.package({
82    manifest,
83    tarballs: {
84      '1.0.0': path.join(npm.prefix, 'npm-exec-test'),
85    } })
86
87  await npm.exec('exec', ['@npmcli/npx-test'])
88  const exists = await fs.stat(path.join(npm.prefix, 'npm-exec-test-success'))
89  t.ok(exists.isFile(), 'bin ran, creating file')
90})
91
92t.test('runs in workspace path', async t => {
93  const registry = new MockRegistry({
94    tap: t,
95    registry: 'https://registry.npmjs.org/',
96  })
97
98  const manifest = registry.manifest({ name: '@npmcli/npx-test' })
99  manifest.versions['1.0.0'].bin = { 'npx-test': 'index.js' }
100
101  const { npm } = await loadMockNpm(t, {
102    config: {
103      audit: false,
104      yes: true,
105      workspace: ['workspace-a'],
106    },
107    prefixDir: {
108      'npm-exec-test': {
109        'package.json': JSON.stringify(manifest),
110        'index.js': `#!/usr/bin/env node
111  require('fs').writeFileSync('npm-exec-test-success', '')`,
112      },
113      'package.json': JSON.stringify({
114        name: '@npmcli/npx-workspace-test',
115        workspaces: ['workspace-a'],
116      }),
117      'workspace-a': {
118        'package.json': JSON.stringify({
119          name: 'workspace-a',
120        }),
121      },
122    },
123  })
124
125  await registry.package({ manifest,
126    tarballs: {
127      '1.0.0': path.join(npm.prefix, 'npm-exec-test'),
128    },
129  })
130  await npm.exec('exec', ['@npmcli/npx-test'])
131  const exists = await fs.stat(path.join(npm.prefix, 'workspace-a', 'npm-exec-test-success'))
132  t.ok(exists.isFile(), 'bin ran, creating file inside workspace')
133})
134
135t.test('finds workspace bin first', async t => {
136  const { npm } = await loadMockNpm(t, {
137    config: {
138      workspace: ['workspace-a'],
139    },
140    prefixDir: {
141      'package.json': JSON.stringify({
142        name: '@npmcli/npx-workspace-root-test',
143        bin: { 'npx-test': 'index.js' },
144        workspaces: ['workspace-a'],
145      }),
146      'index.js': `#!/usr/bin/env node
147  require('fs').writeFileSync('npm-exec-test-fail', '')`,
148      'workspace-a': {
149        'package.json': JSON.stringify({
150          name: '@npmcli/npx-workspace-test',
151          bin: { 'npx-test': 'index.js' },
152        }),
153        'index.js': `#!/usr/bin/env node
154  require('fs').writeFileSync('npm-exec-test-success', '')`,
155      },
156    },
157  })
158
159  await npm.exec('install', []) // reify
160  await npm.exec('exec', ['npx-test'])
161  const exists = await fs.stat(path.join(npm.prefix, 'workspace-a', 'npm-exec-test-success'))
162  t.ok(exists.isFile(), 'bin ran, creating file inside workspace')
163  t.rejects(fs.stat(path.join(npm.prefix, 'npm-exec-test-fail')))
164})
165
166t.test('finds workspace dep first', async t => {
167  const registry = new MockRegistry({
168    tap: t,
169    registry: 'https://registry.npmjs.org/',
170  })
171
172  const manifest = registry.manifest({ name: '@npmcli/subdep', versions: ['1.0.0', '2.0.0'] })
173  manifest.versions['1.0.0'].bin = { 'npx-test': 'index.js' }
174  manifest.versions['2.0.0'].bin = { 'npx-test': 'index.js' }
175
176  const { npm } = await loadMockNpm(t, {
177    prefixDir: {
178      subdep: {
179        one: {
180          'package.json': JSON.stringify(manifest.versions['1.0.0']),
181          'index.js': `#!/usr/bin/env node
182  require('fs').writeFileSync('npm-exec-test-one', '')`,
183        },
184        two: {
185          'package.json': JSON.stringify(manifest.versions['2.0.0']),
186          'index.js': `#!/usr/bin/env node
187  require('fs').writeFileSync('npm-exec-test-two', '')`,
188        },
189      },
190      'package.json': JSON.stringify({
191        name: '@npmcli/npx-workspace-root-test',
192        dependencies: { '@npmcli/subdep': '1.0.0' },
193        bin: { 'npx-test': 'index.js' },
194        workspaces: ['workspace-a'],
195      }),
196      'index.js': `#!/usr/bin/env node
197  require('fs').writeFileSync('npm-exec-test-fail', '')`,
198      'workspace-a': {
199        'package.json': JSON.stringify({
200          name: '@npmcli/npx-workspace-test',
201          dependencies: { '@npmcli/subdep': '2.0.0' },
202          bin: { 'npx-test': 'index.js' },
203        }),
204        'index.js': `#!/usr/bin/env node
205  require('fs').writeFileSync('npm-exec-test-success', '')`,
206      },
207    },
208  })
209
210  await registry.package({ manifest,
211    tarballs: {
212      '1.0.0': path.join(npm.prefix, 'subdep', 'one'),
213      '2.0.0': path.join(npm.prefix, 'subdep', 'two'),
214    },
215  })
216  await npm.exec('install', [])
217  npm.config.set('workspace', ['workspace-a'])
218  await npm.exec('exec', ['npx-test'])
219  const exists = await fs.stat(path.join(npm.prefix, 'workspace-a', 'npm-exec-test-success'))
220  t.ok(exists.isFile(), 'bin ran, creating file')
221})
222
223t.test('npx --no-install @npmcli/npx-test', async t => {
224  const registry = new MockRegistry({
225    tap: t,
226    registry: 'https://registry.npmjs.org/',
227  })
228
229  const manifest = registry.manifest({ name: '@npmcli/npx-test' })
230  manifest.versions['1.0.0'].bin = { 'npx-test': 'index.js' }
231
232  const { npm } = await loadMockNpm(t, {
233    config: {
234      audit: false,
235      yes: false,
236    },
237    prefixDir: {
238      'npm-exec-test': {
239        'package.json': JSON.stringify(manifest),
240        'index.js': `#!/usr/bin/env node
241  require('fs').writeFileSync('npm-exec-test-success', '')`,
242      },
243    },
244  })
245
246  try {
247    await npm.exec('exec', ['@npmcli/npx-test'])
248    t.fail('Expected error was not thrown')
249  } catch (error) {
250    t.match(
251      error.message,
252      'npx canceled due to missing packages and no YES option: ',
253      'Expected error message thrown'
254    )
255  }
256})
257