@verdaccio/e2e-cli 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/build/adapters/bun.d.ts +2 -0
  2. package/build/adapters/deno.d.ts +2 -0
  3. package/build/adapters/index.d.ts +2 -0
  4. package/build/cjs/adapters/bun.cjs +63 -0
  5. package/build/cjs/adapters/bun.cjs.map +1 -0
  6. package/build/cjs/adapters/deno.cjs +58 -0
  7. package/build/cjs/adapters/deno.cjs.map +1 -0
  8. package/build/cjs/adapters/index.cjs +2 -0
  9. package/build/cjs/adapters/yarn-classic.cjs.map +1 -1
  10. package/build/cjs/adapters/yarn-modern.cjs +24 -4
  11. package/build/cjs/adapters/yarn-modern.cjs.map +1 -1
  12. package/build/cjs/index.cjs +10 -3
  13. package/build/cjs/index.cjs.map +1 -1
  14. package/build/cjs/reporter.cjs.map +1 -1
  15. package/build/cjs/runner.cjs.map +1 -1
  16. package/build/cjs/scenarios/index.cjs.map +1 -1
  17. package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -1
  18. package/build/cjs/tests/audit.cjs +8 -5
  19. package/build/cjs/tests/audit.cjs.map +1 -1
  20. package/build/cjs/tests/ci.cjs +9 -0
  21. package/build/cjs/tests/ci.cjs.map +1 -1
  22. package/build/cjs/tests/index.cjs.map +1 -1
  23. package/build/cjs/tests/info.cjs +3 -0
  24. package/build/cjs/tests/info.cjs.map +1 -1
  25. package/build/cjs/tests/login.cjs.map +1 -1
  26. package/build/cjs/tests/publish.cjs +1 -0
  27. package/build/cjs/tests/publish.cjs.map +1 -1
  28. package/build/cjs/tests/star.cjs.map +1 -1
  29. package/build/cjs/utils/process.cjs +1 -1
  30. package/build/cjs/utils/process.cjs.map +1 -1
  31. package/build/cjs/utils/registry-client.cjs.map +1 -1
  32. package/build/esm/adapters/bun.js +61 -0
  33. package/build/esm/adapters/bun.js.map +1 -0
  34. package/build/esm/adapters/deno.js +56 -0
  35. package/build/esm/adapters/deno.js.map +1 -0
  36. package/build/esm/adapters/index.js +2 -0
  37. package/build/esm/adapters/yarn-classic.js.map +1 -1
  38. package/build/esm/adapters/yarn-modern.js +24 -4
  39. package/build/esm/adapters/yarn-modern.js.map +1 -1
  40. package/build/esm/index.js +9 -4
  41. package/build/esm/index.js.map +1 -1
  42. package/build/esm/reporter.js.map +1 -1
  43. package/build/esm/runner.js.map +1 -1
  44. package/build/esm/scenarios/index.js.map +1 -1
  45. package/build/esm/scenarios/install-multiple-deps.js.map +1 -1
  46. package/build/esm/tests/audit.js +8 -5
  47. package/build/esm/tests/audit.js.map +1 -1
  48. package/build/esm/tests/ci.js +9 -0
  49. package/build/esm/tests/ci.js.map +1 -1
  50. package/build/esm/tests/index.js.map +1 -1
  51. package/build/esm/tests/info.js +3 -0
  52. package/build/esm/tests/info.js.map +1 -1
  53. package/build/esm/tests/login.js.map +1 -1
  54. package/build/esm/tests/publish.js +1 -0
  55. package/build/esm/tests/publish.js.map +1 -1
  56. package/build/esm/tests/star.js.map +1 -1
  57. package/build/esm/utils/process.js +1 -1
  58. package/build/esm/utils/process.js.map +1 -1
  59. package/build/esm/utils/registry-client.js.map +1 -1
  60. package/build/index.d.ts +1 -1
  61. package/build/tests/index.d.ts +1 -1
  62. package/build/types.d.ts +2 -3
  63. package/build/utils/process.d.ts +0 -1
  64. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport {\n createNpmAdapter,\n createPnpmAdapter,\n createYarnClassicAdapter,\n createYarnModernAdapter,\n} from './adapters';\nimport { allTests } from './tests';\nimport { CliOptions, PackageManagerAdapter } from './types';\nimport { createUser, pingRegistry } from './utils/registry-client';\nimport { runAll } from './runner';\n\nconst debug = buildDebug('verdaccio:e2e-cli');\n\nfunction parsePmSpec(filter: string): { name: string; version?: string; binPath?: string } {\n // --pm yarn-modern@3, --pm yarn-classic@1.22.22, --pm npm=/path/to/bin\n const eqIdx = filter.indexOf('=');\n if (eqIdx !== -1) {\n return { name: filter.slice(0, eqIdx).toLowerCase(), binPath: filter.slice(eqIdx + 1) };\n }\n const atIdx = filter.indexOf('@');\n if (atIdx !== -1) {\n return { name: filter.slice(0, atIdx).toLowerCase(), version: filter.slice(atIdx + 1) };\n }\n return { name: filter.toLowerCase() };\n}\n\nfunction parseAdapters(pmFilters?: string[]): PackageManagerAdapter[] {\n if (!pmFilters || pmFilters.length === 0) {\n return [createNpmAdapter()];\n }\n\n const adapters: PackageManagerAdapter[] = [];\n\n for (const filter of pmFilters) {\n const { name, version, binPath } = parsePmSpec(filter);\n\n if (name === 'npm') {\n adapters.push(createNpmAdapter(binPath, version));\n } else if (name === 'pnpm') {\n adapters.push(createPnpmAdapter(binPath, version));\n } else if (name === 'yarn-classic' || name === 'yarn1') {\n adapters.push(createYarnClassicAdapter(binPath, version));\n } else if (name === 'yarn-modern' || name === 'yarn') {\n adapters.push(createYarnModernAdapter(binPath, version));\n } else {\n throw new Error(\n `Unknown package manager: \"${name}\". Supported: npm, pnpm, yarn-classic, yarn-modern[@version]`\n );\n }\n }\n\n return adapters;\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n const options: CliOptions = {\n registry: '',\n pm: [],\n test: [],\n concurrency: 1,\n timeout: 50000,\n verbose: false,\n };\n\n for (let i = 2; i < argv.length; i++) {\n const arg = argv[i];\n\n if (arg === '--registry' || arg === '-r') {\n options.registry = argv[++i];\n } else if (arg === '--pm') {\n options.pm!.push(argv[++i]);\n } else if (arg === '--test' || arg === '-t') {\n options.test!.push(argv[++i]);\n } else if (arg === '--concurrency' || arg === '-c') {\n options.concurrency = parseInt(argv[++i], 10);\n } else if (arg === '--timeout') {\n options.timeout = parseInt(argv[++i], 10);\n } else if (arg === '--token') {\n options.token = argv[++i];\n } else if (arg === '--verbose' || arg === '-v') {\n options.verbose = true;\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n } else if (arg.startsWith('--registry=')) {\n options.registry = arg.split('=')[1];\n } else if (arg.startsWith('--pm=')) {\n options.pm!.push(arg.split('=')[1]);\n } else if (arg.startsWith('--test=')) {\n options.test!.push(arg.split('=')[1]);\n } else if (arg.startsWith('--token=')) {\n options.token = arg.split('=')[1];\n } else if (arg.startsWith('--timeout=')) {\n options.timeout = parseInt(arg.split('=')[1], 10);\n } else {\n console.error(`Unknown argument: ${arg}`);\n printHelp();\n process.exit(1);\n }\n }\n\n return options;\n}\n\nfunction printHelp(): void {\n console.log(`\n @verdaccio/e2e-cli - Run Verdaccio e2e tests against any running registry\n\n Usage:\n verdaccio-e2e --registry <url> [options]\n\n Required:\n -r, --registry <url> Verdaccio registry URL (e.g. http://localhost:4873)\n\n Options:\n --pm <name[@version]> Package manager to test (can be repeated)\n Supported: npm, pnpm, yarn-classic, yarn-modern (or yarn)\n Examples: --pm npm@10 --pm pnpm@9\n --pm yarn-modern@4\n --pm yarn-modern@3\n --pm yarn-classic\n --pm npm --pm pnpm (uses system version)\n Auto-installs the requested yarn version if needed\n Default: npm\n\n -t, --test <name> Filter tests by name (can be repeated)\n Available: publish, install, ci, audit, info, deprecate,\n dist-tags, ping, search, star, unpublish\n Scenarios: scenario:install-multiple-deps\n Default: all supported by the PM\n\n --token <token> Auth token (skips user creation)\n --timeout <ms> Per-test timeout (default: 50000)\n -v, --verbose Enable debug output\n -h, --help Show this help\n `);\n}\n\nexport async function main(argv: string[] = process.argv): Promise<void> {\n const options = parseArgs(argv);\n\n if (!options.registry) {\n console.error('Error: --registry is required\\n');\n printHelp();\n process.exit(1);\n }\n\n // Run from a temp dir to isolate from the host project's packageManager, .yarnrc.yml, etc.\n const { createTempFolder } = await import('./utils/project');\n const runDir = await createTempFolder('e2e-run');\n process.chdir(runDir);\n\n if (options.verbose) {\n const { setVerbose } = await import('./utils/process');\n setVerbose(true);\n }\n\n // Ensure registry is reachable\n console.log(`Checking registry at ${options.registry}...`);\n const alive = await pingRegistry(options.registry);\n if (!alive) {\n console.error(`Error: Registry at ${options.registry} is not reachable`);\n process.exit(1);\n }\n console.log(`Registry is alive.`);\n\n // Get auth token\n let token = options.token;\n if (!token) {\n console.log('Creating test user...');\n const auth = await createUser(options.registry);\n token = auth.token;\n console.log(`User \"${auth.user}\" created.`);\n }\n\n // Build adapter list\n const adapters = parseAdapters(options.pm);\n console.log(`Adapters: ${adapters.map((a) => a.name).join(', ')}`);\n\n // Filter tests\n const tests = options.test && options.test.length > 0\n ? allTests.filter((t) => options.test!.includes(t.name))\n : allTests;\n\n console.log(`Tests: ${tests.map((t) => t.name).join(', ')}`);\n\n // Run\n const { exitCode } = await runAll(adapters, tests, options.registry, token, {\n timeout: options.timeout,\n concurrency: options.concurrency,\n testFilter: options.test,\n });\n\n process.exit(exitCode);\n}\n\n// Re-export for programmatic usage\nexport { allTests } from './tests';\nexport { allScenarios } from './scenarios';\nexport { createNpmAdapter, createPnpmAdapter, createYarnClassicAdapter, createYarnModernAdapter } from './adapters';\nexport { runAll, runSuite } from './runner';\nexport type { PackageManagerAdapter, TestDefinition, TestContext, CliOptions } from './types';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport {\n createBunAdapter,\n createDenoAdapter,\n createNpmAdapter,\n createPnpmAdapter,\n createYarnClassicAdapter,\n createYarnModernAdapter,\n} from './adapters';\nimport { runAll } from './runner';\nimport { allTests } from './tests';\nimport { CliOptions, PackageManagerAdapter } from './types';\nimport { createUser, pingRegistry } from './utils/registry-client';\n\nconst debug = buildDebug('verdaccio:e2e-cli');\n\nfunction parsePmSpec(filter: string): { name: string; version?: string; binPath?: string } {\n // --pm yarn-modern@3, --pm yarn-classic@1.22.22, --pm npm=/path/to/bin\n const eqIdx = filter.indexOf('=');\n if (eqIdx !== -1) {\n return { name: filter.slice(0, eqIdx).toLowerCase(), binPath: filter.slice(eqIdx + 1) };\n }\n const atIdx = filter.indexOf('@');\n if (atIdx !== -1) {\n return { name: filter.slice(0, atIdx).toLowerCase(), version: filter.slice(atIdx + 1) };\n }\n return { name: filter.toLowerCase() };\n}\n\nfunction parseAdapters(pmFilters?: string[]): PackageManagerAdapter[] {\n if (!pmFilters || pmFilters.length === 0) {\n return [createNpmAdapter()];\n }\n\n const adapters: PackageManagerAdapter[] = [];\n\n for (const filter of pmFilters) {\n const { name, version, binPath } = parsePmSpec(filter);\n\n if (name === 'npm') {\n adapters.push(createNpmAdapter(binPath, version));\n } else if (name === 'pnpm') {\n adapters.push(createPnpmAdapter(binPath, version));\n } else if (name === 'yarn-classic' || name === 'yarn1') {\n adapters.push(createYarnClassicAdapter(binPath, version));\n } else if (name === 'yarn-modern' || name === 'yarn') {\n adapters.push(createYarnModernAdapter(binPath, version));\n } else if (name === 'bun') {\n adapters.push(createBunAdapter(binPath, version));\n } else if (name === 'deno') {\n adapters.push(createDenoAdapter(binPath, version));\n } else {\n throw new Error(\n `Unknown package manager: \"${name}\". Supported: npm, pnpm, yarn-classic, yarn-modern, bun, deno`\n );\n }\n }\n\n return adapters;\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n const options: CliOptions = {\n registry: '',\n pm: [],\n test: [],\n concurrency: 1,\n timeout: 50000,\n verbose: false,\n };\n\n for (let i = 2; i < argv.length; i++) {\n const arg = argv[i];\n\n if (arg === '--registry' || arg === '-r') {\n options.registry = argv[++i];\n } else if (arg === '--pm') {\n options.pm!.push(argv[++i]);\n } else if (arg === '--test' || arg === '-t') {\n options.test!.push(argv[++i]);\n } else if (arg === '--concurrency' || arg === '-c') {\n options.concurrency = parseInt(argv[++i], 10);\n } else if (arg === '--timeout') {\n options.timeout = parseInt(argv[++i], 10);\n } else if (arg === '--token') {\n options.token = argv[++i];\n } else if (arg === '--verbose' || arg === '-v') {\n options.verbose = true;\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n } else if (arg.startsWith('--registry=')) {\n options.registry = arg.split('=')[1];\n } else if (arg.startsWith('--pm=')) {\n options.pm!.push(arg.split('=')[1]);\n } else if (arg.startsWith('--test=')) {\n options.test!.push(arg.split('=')[1]);\n } else if (arg.startsWith('--token=')) {\n options.token = arg.split('=')[1];\n } else if (arg.startsWith('--timeout=')) {\n options.timeout = parseInt(arg.split('=')[1], 10);\n } else {\n console.error(`Unknown argument: ${arg}`);\n printHelp();\n process.exit(1);\n }\n }\n\n return options;\n}\n\nfunction printHelp(): void {\n console.log(`\n @verdaccio/e2e-cli - Run Verdaccio e2e tests against any running registry\n\n Usage:\n verdaccio-e2e --registry <url> [options]\n\n Required:\n -r, --registry <url> Verdaccio registry URL (e.g. http://localhost:4873)\n\n Options:\n --pm <name[@version]> Package manager to test (can be repeated)\n Supported: npm, pnpm, yarn-classic, yarn-modern (or yarn), bun, deno\n Examples: --pm npm@10 --pm pnpm@9\n --pm yarn-modern@4\n --pm yarn-modern@3\n --pm yarn-classic\n --pm bun\n --pm npm --pm pnpm (uses system version)\n Auto-installs the requested yarn version if needed\n Default: npm\n\n -t, --test <name> Filter tests by name (can be repeated)\n Available: publish, install, ci, audit, info, deprecate,\n dist-tags, ping, search, star, unpublish\n Scenarios: scenario:install-multiple-deps\n Default: all supported by the PM\n\n --token <token> Auth token (skips user creation)\n --timeout <ms> Per-test timeout (default: 50000)\n -v, --verbose Enable debug output\n -h, --help Show this help\n `);\n}\n\nexport async function main(argv: string[] = process.argv): Promise<void> {\n const options = parseArgs(argv);\n\n if (!options.registry) {\n console.error('Error: --registry is required\\n');\n printHelp();\n process.exit(1);\n }\n\n // Run from a temp dir to isolate from the host project's packageManager, .yarnrc.yml, etc.\n const { createTempFolder } = await import('./utils/project');\n const runDir = await createTempFolder('e2e-run');\n process.chdir(runDir);\n\n if (options.verbose) {\n const { setVerbose } = await import('./utils/process');\n setVerbose(true);\n }\n\n // Ensure registry is reachable\n console.log(`Checking registry at ${options.registry}...`);\n const alive = await pingRegistry(options.registry);\n if (!alive) {\n console.error(`Error: Registry at ${options.registry} is not reachable`);\n process.exit(1);\n }\n console.log(`Registry is alive.`);\n\n // Get auth token\n let token = options.token;\n if (!token) {\n console.log('Creating test user...');\n const auth = await createUser(options.registry);\n token = auth.token;\n console.log(`User \"${auth.user}\" created.`);\n }\n\n // Build adapter list\n const adapters = parseAdapters(options.pm);\n console.log(`Adapters: ${adapters.map((a) => a.name).join(', ')}`);\n\n // Filter tests\n const tests =\n options.test && options.test.length > 0\n ? allTests.filter((t) => options.test!.includes(t.name))\n : allTests;\n\n console.log(`Tests: ${tests.map((t) => t.name).join(', ')}`);\n\n // Run\n const { exitCode } = await runAll(adapters, tests, options.registry, token, {\n timeout: options.timeout,\n concurrency: options.concurrency,\n testFilter: options.test,\n });\n\n process.exit(exitCode);\n}\n\n// Re-export for programmatic usage\nexport { allTests } from './tests';\nexport { allScenarios } from './scenarios';\nexport {\n createBunAdapter,\n createDenoAdapter,\n createNpmAdapter,\n createPnpmAdapter,\n createYarnClassicAdapter,\n createYarnModernAdapter,\n} from './adapters';\nexport { runAll, runSuite } from './runner';\nexport type { PackageManagerAdapter, TestDefinition, TestContext, CliOptions } from './types';\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.js","names":[],"sources":["../../src/reporter.ts"],"sourcesContent":["import { appendFileSync } from 'fs';\n\nimport { SuiteResult, TestResult } from './types';\n\nconst COLORS = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n};\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function reportTestStart(adapterName: string, testName: string): void {\n process.stdout.write(\n ` ${COLORS.dim}running${COLORS.reset} ${adapterName} > ${testName}...\\n`\n );\n}\n\nexport function reportTestResult(result: TestResult, hasSubTests = false): void {\n if (result.passed) {\n if (!hasSubTests) {\n process.stdout.write(\n ` ${COLORS.green}✓${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n } else {\n process.stdout.write(\n ` ${COLORS.green}PASS${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n }\n } else {\n process.stdout.write(\n ` ${COLORS.red}FAIL${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n if (result.error) {\n const lines = result.error.split('\\n');\n for (const line of lines) {\n console.log(` ${COLORS.red}${line}${COLORS.reset}`);\n }\n // GitHub Actions annotation\n if (process.env.GITHUB_ACTIONS) {\n const msg = result.error.split('\\n')[0];\n console.log(`::error title=E2E ${result.name}::${msg}`);\n }\n }\n }\n}\n\nexport function reportSuiteStart(adapterName: string): void {\n console.log(`\\n${COLORS.bold}${COLORS.cyan}${adapterName}${COLORS.reset}`);\n}\n\nexport function reportSubTestResult(label: string, passed: boolean, duration?: number): void {\n const icon = passed\n ? `${COLORS.bold}${COLORS.green}✓${COLORS.reset}`\n : `${COLORS.bold}${COLORS.red}✗${COLORS.reset}`;\n const dur = duration !== undefined ? ` ${COLORS.dim}${formatDuration(duration)}${COLORS.reset}` : '';\n process.stdout.write(`\\n ${icon} ${COLORS.bold}${label}${COLORS.reset}${dur}\\n\\n`);\n}\n\nexport function reportSkipped(testName: string): void {\n console.log(` ${COLORS.yellow}SKIP${COLORS.reset} ${testName}`);\n}\n\nexport function reportSummary(results: SuiteResult[]): void {\n console.log(`\\n${COLORS.bold}${'='.repeat(50)}${COLORS.reset}`);\n console.log(`${COLORS.bold}Summary${COLORS.reset}\\n`);\n\n let totalPassed = 0;\n let totalFailed = 0;\n let totalSkipped = 0;\n let totalDuration = 0;\n\n for (const suite of results) {\n const status = suite.failed > 0 ? COLORS.red : COLORS.green;\n const icon = suite.failed > 0 ? 'x' : 'v';\n console.log(\n ` ${status}${icon}${COLORS.reset} ${suite.adapter}: ` +\n `${COLORS.green}${suite.passed} passed${COLORS.reset}, ` +\n `${COLORS.red}${suite.failed} failed${COLORS.reset}, ` +\n `${COLORS.yellow}${suite.skipped} skipped${COLORS.reset} ` +\n `${COLORS.dim}(${formatDuration(suite.duration)})${COLORS.reset}`\n );\n totalPassed += suite.passed;\n totalFailed += suite.failed;\n totalSkipped += suite.skipped;\n totalDuration += suite.duration;\n }\n\n console.log(`\\n Total: ${COLORS.green}${totalPassed} passed${COLORS.reset}, ` +\n `${COLORS.red}${totalFailed} failed${COLORS.reset}, ` +\n `${COLORS.yellow}${totalSkipped} skipped${COLORS.reset} ` +\n `${COLORS.dim}(${formatDuration(totalDuration)})${COLORS.reset}`);\n console.log(`${COLORS.bold}${'='.repeat(50)}${COLORS.reset}\\n`);\n\n // GitHub Actions Job Summary\n const summaryFile = process.env.GITHUB_STEP_SUMMARY;\n if (summaryFile) {\n writeGitHubSummary(summaryFile, results);\n }\n}\n\nfunction writeGitHubSummary(summaryFile: string, results: SuiteResult[]): void {\n const lines: string[] = [];\n\n lines.push('## E2E Test Results\\n');\n\n for (const suite of results) {\n const status = suite.failed > 0 ? '❌' : '✅';\n lines.push(`### ${status} ${suite.adapter}\\n`);\n lines.push('| Test | Status | Duration |');\n lines.push('|------|--------|----------|');\n\n for (const test of suite.tests) {\n const icon = test.passed ? '✅' : '❌';\n const dur = formatDuration(test.duration);\n\n if (test.subTests && test.subTests.length > 0) {\n // Parent row\n lines.push(`| **${test.name}** | ${icon} | ${dur} |`);\n // Sub-test rows\n for (const sub of test.subTests) {\n const subIcon = sub.passed ? '✅' : '❌';\n const subDur = formatDuration(sub.duration);\n const label = sub.passed ? sub.label : `${sub.label}: ${sub.error?.split('\\n')[0] ?? ''}`;\n lines.push(`| &nbsp;&nbsp;&nbsp;&nbsp;${label} | ${subIcon} | ${subDur} |`);\n }\n } else {\n const label = test.passed ? test.name : `${test.name}: ${test.error?.split('\\n')[0] ?? ''}`;\n lines.push(`| ${label} | ${icon} | ${dur} |`);\n }\n }\n\n lines.push('');\n }\n\n let totalPassed = 0;\n let totalFailed = 0;\n let totalSkipped = 0;\n for (const suite of results) {\n totalPassed += suite.passed;\n totalFailed += suite.failed;\n totalSkipped += suite.skipped;\n }\n\n const emoji = totalFailed > 0 ? '❌' : '✅';\n lines.push(`${emoji} **${totalPassed} passed**, **${totalFailed} failed**, **${totalSkipped} skipped**\\n`);\n\n appendFileSync(summaryFile, lines.join('\\n'));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reporter.js","names":[],"sources":["../../src/reporter.ts"],"sourcesContent":["import { appendFileSync } from 'fs';\n\nimport { SuiteResult, TestResult } from './types';\n\nconst COLORS = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n};\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function reportTestStart(adapterName: string, testName: string): void {\n process.stdout.write(` ${COLORS.dim}running${COLORS.reset} ${adapterName} > ${testName}...\\n`);\n}\n\nexport function reportTestResult(result: TestResult, hasSubTests = false): void {\n if (result.passed) {\n if (!hasSubTests) {\n process.stdout.write(\n ` ${COLORS.green}✓${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n } else {\n process.stdout.write(\n ` ${COLORS.green}PASS${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n }\n } else {\n process.stdout.write(\n ` ${COLORS.red}FAIL${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\\n`\n );\n if (result.error) {\n const lines = result.error.split('\\n');\n for (const line of lines) {\n console.log(` ${COLORS.red}${line}${COLORS.reset}`);\n }\n // GitHub Actions annotation\n if (process.env.GITHUB_ACTIONS) {\n const msg = result.error.split('\\n')[0];\n console.log(`::error title=E2E ${result.name}::${msg}`);\n }\n }\n }\n}\n\nexport function reportSuiteStart(adapterName: string): void {\n console.log(`\\n${COLORS.bold}${COLORS.cyan}${adapterName}${COLORS.reset}`);\n}\n\nexport function reportSubTestResult(label: string, passed: boolean, duration?: number): void {\n const icon = passed\n ? `${COLORS.bold}${COLORS.green}✓${COLORS.reset}`\n : `${COLORS.bold}${COLORS.red}✗${COLORS.reset}`;\n const dur =\n duration !== undefined ? ` ${COLORS.dim}${formatDuration(duration)}${COLORS.reset}` : '';\n process.stdout.write(`\\n ${icon} ${COLORS.bold}${label}${COLORS.reset}${dur}\\n\\n`);\n}\n\nexport function reportSkipped(testName: string): void {\n console.log(` ${COLORS.yellow}SKIP${COLORS.reset} ${testName}`);\n}\n\nexport function reportSummary(results: SuiteResult[]): void {\n console.log(`\\n${COLORS.bold}${'='.repeat(50)}${COLORS.reset}`);\n console.log(`${COLORS.bold}Summary${COLORS.reset}\\n`);\n\n let totalPassed = 0;\n let totalFailed = 0;\n let totalSkipped = 0;\n let totalDuration = 0;\n\n for (const suite of results) {\n const status = suite.failed > 0 ? COLORS.red : COLORS.green;\n const icon = suite.failed > 0 ? 'x' : 'v';\n console.log(\n ` ${status}${icon}${COLORS.reset} ${suite.adapter}: ` +\n `${COLORS.green}${suite.passed} passed${COLORS.reset}, ` +\n `${COLORS.red}${suite.failed} failed${COLORS.reset}, ` +\n `${COLORS.yellow}${suite.skipped} skipped${COLORS.reset} ` +\n `${COLORS.dim}(${formatDuration(suite.duration)})${COLORS.reset}`\n );\n totalPassed += suite.passed;\n totalFailed += suite.failed;\n totalSkipped += suite.skipped;\n totalDuration += suite.duration;\n }\n\n console.log(\n `\\n Total: ${COLORS.green}${totalPassed} passed${COLORS.reset}, ` +\n `${COLORS.red}${totalFailed} failed${COLORS.reset}, ` +\n `${COLORS.yellow}${totalSkipped} skipped${COLORS.reset} ` +\n `${COLORS.dim}(${formatDuration(totalDuration)})${COLORS.reset}`\n );\n console.log(`${COLORS.bold}${'='.repeat(50)}${COLORS.reset}\\n`);\n\n // GitHub Actions Job Summary\n const summaryFile = process.env.GITHUB_STEP_SUMMARY;\n if (summaryFile) {\n writeGitHubSummary(summaryFile, results);\n }\n}\n\nfunction writeGitHubSummary(summaryFile: string, results: SuiteResult[]): void {\n const lines: string[] = [];\n\n lines.push('## E2E Test Results\\n');\n\n for (const suite of results) {\n const status = suite.failed > 0 ? '❌' : '✅';\n lines.push(`### ${status} ${suite.adapter}\\n`);\n lines.push('| Test | Status | Duration |');\n lines.push('|------|--------|----------|');\n\n for (const test of suite.tests) {\n const icon = test.passed ? '✅' : '❌';\n const dur = formatDuration(test.duration);\n\n if (test.subTests && test.subTests.length > 0) {\n // Parent row\n lines.push(`| **${test.name}** | ${icon} | ${dur} |`);\n // Sub-test rows\n for (const sub of test.subTests) {\n const subIcon = sub.passed ? '✅' : '❌';\n const subDur = formatDuration(sub.duration);\n const label = sub.passed ? sub.label : `${sub.label}: ${sub.error?.split('\\n')[0] ?? ''}`;\n lines.push(`| &nbsp;&nbsp;&nbsp;&nbsp;${label} | ${subIcon} | ${subDur} |`);\n }\n } else {\n const label = test.passed ? test.name : `${test.name}: ${test.error?.split('\\n')[0] ?? ''}`;\n lines.push(`| ${label} | ${icon} | ${dur} |`);\n }\n }\n\n lines.push('');\n }\n\n let totalPassed = 0;\n let totalFailed = 0;\n let totalSkipped = 0;\n for (const suite of results) {\n totalPassed += suite.passed;\n totalFailed += suite.failed;\n totalSkipped += suite.skipped;\n }\n\n const emoji = totalFailed > 0 ? '❌' : '✅';\n lines.push(\n `${emoji} **${totalPassed} passed**, **${totalFailed} failed**, **${totalSkipped} skipped**\\n`\n );\n\n appendFileSync(summaryFile, lines.join('\\n'));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","names":[],"sources":["../../src/runner.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { URL } from 'url';\n\nimport { PackageManagerAdapter, SubTestResult, SuiteResult, TestContext, TestDefinition, TestResult } from './types';\nimport { reportSkipped, reportSubTestResult, reportSuiteStart, reportSummary, reportTestResult, reportTestStart } from './reporter';\n\nconst debug = buildDebug('verdaccio:e2e-cli:runner');\n\nfunction getPort(registryUrl: string): number {\n try {\n const url = new URL(registryUrl);\n return parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80);\n } catch {\n return 4873;\n }\n}\n\nasync function runSingleTest(\n adapter: PackageManagerAdapter,\n test: TestDefinition,\n registryUrl: string,\n token: string,\n timeout: number\n): Promise<TestResult & { hasSubTests: boolean }> {\n const port = getPort(registryUrl);\n let hasSubTests = false;\n const subResults: SubTestResult[] = [];\n const ctx: TestContext = {\n registryUrl,\n token,\n port,\n exec: adapter.exec.bind(adapter),\n adapter,\n runId: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`,\n async subTest(label: string, fn: () => Promise<void>): Promise<void> {\n hasSubTests = true;\n const subStart = Date.now();\n try {\n await fn();\n const duration = Date.now() - subStart;\n subResults.push({ label, passed: true, duration });\n reportSubTestResult(label, true, duration);\n } catch (err) {\n const duration = Date.now() - subStart;\n const error = err instanceof Error ? err.message : String(err);\n subResults.push({ label, passed: false, duration, error });\n reportSubTestResult(label, false, duration);\n throw err;\n }\n },\n };\n\n const start = Date.now();\n\n return new Promise<TestResult & { hasSubTests: boolean }>((resolve) => {\n const timer = setTimeout(() => {\n resolve({\n name: test.name,\n passed: false,\n duration: Date.now() - start,\n error: `Test timed out after ${timeout}ms`,\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n }, timeout);\n\n test\n .run(ctx)\n .then(() => {\n clearTimeout(timer);\n resolve({\n name: test.name,\n passed: true,\n duration: Date.now() - start,\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n })\n .catch((err) => {\n clearTimeout(timer);\n resolve({\n name: test.name,\n passed: false,\n duration: Date.now() - start,\n error: err instanceof Error ? err.message : String(err),\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n });\n });\n}\n\nexport async function runSuite(\n adapter: PackageManagerAdapter,\n tests: TestDefinition[],\n registryUrl: string,\n token: string,\n options: { timeout: number; testFilter?: string[] }\n): Promise<SuiteResult> {\n const suiteStart = Date.now();\n const results: TestResult[] = [];\n let skipped = 0;\n\n reportSuiteStart(adapter.name);\n\n for (const test of tests) {\n // Filter by test name if specified\n if (options.testFilter && options.testFilter.length > 0) {\n if (!options.testFilter.includes(test.name)) {\n continue;\n }\n }\n\n // Check if adapter supports required commands\n if (test.requires) {\n const unsupported = test.requires.filter((cmd) => !adapter.supports.has(cmd));\n if (unsupported.length > 0) {\n reportSkipped(test.name);\n skipped++;\n continue;\n }\n }\n\n reportTestStart(adapter.name, test.name);\n const result = await runSingleTest(adapter, test, registryUrl, token, options.timeout);\n results.push(result);\n reportTestResult(result, result.hasSubTests);\n }\n\n const passed = results.filter((r) => r.passed).length;\n const failed = results.filter((r) => !r.passed).length;\n\n return {\n adapter: adapter.name,\n tests: results,\n passed,\n failed,\n skipped,\n duration: Date.now() - suiteStart,\n };\n}\n\nexport async function runAll(\n adapters: PackageManagerAdapter[],\n tests: TestDefinition[],\n registryUrl: string,\n token: string,\n options: { timeout: number; concurrency: number; testFilter?: string[] }\n): Promise<{ results: SuiteResult[]; exitCode: number }> {\n debug('running %d adapters with %d tests', adapters.length, tests.length);\n const results: SuiteResult[] = [];\n\n // Run adapters sequentially (each suite is already complex enough)\n for (const adapter of adapters) {\n const result = await runSuite(adapter, tests, registryUrl, token, options);\n results.push(result);\n }\n\n reportSummary(results);\n\n const totalFailed = results.reduce((sum, r) => sum + r.failed, 0);\n return { results, exitCode: totalFailed > 0 ? 1 : 0 };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runner.js","names":[],"sources":["../../src/runner.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { URL } from 'url';\n\nimport {\n reportSkipped,\n reportSubTestResult,\n reportSuiteStart,\n reportSummary,\n reportTestResult,\n reportTestStart,\n} from './reporter';\nimport {\n PackageManagerAdapter,\n SubTestResult,\n SuiteResult,\n TestContext,\n TestDefinition,\n TestResult,\n} from './types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:runner');\n\nfunction getPort(registryUrl: string): number {\n try {\n const url = new URL(registryUrl);\n return parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80);\n } catch {\n return 4873;\n }\n}\n\nasync function runSingleTest(\n adapter: PackageManagerAdapter,\n test: TestDefinition,\n registryUrl: string,\n token: string,\n timeout: number\n): Promise<TestResult & { hasSubTests: boolean }> {\n const port = getPort(registryUrl);\n let hasSubTests = false;\n const subResults: SubTestResult[] = [];\n const ctx: TestContext = {\n registryUrl,\n token,\n port,\n exec: adapter.exec.bind(adapter),\n adapter,\n runId: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`,\n async subTest(label: string, fn: () => Promise<void>): Promise<void> {\n hasSubTests = true;\n const subStart = Date.now();\n try {\n await fn();\n const duration = Date.now() - subStart;\n subResults.push({ label, passed: true, duration });\n reportSubTestResult(label, true, duration);\n } catch (err) {\n const duration = Date.now() - subStart;\n const error = err instanceof Error ? err.message : String(err);\n subResults.push({ label, passed: false, duration, error });\n reportSubTestResult(label, false, duration);\n throw err;\n }\n },\n };\n\n const start = Date.now();\n\n return new Promise<TestResult & { hasSubTests: boolean }>((resolve) => {\n const timer = setTimeout(() => {\n resolve({\n name: test.name,\n passed: false,\n duration: Date.now() - start,\n error: `Test timed out after ${timeout}ms`,\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n }, timeout);\n\n test\n .run(ctx)\n .then(() => {\n clearTimeout(timer);\n resolve({\n name: test.name,\n passed: true,\n duration: Date.now() - start,\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n })\n .catch((err) => {\n clearTimeout(timer);\n resolve({\n name: test.name,\n passed: false,\n duration: Date.now() - start,\n error: err instanceof Error ? err.message : String(err),\n hasSubTests,\n subTests: subResults.length > 0 ? subResults : undefined,\n });\n });\n });\n}\n\nexport async function runSuite(\n adapter: PackageManagerAdapter,\n tests: TestDefinition[],\n registryUrl: string,\n token: string,\n options: { timeout: number; testFilter?: string[] }\n): Promise<SuiteResult> {\n const suiteStart = Date.now();\n const results: TestResult[] = [];\n let skipped = 0;\n\n reportSuiteStart(adapter.name);\n\n for (const test of tests) {\n // Filter by test name if specified\n if (options.testFilter && options.testFilter.length > 0) {\n if (!options.testFilter.includes(test.name)) {\n continue;\n }\n }\n\n // Check if adapter supports required commands\n if (test.requires) {\n const unsupported = test.requires.filter((cmd) => !adapter.supports.has(cmd));\n if (unsupported.length > 0) {\n reportSkipped(test.name);\n skipped++;\n continue;\n }\n }\n\n reportTestStart(adapter.name, test.name);\n const result = await runSingleTest(adapter, test, registryUrl, token, options.timeout);\n results.push(result);\n reportTestResult(result, result.hasSubTests);\n }\n\n const passed = results.filter((r) => r.passed).length;\n const failed = results.filter((r) => !r.passed).length;\n\n return {\n adapter: adapter.name,\n tests: results,\n passed,\n failed,\n skipped,\n duration: Date.now() - suiteStart,\n };\n}\n\nexport async function runAll(\n adapters: PackageManagerAdapter[],\n tests: TestDefinition[],\n registryUrl: string,\n token: string,\n options: { timeout: number; concurrency: number; testFilter?: string[] }\n): Promise<{ results: SuiteResult[]; exitCode: number }> {\n debug('running %d adapters with %d tests', adapters.length, tests.length);\n const results: SuiteResult[] = [];\n\n // Run adapters sequentially (each suite is already complex enough)\n for (const adapter of adapters) {\n const result = await runSuite(adapter, tests, registryUrl, token, options);\n results.push(result);\n }\n\n reportSummary(results);\n\n const totalFailed = results.reduce((sum, r) => sum + r.failed, 0);\n return { results, exitCode: totalFailed > 0 ? 1 : 0 };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/scenarios/index.ts"],"sourcesContent":["import { TestDefinition } from '../types';\n\nimport { installMultipleDepsScenario } from './install-multiple-deps';\n\nexport const allScenarios: TestDefinition[] = [installMultipleDepsScenario];\n\nexport { installMultipleDepsScenario };\n"],"mappings":";;AAIA,IAAa,eAAiC,CAAC,4BAA4B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/scenarios/index.ts"],"sourcesContent":["import { TestDefinition } from '../types';\nimport { installMultipleDepsScenario } from './install-multiple-deps';\n\nexport const allScenarios: TestDefinition[] = [installMultipleDepsScenario];\n\nexport { installMultipleDepsScenario };\n"],"mappings":";;AAGA,IAAa,eAAiC,CAAC,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"install-multiple-deps.js","names":[],"sources":["../../../src/scenarios/install-multiple-deps.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:scenario:install-multiple-deps');\n\n/**\n * Scenario: install-multiple-deps\n *\n * Publishes several packages to the registry (some scoped, some unscoped,\n * some with inter-dependencies), then creates a consumer project that\n * depends on all of them and runs `install`.\n *\n * This exercises the registry under realistic load:\n * - many parallel tarball downloads\n * - metadata resolution for multiple packages\n * - scoped-package authentication\n * - transitive dependency resolution\n */\n\nconst SEED_PACKAGES_COUNT = 5;\n\n/**\n * Parse `info --json` output across all adapters.\n * Yarn classic wraps the result in NDJSON lines with `{ type: \"inspect\", data: { ... } }`.\n */\nfunction parseInfoOutput(stdout: string, adapterType: string): any {\n if (adapterType === 'yarn-classic') {\n // Yarn classic outputs NDJSON — find the \"inspect\" line that holds package data\n const lines = stdout.split('\\n').filter(Boolean);\n for (const line of lines) {\n try {\n const obj = JSON.parse(line);\n if (obj.type === 'inspect' && obj.data) {\n return obj.data;\n }\n } catch {\n // skip non-JSON lines\n }\n }\n // Fallback: try parsing the whole output\n return JSON.parse(stdout);\n }\n return JSON.parse(stdout);\n}\n\nasync function publishSeedPackage(\n ctx: TestContext,\n pkgName: string,\n version: string,\n dependencies: Record<string, string> = {}\n): Promise<void> {\n debug('publishing seed package %s@%s', pkgName, version);\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n version,\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n dependencies\n );\n\n if (ctx.adapter.type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published %s@%s', pkgName, version);\n}\n\nasync function testInstallMultipleDeps(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Shared project folder used as cwd for info commands across subtests.\n // Yarn modern requires a project context (package.json + .yarnrc.yml) for `yarn npm info`.\n let projectCwd = '';\n\n // --- Phase 1: Publish a tree of seed packages ---\n await ctx.subTest('publish seed packages', async () => {\n // Leaf packages (no dependencies of their own)\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n const name = `@verdaccio/seed-${id}-${i}`;\n await publishSeedPackage(ctx, name, '1.0.0');\n }\n\n // A \"shared\" package that multiple consumers will pull in\n const sharedPkg = `@verdaccio/seed-shared-${id}`;\n await publishSeedPackage(ctx, sharedPkg, '1.0.0');\n\n // An intermediate package that depends on some leaf packages + shared\n const intermediatePkg = `@verdaccio/seed-mid-${id}`;\n await publishSeedPackage(ctx, intermediatePkg, '1.0.0', {\n [`@verdaccio/seed-${id}-1`]: '1.0.0',\n [`@verdaccio/seed-${id}-2`]: '1.0.0',\n [sharedPkg]: '1.0.0',\n });\n\n debug('all seed packages published');\n });\n\n // --- Phase 2: Create a consumer project that depends on everything ---\n await ctx.subTest('install all dependencies in consumer project', async () => {\n const consumerDeps: Record<string, string> = {};\n\n // Direct deps on all leaf packages\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n consumerDeps[`@verdaccio/seed-${id}-${i}`] = '1.0.0';\n }\n\n // Also depend on the intermediate (pulls in transitive deps)\n consumerDeps[`@verdaccio/seed-mid-${id}`] = '1.0.0';\n\n // And the shared package directly (already a transitive dep of mid)\n consumerDeps[`@verdaccio/seed-shared-${id}`] = '1.0.0';\n\n const consumerName = `@verdaccio/consumer-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n consumerName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n consumerDeps\n );\n\n projectCwd = tempFolder;\n\n debug('installing %d dependencies in consumer project', Object.keys(consumerDeps).length);\n\n const args = ['install', ...ctx.adapter.registryArg(ctx.registryUrl)];\n await ctx.adapter.exec({ cwd: tempFolder }, ...args);\n\n debug('install completed for consumer project');\n });\n\n // --- Phase 3: Verify all packages resolved correctly ---\n await ctx.subTest('verify installed packages', async () => {\n // Check that each seed package is retrievable via `info`\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n const name = `@verdaccio/seed-${id}-${i}`;\n const resp = await ctx.adapter.exec(\n { cwd: projectCwd },\n 'info',\n name,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const info = parseInfoOutput(resp.stdout, ctx.adapter.type);\n assert.strictEqual(info.name, name, `Expected package name \"${name}\"`);\n assert.strictEqual(info.version, '1.0.0', `Expected version 1.0.0 for ${name}`);\n }\n\n // Verify intermediate package has correct dependencies in registry metadata\n const midName = `@verdaccio/seed-mid-${id}`;\n const resp = await ctx.adapter.exec(\n { cwd: projectCwd },\n 'info',\n midName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const midInfo = parseInfoOutput(resp.stdout, ctx.adapter.type);\n assert.strictEqual(midInfo.name, midName);\n\n const deps = midInfo.dependencies || {};\n assert.strictEqual(\n deps[`@verdaccio/seed-${id}-1`],\n '1.0.0',\n 'Intermediate should depend on seed-1'\n );\n assert.strictEqual(\n deps[`@verdaccio/seed-${id}-2`],\n '1.0.0',\n 'Intermediate should depend on seed-2'\n );\n assert.strictEqual(\n deps[`@verdaccio/seed-shared-${id}`],\n '1.0.0',\n 'Intermediate should depend on shared'\n );\n\n debug('all packages verified');\n });\n\n // --- Phase 4: Publish updated versions and re-install ---\n await ctx.subTest('update and re-install', async () => {\n // Publish v2 of a leaf and the shared package\n const leafName = `@verdaccio/seed-${id}-1`;\n await publishSeedPackage(ctx, leafName, '2.0.0');\n\n const sharedName = `@verdaccio/seed-shared-${id}`;\n await publishSeedPackage(ctx, sharedName, '2.0.0');\n\n // Create a new consumer that uses ^ ranges — should pick up 2.0.0\n const consumerDeps: Record<string, string> = {\n [leafName]: '^1.0.0',\n [sharedName]: '^1.0.0',\n };\n\n const consumerName = `@verdaccio/consumer-update-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n consumerName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n consumerDeps\n );\n\n await ctx.adapter.exec({ cwd: tempFolder }, 'install', ...ctx.adapter.registryArg(ctx.registryUrl));\n\n // Verify that the registry now serves both versions\n const leafResp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n leafName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const leafInfo = parseInfoOutput(leafResp.stdout, ctx.adapter.type);\n\n // dist-tags.latest should be 2.0.0\n assert.strictEqual(\n leafInfo['dist-tags']?.latest ?? leafInfo.version,\n '2.0.0',\n `Expected latest version of ${leafName} to be 2.0.0`\n );\n\n debug('update and re-install verified');\n });\n}\n\nexport const installMultipleDepsScenario: TestDefinition = {\n name: 'scenario:install-multiple-deps',\n requires: ['publish', 'install', 'info'],\n run: testInstallMultipleDeps,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"install-multiple-deps.js","names":[],"sources":["../../../src/scenarios/install-multiple-deps.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:scenario:install-multiple-deps');\n\n/**\n * Scenario: install-multiple-deps\n *\n * Publishes several packages to the registry (some scoped, some unscoped,\n * some with inter-dependencies), then creates a consumer project that\n * depends on all of them and runs `install`.\n *\n * This exercises the registry under realistic load:\n * - many parallel tarball downloads\n * - metadata resolution for multiple packages\n * - scoped-package authentication\n * - transitive dependency resolution\n */\n\nconst SEED_PACKAGES_COUNT = 5;\n\n/**\n * Parse `info --json` output across all adapters.\n * Yarn classic wraps the result in NDJSON lines with `{ type: \"inspect\", data: { ... } }`.\n */\nfunction parseInfoOutput(stdout: string, adapterType: string): any {\n if (adapterType === 'yarn-classic') {\n // Yarn classic outputs NDJSON — find the \"inspect\" line that holds package data\n const lines = stdout.split('\\n').filter(Boolean);\n for (const line of lines) {\n try {\n const obj = JSON.parse(line);\n if (obj.type === 'inspect' && obj.data) {\n return obj.data;\n }\n } catch {\n // skip non-JSON lines\n }\n }\n // Fallback: try parsing the whole output\n return JSON.parse(stdout);\n }\n return JSON.parse(stdout);\n}\n\nasync function publishSeedPackage(\n ctx: TestContext,\n pkgName: string,\n version: string,\n dependencies: Record<string, string> = {}\n): Promise<void> {\n debug('publishing seed package %s@%s', pkgName, version);\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n version,\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n dependencies\n );\n\n if (ctx.adapter.type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published %s@%s', pkgName, version);\n}\n\nasync function testInstallMultipleDeps(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Shared project folder used as cwd for info commands across subtests.\n // Yarn modern requires a project context (package.json + .yarnrc.yml) for `yarn npm info`.\n let projectCwd = '';\n\n // --- Phase 1: Publish a tree of seed packages ---\n await ctx.subTest('publish seed packages', async () => {\n // Leaf packages (no dependencies of their own)\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n const name = `@verdaccio/seed-${id}-${i}`;\n await publishSeedPackage(ctx, name, '1.0.0');\n }\n\n // A \"shared\" package that multiple consumers will pull in\n const sharedPkg = `@verdaccio/seed-shared-${id}`;\n await publishSeedPackage(ctx, sharedPkg, '1.0.0');\n\n // An intermediate package that depends on some leaf packages + shared\n const intermediatePkg = `@verdaccio/seed-mid-${id}`;\n await publishSeedPackage(ctx, intermediatePkg, '1.0.0', {\n [`@verdaccio/seed-${id}-1`]: '1.0.0',\n [`@verdaccio/seed-${id}-2`]: '1.0.0',\n [sharedPkg]: '1.0.0',\n });\n\n debug('all seed packages published');\n });\n\n // --- Phase 2: Create a consumer project that depends on everything ---\n await ctx.subTest('install all dependencies in consumer project', async () => {\n const consumerDeps: Record<string, string> = {};\n\n // Direct deps on all leaf packages\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n consumerDeps[`@verdaccio/seed-${id}-${i}`] = '1.0.0';\n }\n\n // Also depend on the intermediate (pulls in transitive deps)\n consumerDeps[`@verdaccio/seed-mid-${id}`] = '1.0.0';\n\n // And the shared package directly (already a transitive dep of mid)\n consumerDeps[`@verdaccio/seed-shared-${id}`] = '1.0.0';\n\n const consumerName = `@verdaccio/consumer-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n consumerName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n consumerDeps\n );\n\n projectCwd = tempFolder;\n\n debug('installing %d dependencies in consumer project', Object.keys(consumerDeps).length);\n\n const args = ['install', ...ctx.adapter.registryArg(ctx.registryUrl)];\n await ctx.adapter.exec({ cwd: tempFolder }, ...args);\n\n debug('install completed for consumer project');\n });\n\n // --- Phase 3: Verify all packages resolved correctly ---\n await ctx.subTest('verify installed packages', async () => {\n // Check that each seed package is retrievable via `info`\n for (let i = 1; i <= SEED_PACKAGES_COUNT; i++) {\n const name = `@verdaccio/seed-${id}-${i}`;\n const resp = await ctx.adapter.exec(\n { cwd: projectCwd },\n 'info',\n name,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const info = parseInfoOutput(resp.stdout, ctx.adapter.type);\n assert.strictEqual(info.name, name, `Expected package name \"${name}\"`);\n assert.strictEqual(info.version, '1.0.0', `Expected version 1.0.0 for ${name}`);\n }\n\n // Verify intermediate package has correct dependencies in registry metadata\n const midName = `@verdaccio/seed-mid-${id}`;\n const resp = await ctx.adapter.exec(\n { cwd: projectCwd },\n 'info',\n midName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const midInfo = parseInfoOutput(resp.stdout, ctx.adapter.type);\n assert.strictEqual(midInfo.name, midName);\n\n const deps = midInfo.dependencies || {};\n assert.strictEqual(\n deps[`@verdaccio/seed-${id}-1`],\n '1.0.0',\n 'Intermediate should depend on seed-1'\n );\n assert.strictEqual(\n deps[`@verdaccio/seed-${id}-2`],\n '1.0.0',\n 'Intermediate should depend on seed-2'\n );\n assert.strictEqual(\n deps[`@verdaccio/seed-shared-${id}`],\n '1.0.0',\n 'Intermediate should depend on shared'\n );\n\n debug('all packages verified');\n });\n\n // --- Phase 4: Publish updated versions and re-install ---\n await ctx.subTest('update and re-install', async () => {\n // Publish v2 of a leaf and the shared package\n const leafName = `@verdaccio/seed-${id}-1`;\n await publishSeedPackage(ctx, leafName, '2.0.0');\n\n const sharedName = `@verdaccio/seed-shared-${id}`;\n await publishSeedPackage(ctx, sharedName, '2.0.0');\n\n // Create a new consumer that uses ^ ranges — should pick up 2.0.0\n const consumerDeps: Record<string, string> = {\n [leafName]: '^1.0.0',\n [sharedName]: '^1.0.0',\n };\n\n const consumerName = `@verdaccio/consumer-update-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n consumerName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n consumerDeps\n );\n\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n // Verify that the registry now serves both versions\n const leafResp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n leafName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const leafInfo = parseInfoOutput(leafResp.stdout, ctx.adapter.type);\n\n // dist-tags.latest should be 2.0.0\n assert.strictEqual(\n leafInfo['dist-tags']?.latest ?? leafInfo.version,\n '2.0.0',\n `Expected latest version of ${leafName} to be 2.0.0`\n );\n\n debug('update and re-install verified');\n });\n}\n\nexport const installMultipleDepsScenario: TestDefinition = {\n name: 'scenario:install-multiple-deps',\n requires: ['publish', 'install', 'info'],\n run: testInstallMultipleDeps,\n};\n"],"mappings":"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"}
@@ -3,8 +3,8 @@ import assert from "assert";
3
3
  //#region src/tests/audit.ts
4
4
  var debug = buildDebug("verdaccio:e2e-cli:test:audit");
5
5
  async function testAudit(ctx) {
6
- if (ctx.adapter.type !== "npm") {
7
- debug("skipping audit test for %s (npm only)", ctx.adapter.type);
6
+ if (ctx.adapter.type !== "npm" && ctx.adapter.type !== "bun") {
7
+ debug("skipping audit test for %s (npm/bun only)", ctx.adapter.type);
8
8
  return;
9
9
  }
10
10
  const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];
@@ -25,9 +25,12 @@ async function testAudit(ctx) {
25
25
  return;
26
26
  }
27
27
  const resp = await ctx.adapter.exec({ cwd: tempFolder }, "audit", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
28
- const parsedBody = JSON.parse(resp.stdout);
29
- assert.ok(parsedBody.auditReportVersion !== void 0, "Expected \"auditReportVersion\" in audit response");
30
- assert.ok(parsedBody.vulnerabilities !== void 0, "Expected \"vulnerabilities\" in audit response");
28
+ if (ctx.adapter.type === "bun") assert.ok(resp.stdout.length > 0 || resp.stderr.length > 0, "Expected audit output");
29
+ else {
30
+ const parsedBody = JSON.parse(resp.stdout);
31
+ assert.ok(parsedBody.auditReportVersion !== void 0, "Expected \"auditReportVersion\" in audit response");
32
+ assert.ok(parsedBody.vulnerabilities !== void 0, "Expected \"vulnerabilities\" in audit response");
33
+ }
31
34
  }
32
35
  }
33
36
  var auditTest = {
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","names":[],"sources":["../../../src/tests/audit.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:audit');\n\nasync function testAudit(ctx: TestContext): Promise<void> {\n // Audit is only reliable with npm — pnpm/yarn audit output varies too much\n if (ctx.adapter.type !== 'npm') {\n debug('skipping audit test for %s (npm only)', ctx.adapter.type);\n return;\n }\n\n const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { jquery: '3.6.1' }\n );\n\n // install is required to create package lock file\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n // npm audit exits with code 1 both when vulnerabilities are found AND when\n // the endpoint is unavailable. We need to distinguish between the two.\n // Probe the audit endpoint first to avoid false failures on registries\n // that don't support it (e.g. verdaccio next-7+).\n try {\n const probe = await fetch(`${ctx.registryUrl}/-/npm/v1/security/audits/quick`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (probe.status === 404) {\n debug('audit endpoint not supported by this registry (404), skipping');\n return;\n }\n } catch {\n debug('audit endpoint probe failed, skipping');\n return;\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'audit',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n const parsedBody = JSON.parse(resp.stdout);\n assert.ok(parsedBody.auditReportVersion !== undefined, 'Expected \"auditReportVersion\" in audit response');\n assert.ok(parsedBody.vulnerabilities !== undefined, 'Expected \"vulnerabilities\" in audit response');\n }\n}\n\nexport const auditTest: TestDefinition = {\n name: 'audit',\n requires: ['audit'],\n run: testAudit,\n};\n"],"mappings":";;;AAKA,IAAM,QAAQ,WAAW,+BAA+B;AAExD,eAAe,UAAU,KAAiC;AAExD,KAAI,IAAI,QAAQ,SAAS,OAAO;AAC9B,QAAM,yCAAyC,IAAI,QAAQ,KAAK;AAChE;;CAGF,MAAM,WAAW,CAAC,mBAAmB,IAAI,SAAS,oBAAoB,IAAI,QAAQ;AAElF,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,EAAE,QAAQ,SAAS,CACpB;AAGD,QAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAMD,MAAI;AAMF,QALc,MAAM,MAAM,GAAG,IAAI,YAAY,kCAAkC;IAC7E,QAAQ;IACR,SAAS,EAAE,gBAAgB,oBAAoB;IAC/C,MAAM;IACP,CAAC,EACQ,WAAW,KAAK;AACxB,UAAM,gEAAgE;AACtE;;UAEI;AACN,SAAM,wCAAwC;AAC9C;;EAGF,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;EAED,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,SAAO,GAAG,WAAW,uBAAuB,KAAA,GAAW,oDAAkD;AACzG,SAAO,GAAG,WAAW,oBAAoB,KAAA,GAAW,iDAA+C;;;AAIvG,IAAa,YAA4B;CACvC,MAAM;CACN,UAAU,CAAC,QAAQ;CACnB,KAAK;CACN"}
1
+ {"version":3,"file":"audit.js","names":[],"sources":["../../../src/tests/audit.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:audit');\n\nasync function testAudit(ctx: TestContext): Promise<void> {\n // Audit is only reliable with npm and bun — pnpm/yarn audit output varies too much\n if (ctx.adapter.type !== 'npm' && ctx.adapter.type !== 'bun') {\n debug('skipping audit test for %s (npm/bun only)', ctx.adapter.type);\n return;\n }\n\n const packages = [`verdaccio-audit-${ctx.runId}`, `@verdaccio/audit-${ctx.runId}`];\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { jquery: '3.6.1' }\n );\n\n // install is required to create package lock file\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n // npm audit exits with code 1 both when vulnerabilities are found AND when\n // the endpoint is unavailable. We need to distinguish between the two.\n // Probe the audit endpoint first to avoid false failures on registries\n // that don't support it (e.g. verdaccio next-7+).\n try {\n const probe = await fetch(`${ctx.registryUrl}/-/npm/v1/security/audits/quick`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n });\n if (probe.status === 404) {\n debug('audit endpoint not supported by this registry (404), skipping');\n return;\n }\n } catch {\n debug('audit endpoint probe failed, skipping');\n return;\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'audit',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (ctx.adapter.type === 'bun') {\n // bun audit output may differ — exit code 0 is sufficient for basic validation\n assert.ok(resp.stdout.length > 0 || resp.stderr.length > 0, 'Expected audit output');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.ok(\n parsedBody.auditReportVersion !== undefined,\n 'Expected \"auditReportVersion\" in audit response'\n );\n assert.ok(\n parsedBody.vulnerabilities !== undefined,\n 'Expected \"vulnerabilities\" in audit response'\n );\n }\n }\n}\n\nexport const auditTest: TestDefinition = {\n name: 'audit',\n requires: ['audit'],\n run: testAudit,\n};\n"],"mappings":";;;AAKA,IAAM,QAAQ,WAAW,+BAA+B;AAExD,eAAe,UAAU,KAAiC;AAExD,KAAI,IAAI,QAAQ,SAAS,SAAS,IAAI,QAAQ,SAAS,OAAO;AAC5D,QAAM,6CAA6C,IAAI,QAAQ,KAAK;AACpE;;CAGF,MAAM,WAAW,CAAC,mBAAmB,IAAI,SAAS,oBAAoB,IAAI,QAAQ;AAElF,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,EAAE,QAAQ,SAAS,CACpB;AAGD,QAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAMD,MAAI;AAMF,QALc,MAAM,MAAM,GAAG,IAAI,YAAY,kCAAkC;IAC7E,QAAQ;IACR,SAAS,EAAE,gBAAgB,oBAAoB;IAC/C,MAAM;IACP,CAAC,EACQ,WAAW,KAAK;AACxB,UAAM,gEAAgE;AACtE;;UAEI;AACN,SAAM,wCAAwC;AAC9C;;EAGF,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,MAAI,IAAI,QAAQ,SAAS,MAEvB,QAAO,GAAG,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,GAAG,wBAAwB;OAC/E;GACL,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,UAAO,GACL,WAAW,uBAAuB,KAAA,GAClC,oDACD;AACD,UAAO,GACL,WAAW,oBAAoB,KAAA,GAC/B,iDACD;;;;AAKP,IAAa,YAA4B;CACvC,MAAM;CACN,UAAU,CAAC,QAAQ;CACnB,KAAK;CACN"}
@@ -32,10 +32,19 @@ function getCiArgs(ctx) {
32
32
  ...ctx.adapter.registryArg(ctx.registryUrl)
33
33
  ];
34
34
  case "yarn-modern": return ["install", "--immutable"];
35
+ case "bun": return [
36
+ "install",
37
+ "--frozen-lockfile",
38
+ ...ctx.adapter.registryArg(ctx.registryUrl)
39
+ ];
35
40
  default: return ["ci", ...ctx.adapter.registryArg(ctx.registryUrl)];
36
41
  }
37
42
  }
38
43
  async function testCi(ctx) {
44
+ if (ctx.adapter.type === "deno") {
45
+ debug("skipping ci test for deno (no ci command)");
46
+ return;
47
+ }
39
48
  const { tempFolder } = await ctx.adapter.prepareProject(`ci-test-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, { "is-odd": "1.0.0" });
40
49
  debug("running initial install to generate lockfile in %s", tempFolder);
41
50
  await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
@@ -1 +1 @@
1
- {"version":3,"file":"ci.js","names":[],"sources":["../../../src/tests/ci.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { rm } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:ci');\n\n/**\n * Test: ci\n *\n * Verifies that lockfile-based install works correctly.\n * Unlike `install`, CI commands skip metadata fetches and resolve\n * tarballs directly from the URLs stored in the lockfile.\n *\n * Flow:\n * 1. Prepare a project with dependencies\n * 2. Run `install` to generate the lockfile\n * 3. Remove node_modules\n * 4. Run the CI-equivalent command (npm ci / pnpm install --frozen-lockfile / yarn --frozen-lockfile / yarn --immutable)\n * 5. Verify it completes successfully\n */\n\nfunction getCiArgs(ctx: TestContext): string[] {\n switch (ctx.adapter.type) {\n case 'npm':\n // `npm ci` — separate command, reads package-lock.json\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'pnpm':\n // `pnpm install --frozen-lockfile` — same command, strict mode\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'yarn-classic':\n // `yarn install --frozen-lockfile`\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'yarn-modern':\n // `yarn install --immutable`\n return ['install', '--immutable'];\n default:\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n }\n}\n\nasync function testCi(ctx: TestContext): Promise<void> {\n const { tempFolder } = await ctx.adapter.prepareProject(\n `ci-test-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { 'is-odd': '1.0.0' }\n );\n\n debug('running initial install to generate lockfile in %s', tempFolder);\n\n // Step 1: Run install to generate the lockfile\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n debug('lockfile generated, removing node_modules');\n\n // Step 2: Remove node_modules to force a clean install from lockfile\n const nodeModulesPath = join(tempFolder, 'node_modules');\n await rm(nodeModulesPath, { recursive: true, force: true });\n\n // For pnpm, also clean the virtual store\n const pnpmStorePath = join(tempFolder, '.pnpm-store');\n await rm(pnpmStorePath, { recursive: true, force: true });\n\n debug('node_modules removed, running ci command');\n\n // Step 3: Run CI-equivalent command (tarball-only, no metadata fetches)\n const ciArgs = getCiArgs(ctx);\n debug('ci args: %o', ciArgs);\n const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...ciArgs);\n\n // Step 4: Verify success\n if (ctx.adapter.type === 'npm') {\n // npm ci with --json is not reliable, just check exit code 0\n // and verify output mentions added packages\n const output = resp.stdout + resp.stderr;\n debug('npm ci output: %s', output);\n assert.ok(true, 'npm ci completed successfully');\n } else {\n debug('ci completed successfully for %s', ctx.adapter.type);\n assert.ok(true, `CI install completed successfully for ${ctx.adapter.type}`);\n }\n}\n\nexport const ciTest: TestDefinition = {\n name: 'ci',\n requires: ['install'],\n run: testCi,\n};\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,4BAA4B;;;;;;;;;;;;;;;AAiBrD,SAAS,UAAU,KAA4B;AAC7C,SAAQ,IAAI,QAAQ,MAApB;EACE,KAAK,MAEH,QAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;EAC5D,KAAK,OAEH,QAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY;GAAC;EACtF,KAAK,eAEH,QAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY;GAAC;EACtF,KAAK,cAEH,QAAO,CAAC,WAAW,cAAc;EACnC,QACE,QAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;;;AAIhE,eAAe,OAAO,KAAiC;CACrD,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,WAAW,IAAI,SACf,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,EAAE,UAAU,SAAS,CACtB;AAED,OAAM,sDAAsD,WAAW;AAGvE,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,OAAM,4CAA4C;AAIlD,OAAM,GADkB,KAAK,YAAY,eAAe,EAC9B;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAI3D,OAAM,GADgB,KAAK,YAAY,cAAc,EAC7B;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAEzD,OAAM,2CAA2C;CAGjD,MAAM,SAAS,UAAU,IAAI;AAC7B,OAAM,eAAe,OAAO;CAC5B,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,YAAY,EAAE,GAAG,OAAO;AAGnE,KAAI,IAAI,QAAQ,SAAS,OAAO;AAI9B,QAAM,qBADS,KAAK,SAAS,KAAK,OACA;AAClC,SAAO,GAAG,MAAM,gCAAgC;QAC3C;AACL,QAAM,oCAAoC,IAAI,QAAQ,KAAK;AAC3D,SAAO,GAAG,MAAM,yCAAyC,IAAI,QAAQ,OAAO;;;AAIhF,IAAa,SAAyB;CACpC,MAAM;CACN,UAAU,CAAC,UAAU;CACrB,KAAK;CACN"}
1
+ {"version":3,"file":"ci.js","names":[],"sources":["../../../src/tests/ci.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { rm } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:ci');\n\n/**\n * Test: ci\n *\n * Verifies that lockfile-based install works correctly.\n * Unlike `install`, CI commands skip metadata fetches and resolve\n * tarballs directly from the URLs stored in the lockfile.\n *\n * Flow:\n * 1. Prepare a project with dependencies\n * 2. Run `install` to generate the lockfile\n * 3. Remove node_modules\n * 4. Run the CI-equivalent command (npm ci / pnpm install --frozen-lockfile / yarn --frozen-lockfile / yarn --immutable)\n * 5. Verify it completes successfully\n */\n\nfunction getCiArgs(ctx: TestContext): string[] {\n switch (ctx.adapter.type) {\n case 'npm':\n // `npm ci` — separate command, reads package-lock.json\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'pnpm':\n // `pnpm install --frozen-lockfile` — same command, strict mode\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'yarn-classic':\n // `yarn install --frozen-lockfile`\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n case 'yarn-modern':\n // `yarn install --immutable`\n return ['install', '--immutable'];\n case 'bun':\n // `bun install --frozen-lockfile`\n return ['install', '--frozen-lockfile', ...ctx.adapter.registryArg(ctx.registryUrl)];\n default:\n return ['ci', ...ctx.adapter.registryArg(ctx.registryUrl)];\n }\n}\n\nasync function testCi(ctx: TestContext): Promise<void> {\n // deno doesn't have a CI/frozen-lockfile equivalent\n if (ctx.adapter.type === 'deno') {\n debug('skipping ci test for deno (no ci command)');\n return;\n }\n\n const { tempFolder } = await ctx.adapter.prepareProject(\n `ci-test-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n { 'is-odd': '1.0.0' }\n );\n\n debug('running initial install to generate lockfile in %s', tempFolder);\n\n // Step 1: Run install to generate the lockfile\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n debug('lockfile generated, removing node_modules');\n\n // Step 2: Remove node_modules to force a clean install from lockfile\n const nodeModulesPath = join(tempFolder, 'node_modules');\n await rm(nodeModulesPath, { recursive: true, force: true });\n\n // For pnpm, also clean the virtual store\n const pnpmStorePath = join(tempFolder, '.pnpm-store');\n await rm(pnpmStorePath, { recursive: true, force: true });\n\n debug('node_modules removed, running ci command');\n\n // Step 3: Run CI-equivalent command (tarball-only, no metadata fetches)\n const ciArgs = getCiArgs(ctx);\n debug('ci args: %o', ciArgs);\n const resp = await ctx.adapter.exec({ cwd: tempFolder }, ...ciArgs);\n\n // Step 4: Verify success\n if (ctx.adapter.type === 'npm') {\n // npm ci with --json is not reliable, just check exit code 0\n // and verify output mentions added packages\n const output = resp.stdout + resp.stderr;\n debug('npm ci output: %s', output);\n assert.ok(true, 'npm ci completed successfully');\n } else {\n debug('ci completed successfully for %s', ctx.adapter.type);\n assert.ok(true, `CI install completed successfully for ${ctx.adapter.type}`);\n }\n}\n\nexport const ciTest: TestDefinition = {\n name: 'ci',\n requires: ['install'],\n run: testCi,\n};\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,4BAA4B;;;;;;;;;;;;;;;AAiBrD,SAAS,UAAU,KAA4B;AAC7C,SAAQ,IAAI,QAAQ,MAApB;EACE,KAAK,MAEH,QAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;EAC5D,KAAK,OAEH,QAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY;GAAC;EACtF,KAAK,eAEH,QAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY;GAAC;EACtF,KAAK,cAEH,QAAO,CAAC,WAAW,cAAc;EACnC,KAAK,MAEH,QAAO;GAAC;GAAW;GAAqB,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY;GAAC;EACtF,QACE,QAAO,CAAC,MAAM,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;;;AAIhE,eAAe,OAAO,KAAiC;AAErD,KAAI,IAAI,QAAQ,SAAS,QAAQ;AAC/B,QAAM,4CAA4C;AAClD;;CAGF,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,WAAW,IAAI,SACf,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,EAAE,UAAU,SAAS,CACtB;AAED,OAAM,sDAAsD,WAAW;AAGvE,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,OAAM,4CAA4C;AAIlD,OAAM,GADkB,KAAK,YAAY,eAAe,EAC9B;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAI3D,OAAM,GADgB,KAAK,YAAY,cAAc,EAC7B;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAEzD,OAAM,2CAA2C;CAGjD,MAAM,SAAS,UAAU,IAAI;AAC7B,OAAM,eAAe,OAAO;CAC5B,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,YAAY,EAAE,GAAG,OAAO;AAGnE,KAAI,IAAI,QAAQ,SAAS,OAAO;AAI9B,QAAM,qBADS,KAAK,SAAS,KAAK,OACA;AAClC,SAAO,GAAG,MAAM,gCAAgC;QAC3C;AACL,QAAM,oCAAoC,IAAI,QAAQ,KAAK;AAC3D,SAAO,GAAG,MAAM,yCAAyC,IAAI,QAAQ,OAAO;;;AAIhF,IAAa,SAAyB;CACpC,MAAM;CACN,UAAU,CAAC,UAAU;CACrB,KAAK;CACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import { TestDefinition } from '../types';\n\nimport { allScenarios, installMultipleDepsScenario } from '../scenarios';\nimport { auditTest } from './audit';\nimport { ciTest } from './ci';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\nimport { loginTest } from './login';\nimport { pingTest } from './ping';\nimport { publishTest } from './publish';\nimport { searchTest } from './search';\nimport { starTest } from './star';\nimport { unpublishTest } from './unpublish';\n\nexport const allTests: TestDefinition[] = [\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n // Scenarios (complex, multi-step tests)\n ...allScenarios,\n];\n\nexport {\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n // Scenarios\n installMultipleDepsScenario,\n allScenarios,\n};\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAa,WAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,GAAG;CACJ"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import { allScenarios, installMultipleDepsScenario } from '../scenarios';\nimport { TestDefinition } from '../types';\nimport { auditTest } from './audit';\nimport { ciTest } from './ci';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\nimport { loginTest } from './login';\nimport { pingTest } from './ping';\nimport { publishTest } from './publish';\nimport { searchTest } from './search';\nimport { starTest } from './star';\nimport { unpublishTest } from './unpublish';\n\nexport const allTests: TestDefinition[] = [\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n // Scenarios (complex, multi-step tests)\n ...allScenarios,\n];\n\nexport {\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n // Scenarios\n installMultipleDepsScenario,\n allScenarios,\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,IAAa,WAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,GAAG;CACJ"}
@@ -19,6 +19,9 @@ async function testInfo(ctx) {
19
19
  }
20
20
  });
21
21
  assert.ok(dataLine, "Expected yarn info NDJSON to contain an \"inspect\" entry");
22
+ } else if (ctx.adapter.type === "deno") {
23
+ const output = resp.stdout + resp.stderr;
24
+ assert.ok(output.includes("verdaccio"), "Expected deno info output to reference verdaccio");
22
25
  } else {
23
26
  const parsedBody = JSON.parse(resp.stdout);
24
27
  assert.strictEqual(parsedBody.name, "verdaccio", "Expected package name \"verdaccio\"");
@@ -1 +1 @@
1
- {"version":3,"file":"info.js","names":[],"sources":["../../../src/tests/info.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { createTempFolder } from '../utils/project';\nimport { writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nasync function testInfo(ctx: TestContext): Promise<void> {\n // Create a minimal temp project so yarn modern has a project context\n // and yarn classic doesn't pick up the repo's packageManager field\n const tempFolder = await createTempFolder('info-test');\n await writeFile(join(tempFolder, 'package.json'), JSON.stringify({ name: 'info-test', version: '1.0.0' }));\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n 'verdaccio',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (ctx.adapter.type === 'yarn-classic') {\n const lines = resp.stdout.split('\\n').filter(Boolean);\n const dataLine = lines.find((l) => {\n try {\n const obj = JSON.parse(l);\n return obj.type === 'inspect';\n } catch {\n return false;\n }\n });\n assert.ok(dataLine, 'Expected yarn info NDJSON to contain an \"inspect\" entry');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(parsedBody.name, 'verdaccio', 'Expected package name \"verdaccio\"');\n assert.ok(parsedBody.dependencies !== undefined, 'Expected \"dependencies\" to be defined');\n }\n}\n\nexport const infoTest: TestDefinition = {\n name: 'info',\n requires: ['info'],\n run: testInfo,\n};\n"],"mappings":";;;;;AAOA,eAAe,SAAS,KAAiC;CAGvD,MAAM,aAAa,MAAM,iBAAiB,YAAY;AACtD,OAAM,UAAU,KAAK,YAAY,eAAe,EAAE,KAAK,UAAU;EAAE,MAAM;EAAa,SAAS;EAAS,CAAC,CAAC;CAE1G,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,QACA,aACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,KAAI,IAAI,QAAQ,SAAS,gBAAgB;EAEvC,MAAM,WADQ,KAAK,OAAO,MAAM,KAAK,CAAC,OAAO,QAAQ,CAC9B,MAAM,MAAM;AACjC,OAAI;AAEF,WADY,KAAK,MAAM,EAAE,CACd,SAAS;WACd;AACN,WAAO;;IAET;AACF,SAAO,GAAG,UAAU,4DAA0D;QACzE;EACL,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,SAAO,YAAY,WAAW,MAAM,aAAa,sCAAoC;AACrF,SAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,0CAAwC;;;AAI7F,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
1
+ {"version":3,"file":"info.js","names":[],"sources":["../../../src/tests/info.ts"],"sourcesContent":["import assert from 'assert';\nimport { writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { createTempFolder } from '../utils/project';\n\nasync function testInfo(ctx: TestContext): Promise<void> {\n // Create a minimal temp project so yarn modern has a project context\n // and yarn classic doesn't pick up the repo's packageManager field\n const tempFolder = await createTempFolder('info-test');\n await writeFile(\n join(tempFolder, 'package.json'),\n JSON.stringify({ name: 'info-test', version: '1.0.0' })\n );\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n 'verdaccio',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (ctx.adapter.type === 'yarn-classic') {\n const lines = resp.stdout.split('\\n').filter(Boolean);\n const dataLine = lines.find((l) => {\n try {\n const obj = JSON.parse(l);\n return obj.type === 'inspect';\n } catch {\n return false;\n }\n });\n assert.ok(dataLine, 'Expected yarn info NDJSON to contain an \"inspect\" entry');\n } else if (ctx.adapter.type === 'deno') {\n // deno info npm:<pkg> outputs a text dependency tree\n const output = resp.stdout + resp.stderr;\n assert.ok(output.includes('verdaccio'), 'Expected deno info output to reference verdaccio');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(parsedBody.name, 'verdaccio', 'Expected package name \"verdaccio\"');\n assert.ok(parsedBody.dependencies !== undefined, 'Expected \"dependencies\" to be defined');\n }\n}\n\nexport const infoTest: TestDefinition = {\n name: 'info',\n requires: ['info'],\n run: testInfo,\n};\n"],"mappings":";;;;;AAOA,eAAe,SAAS,KAAiC;CAGvD,MAAM,aAAa,MAAM,iBAAiB,YAAY;AACtD,OAAM,UACJ,KAAK,YAAY,eAAe,EAChC,KAAK,UAAU;EAAE,MAAM;EAAa,SAAS;EAAS,CAAC,CACxD;CAED,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,QACA,aACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,KAAI,IAAI,QAAQ,SAAS,gBAAgB;EAEvC,MAAM,WADQ,KAAK,OAAO,MAAM,KAAK,CAAC,OAAO,QAAQ,CAC9B,MAAM,MAAM;AACjC,OAAI;AAEF,WADY,KAAK,MAAM,EAAE,CACd,SAAS;WACd;AACN,WAAO;;IAET;AACF,SAAO,GAAG,UAAU,4DAA0D;YACrE,IAAI,QAAQ,SAAS,QAAQ;EAEtC,MAAM,SAAS,KAAK,SAAS,KAAK;AAClC,SAAO,GAAG,OAAO,SAAS,YAAY,EAAE,mDAAmD;QACtF;EACL,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,SAAO,YAAY,WAAW,MAAM,aAAa,sCAAoC;AACrF,SAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,0CAAwC;;;AAI7F,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","names":[],"sources":["../../../src/tests/login.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport YAML from 'js-yaml';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { createTempFolder, getPackageJSON, getREADME } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:login');\n\n/**\n * Creates a project with registry config but WITHOUT auth token,\n * so whoami will use the token saved by login to ~/.yarnrc.yml.\n */\nasync function prepareLoginProject(\n ctx: TestContext,\n name: string\n): Promise<string> {\n debug('preparing project for %s (no auth token)', name);\n const tempFolder = await createTempFolder(name);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tempFolder, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tempFolder, 'package.json'), JSON.stringify({ name, version: '1.0.0' }));\n if (ctx.adapter.importPlugin) {\n debug('importing npm-login plugin into %s', tempFolder);\n await ctx.adapter.importPlugin(tempFolder, 'npm-login');\n }\n return tempFolder;\n}\n\nasync function yarnLogin(\n ctx: TestContext,\n cwd: string,\n user: string,\n password: string,\n email: string\n) {\n debug('yarn npm login as %s', user);\n return ctx.adapter.exec(\n { cwd },\n 'login',\n '--auth-type=legacy',\n `--user=${user}`,\n `--password=${password}`,\n `--email=${email}`\n );\n}\n\nasync function yarnWhoami(ctx: TestContext, cwd: string): Promise<string> {\n const resp = await ctx.adapter.exec({ cwd }, 'whoami');\n debug('yarn npm whoami: %s', resp.stdout);\n return resp.stdout;\n}\n\nasync function testLogin(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n const id = ctx.runId;\n\n if (type !== 'yarn-modern') {\n debug('login test only supported for yarn-modern, skipping %s', type);\n return;\n }\n\n const user1 = `login-a-${id}`;\n const password1 = 'test-password-123';\n const email1 = `${user1}@test.example.com`;\n\n await ctx.subTest('create new user + whoami', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login1-${id}`);\n const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);\n assert.ok(\n loginResp.stdout.includes('Logged in') || loginResp.stdout.includes('token saved'),\n `Expected login success message but got \"${loginResp.stdout}\"`\n );\n const who = await yarnWhoami(ctx, tf);\n assert.ok(who.includes(user1), `Expected whoami \"${user1}\" but got \"${who}\"`);\n });\n\n await ctx.subTest('login existing user', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);\n const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);\n assert.ok(\n loginResp.stdout.includes('Logged in') || loginResp.stdout.includes('token saved'),\n `Expected login success for existing user but got \"${loginResp.stdout}\"`\n );\n const who = await yarnWhoami(ctx, tf);\n assert.ok(who.includes(user1), `Expected \"${user1}\" but got \"${who}\"`);\n });\n\n await ctx.subTest('wrong password fails', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, 'wrong-password', email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Login with wrong password should have failed');\n });\n\n await ctx.subTest('login then publish', async () => {\n const pkgName = `@verdaccio/login-pub-${id}`;\n const tf = await createTempFolder(`login-pub-${id}`);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tf, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tf, 'package.json'), getPackageJSON(pkgName, '1.0.0'));\n await writeFile(join(tf, 'README.md'), getREADME(pkgName));\n if (ctx.adapter.importPlugin) {\n await ctx.adapter.importPlugin(tf, 'npm-login');\n }\n await yarnLogin(ctx, tf, user1, password1, email1);\n await ctx.adapter.exec({ cwd: tf }, 'install');\n const pubResp = await ctx.adapter.exec({ cwd: tf }, 'publish');\n assert.ok(\n pubResp.stdout.includes('Package archive published'),\n `Expected publish success but got \"${pubResp.stdout}\"`\n );\n });\n\n await ctx.subTest('switch users', async () => {\n const user2 = `login-b-${id}`;\n const password2 = 'other-password-456';\n const email2 = `${user2}@test.example.com`;\n const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);\n\n await yarnLogin(ctx, tf, user1, password1, email1);\n const who1 = await yarnWhoami(ctx, tf);\n assert.ok(who1.includes(user1), `Expected \"${user1}\" but got \"${who1}\"`);\n\n await yarnLogin(ctx, tf, user2, password2, email2);\n const who2 = await yarnWhoami(ctx, tf);\n assert.ok(who2.includes(user2), `Expected \"${user2}\" after switch but got \"${who2}\"`);\n });\n}\n\nexport const loginTest: TestDefinition = {\n name: 'login',\n requires: ['login'],\n run: testLogin,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"login.js","names":[],"sources":["../../../src/tests/login.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { writeFile } from 'fs/promises';\nimport YAML from 'js-yaml';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { createTempFolder, getPackageJSON, getREADME } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:login');\n\n/**\n * Creates a project with registry config but WITHOUT auth token,\n * so whoami will use the token saved by login to ~/.yarnrc.yml.\n */\nasync function prepareLoginProject(ctx: TestContext, name: string): Promise<string> {\n debug('preparing project for %s (no auth token)', name);\n const tempFolder = await createTempFolder(name);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tempFolder, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tempFolder, 'package.json'), JSON.stringify({ name, version: '1.0.0' }));\n if (ctx.adapter.importPlugin) {\n debug('importing npm-login plugin into %s', tempFolder);\n await ctx.adapter.importPlugin(tempFolder, 'npm-login');\n }\n return tempFolder;\n}\n\nasync function yarnLogin(\n ctx: TestContext,\n cwd: string,\n user: string,\n password: string,\n email: string\n) {\n debug('yarn npm login as %s', user);\n return ctx.adapter.exec(\n { cwd },\n 'login',\n '--auth-type=legacy',\n `--user=${user}`,\n `--password=${password}`,\n `--email=${email}`\n );\n}\n\nasync function yarnWhoami(ctx: TestContext, cwd: string): Promise<string> {\n const resp = await ctx.adapter.exec({ cwd }, 'whoami');\n debug('yarn npm whoami: %s', resp.stdout);\n return resp.stdout;\n}\n\nasync function testLogin(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n const id = ctx.runId;\n\n if (type !== 'yarn-modern') {\n debug('login test only supported for yarn-modern, skipping %s', type);\n return;\n }\n\n const user1 = `login-a-${id}`;\n const password1 = 'test-password-123';\n const email1 = `${user1}@test.example.com`;\n\n await ctx.subTest('create new user + whoami', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login1-${id}`);\n const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);\n assert.ok(\n loginResp.stdout.includes('Logged in') || loginResp.stdout.includes('token saved'),\n `Expected login success message but got \"${loginResp.stdout}\"`\n );\n const who = await yarnWhoami(ctx, tf);\n assert.ok(who.includes(user1), `Expected whoami \"${user1}\" but got \"${who}\"`);\n });\n\n await ctx.subTest('login existing user', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);\n const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);\n assert.ok(\n loginResp.stdout.includes('Logged in') || loginResp.stdout.includes('token saved'),\n `Expected login success for existing user but got \"${loginResp.stdout}\"`\n );\n const who = await yarnWhoami(ctx, tf);\n assert.ok(who.includes(user1), `Expected \"${user1}\" but got \"${who}\"`);\n });\n\n await ctx.subTest('wrong password fails', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, 'wrong-password', email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Login with wrong password should have failed');\n });\n\n await ctx.subTest('login then publish', async () => {\n const pkgName = `@verdaccio/login-pub-${id}`;\n const tf = await createTempFolder(`login-pub-${id}`);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tf, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tf, 'package.json'), getPackageJSON(pkgName, '1.0.0'));\n await writeFile(join(tf, 'README.md'), getREADME(pkgName));\n if (ctx.adapter.importPlugin) {\n await ctx.adapter.importPlugin(tf, 'npm-login');\n }\n await yarnLogin(ctx, tf, user1, password1, email1);\n await ctx.adapter.exec({ cwd: tf }, 'install');\n const pubResp = await ctx.adapter.exec({ cwd: tf }, 'publish');\n assert.ok(\n pubResp.stdout.includes('Package archive published'),\n `Expected publish success but got \"${pubResp.stdout}\"`\n );\n });\n\n await ctx.subTest('switch users', async () => {\n const user2 = `login-b-${id}`;\n const password2 = 'other-password-456';\n const email2 = `${user2}@test.example.com`;\n const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);\n\n await yarnLogin(ctx, tf, user1, password1, email1);\n const who1 = await yarnWhoami(ctx, tf);\n assert.ok(who1.includes(user1), `Expected \"${user1}\" but got \"${who1}\"`);\n\n await yarnLogin(ctx, tf, user2, password2, email2);\n const who2 = await yarnWhoami(ctx, tf);\n assert.ok(who2.includes(user2), `Expected \"${user2}\" after switch but got \"${who2}\"`);\n });\n}\n\nexport const loginTest: TestDefinition = {\n name: 'login',\n requires: ['login'],\n run: testLogin,\n};\n"],"mappings":"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"}
@@ -14,6 +14,7 @@ async function testPublish(ctx) {
14
14
  const resp = await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
15
15
  if (type === "yarn-modern") assert.ok(resp.stdout.match(/Package archive published/), `Expected "Package archive published" for ${pkgName} but got "${resp.stdout}"`);
16
16
  else if (type === "yarn-classic") assert.ok(resp.stdout.length > 0, `Expected publish output for ${pkgName}`);
17
+ else if (type === "bun") assert.ok(true, `Publish succeeded for ${pkgName}`);
17
18
  else try {
18
19
  const parsedBody = JSON.parse(resp.stdout);
19
20
  assert.strictEqual(parsedBody.name, pkgName, `Expected package name "${pkgName}" but got "${parsedBody.name}"`);
@@ -1 +1 @@
1
- {"version":3,"file":"publish.js","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPublish(ctx: TestContext): Promise<void> {\n const packages = [\n `verdaccio-memory-${ctx.runId}`,\n `verdaccio-${ctx.runId}`,\n `@verdaccio/foo-${ctx.runId}`,\n `@verdaccio/some-foo-${ctx.runId}`,\n ];\n\n const type = ctx.adapter.type;\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n // yarn modern requires install before publish to generate lockfile\n if (type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n assert.ok(\n resp.stdout.match(/Package archive published/),\n `Expected \"Package archive published\" for ${pkgName} but got \"${resp.stdout}\"`\n );\n } else if (type === 'yarn-classic') {\n // yarn classic --json outputs NDJSON — just verify no error exit (exit code 0 is enough)\n assert.ok(resp.stdout.length > 0, `Expected publish output for ${pkgName}`);\n } else {\n // npm / pnpm\n try {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(parsedBody.name, pkgName, `Expected package name \"${pkgName}\" but got \"${parsedBody.name}\"`);\n assert.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);\n } catch {\n // pnpm v11+ native publish doesn't output JSON — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n }\n }\n }\n}\n\nexport const publishTest: TestDefinition = {\n name: 'publish',\n requires: ['publish'],\n run: testPublish,\n};\n"],"mappings":";;AAIA,eAAe,YAAY,KAAiC;CAC1D,MAAM,WAAW;EACf,oBAAoB,IAAI;EACxB,aAAa,IAAI;EACjB,kBAAkB,IAAI;EACtB,uBAAuB,IAAI;EAC5B;CAED,MAAM,OAAO,IAAI,QAAQ;AAEzB,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AAGD,MAAI,SAAS,cACX,OAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,YAAY,EAAE,UAAU;EAGxD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,MAAI,SAAS,cACX,QAAO,GACL,KAAK,OAAO,MAAM,4BAA4B,EAC9C,4CAA4C,QAAQ,YAAY,KAAK,OAAO,GAC7E;WACQ,SAAS,eAElB,QAAO,GAAG,KAAK,OAAO,SAAS,GAAG,+BAA+B,UAAU;MAG3E,KAAI;GACF,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,UAAO,YAAY,WAAW,MAAM,SAAS,0BAA0B,QAAQ,aAAa,WAAW,KAAK,GAAG;AAC/G,UAAO,GAAG,WAAW,OAAO,oCAAoC,UAAU;UACpE;AAEN,UAAO,GAAG,MAAM,yBAAyB,UAAU;;;;AAM3D,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,UAAU;CACrB,KAAK;CACN"}
1
+ {"version":3,"file":"publish.js","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPublish(ctx: TestContext): Promise<void> {\n const packages = [\n `verdaccio-memory-${ctx.runId}`,\n `verdaccio-${ctx.runId}`,\n `@verdaccio/foo-${ctx.runId}`,\n `@verdaccio/some-foo-${ctx.runId}`,\n ];\n\n const type = ctx.adapter.type;\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n // yarn modern requires install before publish to generate lockfile\n if (type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n assert.ok(\n resp.stdout.match(/Package archive published/),\n `Expected \"Package archive published\" for ${pkgName} but got \"${resp.stdout}\"`\n );\n } else if (type === 'yarn-classic') {\n // yarn classic --json outputs NDJSON — just verify no error exit (exit code 0 is enough)\n assert.ok(resp.stdout.length > 0, `Expected publish output for ${pkgName}`);\n } else if (type === 'bun') {\n // bun publish outputs text confirmation — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n } else {\n // npm / pnpm\n try {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(\n parsedBody.name,\n pkgName,\n `Expected package name \"${pkgName}\" but got \"${parsedBody.name}\"`\n );\n assert.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);\n } catch {\n // pnpm v11+ native publish doesn't output JSON — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n }\n }\n }\n}\n\nexport const publishTest: TestDefinition = {\n name: 'publish',\n requires: ['publish'],\n run: testPublish,\n};\n"],"mappings":";;AAIA,eAAe,YAAY,KAAiC;CAC1D,MAAM,WAAW;EACf,oBAAoB,IAAI;EACxB,aAAa,IAAI;EACjB,kBAAkB,IAAI;EACtB,uBAAuB,IAAI;EAC5B;CAED,MAAM,OAAO,IAAI,QAAQ;AAEzB,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AAGD,MAAI,SAAS,cACX,OAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,YAAY,EAAE,UAAU;EAGxD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AAED,MAAI,SAAS,cACX,QAAO,GACL,KAAK,OAAO,MAAM,4BAA4B,EAC9C,4CAA4C,QAAQ,YAAY,KAAK,OAAO,GAC7E;WACQ,SAAS,eAElB,QAAO,GAAG,KAAK,OAAO,SAAS,GAAG,+BAA+B,UAAU;WAClE,SAAS,MAElB,QAAO,GAAG,MAAM,yBAAyB,UAAU;MAGnD,KAAI;GACF,MAAM,aAAa,KAAK,MAAM,KAAK,OAAO;AAC1C,UAAO,YACL,WAAW,MACX,SACA,0BAA0B,QAAQ,aAAa,WAAW,KAAK,GAChE;AACD,UAAO,GAAG,WAAW,OAAO,oCAAoC,UAAU;UACpE;AAEN,UAAO,GAAG,MAAM,yBAAyB,UAAU;;;;AAM3D,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,UAAU;CACrB,KAAK;CACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"star.js","names":[],"sources":["../../../src/tests/star.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testStar(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: star a package\n const pkgName1 = `@verdaccio/star1-${id}`;\n const { tempFolder: tf1 } = await ctx.adapter.prepareProject(\n pkgName1,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await ctx.adapter.exec(\n { cwd: tf1 },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const resp1 = await ctx.adapter.exec(\n { cwd: tf1 },\n 'star',\n pkgName1,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp1.stdout.includes(pkgName1),\n `Expected star output to contain \"${pkgName1}\" but got \"${resp1.stdout}\"`\n );\n\n // Test 2: unstar a package\n const pkgName2 = `@verdaccio/star2-${id}`;\n const { tempFolder: tf2 } = await ctx.adapter.prepareProject(\n pkgName2,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await ctx.adapter.exec(\n { cwd: tf2 },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n await ctx.adapter.exec(\n { cwd: tf2 },\n 'star',\n pkgName2,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'unstar',\n pkgName2,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp2.stdout.includes(pkgName2),\n `Expected unstar output to contain \"${pkgName2}\" but got \"${resp2.stdout}\"`\n );\n}\n\nexport const starTest: TestDefinition = {\n name: 'star',\n requires: ['star'],\n run: testStar,\n};\n"],"mappings":";;AAIA,eAAe,SAAS,KAAiC;CACvD,MAAM,KAAK,IAAI;CAGf,MAAM,WAAW,oBAAoB;CACrC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,KAAK,EACZ,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;CACD,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,KAAK,EACZ,QACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,QAAO,GACL,MAAM,OAAO,SAAS,SAAS,EAC/B,oCAAoC,SAAS,aAAa,MAAM,OAAO,GACxE;CAGD,MAAM,WAAW,oBAAoB;CACrC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,KAAK,EACZ,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,KAAK,EACZ,QACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;CACD,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,KAAK,EACZ,UACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,QAAO,GACL,MAAM,OAAO,SAAS,SAAS,EAC/B,sCAAsC,SAAS,aAAa,MAAM,OAAO,GAC1E;;AAGH,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
1
+ {"version":3,"file":"star.js","names":[],"sources":["../../../src/tests/star.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testStar(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: star a package\n const pkgName1 = `@verdaccio/star1-${id}`;\n const { tempFolder: tf1 } = await ctx.adapter.prepareProject(\n pkgName1,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await ctx.adapter.exec({ cwd: tf1 }, 'publish', ...ctx.adapter.registryArg(ctx.registryUrl));\n const resp1 = await ctx.adapter.exec(\n { cwd: tf1 },\n 'star',\n pkgName1,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp1.stdout.includes(pkgName1),\n `Expected star output to contain \"${pkgName1}\" but got \"${resp1.stdout}\"`\n );\n\n // Test 2: unstar a package\n const pkgName2 = `@verdaccio/star2-${id}`;\n const { tempFolder: tf2 } = await ctx.adapter.prepareProject(\n pkgName2,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await ctx.adapter.exec({ cwd: tf2 }, 'publish', ...ctx.adapter.registryArg(ctx.registryUrl));\n await ctx.adapter.exec(\n { cwd: tf2 },\n 'star',\n pkgName2,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'unstar',\n pkgName2,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp2.stdout.includes(pkgName2),\n `Expected unstar output to contain \"${pkgName2}\" but got \"${resp2.stdout}\"`\n );\n}\n\nexport const starTest: TestDefinition = {\n name: 'star',\n requires: ['star'],\n run: testStar,\n};\n"],"mappings":";;AAIA,eAAe,SAAS,KAAiC;CACvD,MAAM,KAAK,IAAI;CAGf,MAAM,WAAW,oBAAoB;CACrC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,KAAK,EAAE,WAAW,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;CAC5F,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,KAAK,EACZ,QACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,QAAO,GACL,MAAM,OAAO,SAAS,SAAS,EAC/B,oCAAoC,SAAS,aAAa,MAAM,OAAO,GACxE;CAGD,MAAM,WAAW,oBAAoB;CACrC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,KAAK,EAAE,WAAW,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAAC;AAC5F,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,KAAK,EACZ,QACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;CACD,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,KAAK,EACZ,UACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,QAAO,GACL,MAAM,OAAO,SAAS,SAAS,EAC/B,sCAAsC,SAAS,aAAa,MAAM,OAAO,GAC1E;;AAGH,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
@@ -1,7 +1,7 @@
1
1
  import buildDebug from "debug";
2
2
  import { spawn } from "child_process";
3
- import { createInterface } from "readline";
4
3
  import { basename } from "path";
4
+ import { createInterface } from "readline";
5
5
  //#region src/utils/process.ts
6
6
  var debug = buildDebug("verdaccio:e2e-cli:process");
7
7
  var debugRead = buildDebug("verdaccio:e2e-cli:line");
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","names":[],"sources":["../../../src/utils/process.ts"],"sourcesContent":["import { SpawnOptions, spawn } from 'child_process';\nimport buildDebug from 'debug';\nimport { createInterface } from 'readline';\nimport { basename } from 'path';\n\nimport { ExecOutput } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:process');\nconst debugRead = buildDebug('verdaccio:e2e-cli:line');\n\nlet _verbose = false;\n\nexport function setVerbose(v: boolean) {\n _verbose = v;\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n};\n\nfunction shortCmd(cmd: string, args: string[]): string {\n const bin = basename(cmd);\n return `${bin} ${args.join(' ')}`;\n}\n\nexport async function exec(options: SpawnOptions, cmd: string, args: string[]): Promise<ExecOutput> {\n debug('start exec %o %o %o', options, cmd, args ? args.join(' ') : '');\n let stdout = '';\n let stderr = '';\n\n if (_verbose) {\n const cwd = options.cwd ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}` : '';\n process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\\n`);\n }\n\n const spawnOptions: SpawnOptions = {\n cwd: options.cwd,\n stdio: options.stdio || 'pipe',\n env: options.env || process.env,\n };\n\n if (process.platform.startsWith('win')) {\n args.unshift('/c', cmd);\n cmd = 'cmd.exe';\n spawnOptions.stdio = 'pipe';\n }\n\n const start = Date.now();\n const childProcess = spawn(cmd, args, spawnOptions);\n\n if (childProcess.stdout) {\n childProcess.stdout.on('data', (data) => {\n debugRead('data %o', data.toString());\n });\n const rl = createInterface({\n input: childProcess.stdout,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', function (line) {\n debugRead('line %o', line);\n stdout += line;\n });\n }\n\n if (childProcess.stderr) {\n childProcess.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n }\n\n const err = new Error(`Running \"${cmd} ${args.join(' ')}\" returned error code `);\n return new Promise((resolve, reject) => {\n childProcess.on('exit', (code) => {\n const duration = Date.now() - start;\n debugRead('exit %o', code);\n\n if (_verbose) {\n const status = code\n ? `${COLORS.red}exit ${code}${COLORS.reset}`\n : `${COLORS.green}ok${COLORS.reset}`;\n process.stdout.write(` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\\n`);\n }\n\n if (!code) {\n resolve({ stdout, stderr });\n } else {\n err.message += `${code}...\\n\\nSTDOUT:\\n${stdout}\\n\\nSTDERR:\\n${stderr}\\n`;\n reject(err);\n }\n });\n });\n}\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,4BAA4B;AACrD,IAAM,YAAY,WAAW,yBAAyB;AAEtD,IAAI,WAAW;AAEf,SAAgB,WAAW,GAAY;AACrC,YAAW;;AAGb,IAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,MAAM;CACN,OAAO;CACP,KAAK;CACL,QAAQ;CACT;AAED,SAAS,SAAS,KAAa,MAAwB;AAErD,QAAO,GADK,SAAS,IAAI,CACX,GAAG,KAAK,KAAK,IAAI;;AAGjC,eAAsB,KAAK,SAAuB,KAAa,MAAqC;AAClG,OAAM,uBAAuB,SAAS,KAAK,OAAO,KAAK,KAAK,IAAI,GAAG,GAAG;CACtE,IAAI,SAAS;CACb,IAAI,SAAS;AAEb,KAAI,UAAU;EACZ,MAAM,MAAM,QAAQ,MAAM,IAAI,OAAO,IAAI,QAAQ,SAAS,OAAO,QAAQ,IAAI,CAAC,CAAC,GAAG,OAAO,UAAU;AACnG,UAAQ,OAAO,MAAM,SAAS,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,SAAS,KAAK,KAAK,GAAG,IAAI,IAAI;;CAG7F,MAAM,eAA6B;EACjC,KAAK,QAAQ;EACb,OAAO,QAAQ,SAAS;EACxB,KAAK,QAAQ,OAAO,QAAQ;EAC7B;AAED,KAAI,QAAQ,SAAS,WAAW,MAAM,EAAE;AACtC,OAAK,QAAQ,MAAM,IAAI;AACvB,QAAM;AACN,eAAa,QAAQ;;CAGvB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,eAAe,MAAM,KAAK,MAAM,aAAa;AAEnD,KAAI,aAAa,QAAQ;AACvB,eAAa,OAAO,GAAG,SAAS,SAAS;AACvC,aAAU,WAAW,KAAK,UAAU,CAAC;IACrC;AACS,kBAAgB;GACzB,OAAO,aAAa;GACpB,QAAQ,QAAQ;GAChB,UAAU;GACX,CAAC,CAEC,GAAG,QAAQ,SAAU,MAAM;AAC5B,aAAU,WAAW,KAAK;AAC1B,aAAU;IACV;;AAGJ,KAAI,aAAa,OACf,cAAa,OAAO,GAAG,SAAS,SAAS;AACvC,YAAU,KAAK,UAAU;GACzB;CAGJ,MAAM,sBAAM,IAAI,MAAM,YAAY,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,wBAAwB;AAChF,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,eAAa,GAAG,SAAS,SAAS;GAChC,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,aAAU,WAAW,KAAK;AAE1B,OAAI,UAAU;IACZ,MAAM,SAAS,OACX,GAAG,OAAO,IAAI,OAAO,OAAO,OAAO,UACnC,GAAG,OAAO,MAAM,IAAI,OAAO;AAC/B,YAAQ,OAAO,MAAM,SAAS,OAAO,IAAI,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,SAAS,KAAK,OAAO,MAAM,IAAI;;AAGzG,OAAI,CAAC,KACH,SAAQ;IAAE;IAAQ;IAAQ,CAAC;QACtB;AACL,QAAI,WAAW,GAAG,KAAK,kBAAkB,OAAO,eAAe,OAAO;AACtE,WAAO,IAAI;;IAEb;GACF"}
1
+ {"version":3,"file":"process.js","names":[],"sources":["../../../src/utils/process.ts"],"sourcesContent":["import { SpawnOptions, spawn } from 'child_process';\nimport buildDebug from 'debug';\nimport { basename } from 'path';\nimport { createInterface } from 'readline';\n\nimport { ExecOutput } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:process');\nconst debugRead = buildDebug('verdaccio:e2e-cli:line');\n\nlet _verbose = false;\n\nexport function setVerbose(v: boolean) {\n _verbose = v;\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n};\n\nfunction shortCmd(cmd: string, args: string[]): string {\n const bin = basename(cmd);\n return `${bin} ${args.join(' ')}`;\n}\n\nexport async function exec(\n options: SpawnOptions,\n cmd: string,\n args: string[]\n): Promise<ExecOutput> {\n debug('start exec %o %o %o', options, cmd, args ? args.join(' ') : '');\n let stdout = '';\n let stderr = '';\n\n if (_verbose) {\n const cwd = options.cwd\n ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}`\n : '';\n process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\\n`);\n }\n\n const spawnOptions: SpawnOptions = {\n cwd: options.cwd,\n stdio: options.stdio || 'pipe',\n env: options.env || process.env,\n };\n\n if (process.platform.startsWith('win')) {\n args.unshift('/c', cmd);\n cmd = 'cmd.exe';\n spawnOptions.stdio = 'pipe';\n }\n\n const start = Date.now();\n const childProcess = spawn(cmd, args, spawnOptions);\n\n if (childProcess.stdout) {\n childProcess.stdout.on('data', (data) => {\n debugRead('data %o', data.toString());\n });\n const rl = createInterface({\n input: childProcess.stdout,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', function (line) {\n debugRead('line %o', line);\n stdout += line;\n });\n }\n\n if (childProcess.stderr) {\n childProcess.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n }\n\n const err = new Error(`Running \"${cmd} ${args.join(' ')}\" returned error code `);\n return new Promise((resolve, reject) => {\n childProcess.on('exit', (code) => {\n const duration = Date.now() - start;\n debugRead('exit %o', code);\n\n if (_verbose) {\n const status = code\n ? `${COLORS.red}exit ${code}${COLORS.reset}`\n : `${COLORS.green}ok${COLORS.reset}`;\n process.stdout.write(\n ` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\\n`\n );\n }\n\n if (!code) {\n resolve({ stdout, stderr });\n } else {\n err.message += `${code}...\\n\\nSTDOUT:\\n${stdout}\\n\\nSTDERR:\\n${stderr}\\n`;\n reject(err);\n }\n });\n });\n}\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,4BAA4B;AACrD,IAAM,YAAY,WAAW,yBAAyB;AAEtD,IAAI,WAAW;AAEf,SAAgB,WAAW,GAAY;AACrC,YAAW;;AAGb,IAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,MAAM;CACN,OAAO;CACP,KAAK;CACL,QAAQ;CACT;AAED,SAAS,SAAS,KAAa,MAAwB;AAErD,QAAO,GADK,SAAS,IAAI,CACX,GAAG,KAAK,KAAK,IAAI;;AAGjC,eAAsB,KACpB,SACA,KACA,MACqB;AACrB,OAAM,uBAAuB,SAAS,KAAK,OAAO,KAAK,KAAK,IAAI,GAAG,GAAG;CACtE,IAAI,SAAS;CACb,IAAI,SAAS;AAEb,KAAI,UAAU;EACZ,MAAM,MAAM,QAAQ,MAChB,IAAI,OAAO,IAAI,QAAQ,SAAS,OAAO,QAAQ,IAAI,CAAC,CAAC,GAAG,OAAO,UAC/D;AACJ,UAAQ,OAAO,MAAM,SAAS,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,SAAS,KAAK,KAAK,GAAG,IAAI,IAAI;;CAG7F,MAAM,eAA6B;EACjC,KAAK,QAAQ;EACb,OAAO,QAAQ,SAAS;EACxB,KAAK,QAAQ,OAAO,QAAQ;EAC7B;AAED,KAAI,QAAQ,SAAS,WAAW,MAAM,EAAE;AACtC,OAAK,QAAQ,MAAM,IAAI;AACvB,QAAM;AACN,eAAa,QAAQ;;CAGvB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,eAAe,MAAM,KAAK,MAAM,aAAa;AAEnD,KAAI,aAAa,QAAQ;AACvB,eAAa,OAAO,GAAG,SAAS,SAAS;AACvC,aAAU,WAAW,KAAK,UAAU,CAAC;IACrC;AACS,kBAAgB;GACzB,OAAO,aAAa;GACpB,QAAQ,QAAQ;GAChB,UAAU;GACX,CAAC,CAEC,GAAG,QAAQ,SAAU,MAAM;AAC5B,aAAU,WAAW,KAAK;AAC1B,aAAU;IACV;;AAGJ,KAAI,aAAa,OACf,cAAa,OAAO,GAAG,SAAS,SAAS;AACvC,YAAU,KAAK,UAAU;GACzB;CAGJ,MAAM,sBAAM,IAAI,MAAM,YAAY,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,wBAAwB;AAChF,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,eAAa,GAAG,SAAS,SAAS;GAChC,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,aAAU,WAAW,KAAK;AAE1B,OAAI,UAAU;IACZ,MAAM,SAAS,OACX,GAAG,OAAO,IAAI,OAAO,OAAO,OAAO,UACnC,GAAG,OAAO,MAAM,IAAI,OAAO;AAC/B,YAAQ,OAAO,MACb,SAAS,OAAO,IAAI,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,SAAS,KAAK,OAAO,MAAM,IAC/E;;AAGH,OAAI,CAAC,KACH,SAAQ;IAAE;IAAQ;IAAQ,CAAC;QACtB;AACL,QAAI,WAAW,GAAG,KAAK,kBAAkB,OAAO,eAAe,OAAO;AACtE,WAAO,IAAI;;IAEb;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry-client.js","names":[],"sources":["../../../src/utils/registry-client.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { ping } from '@verdaccio/registry-cli';\n\nconst debug = buildDebug('verdaccio:e2e-cli:registry-client');\n\nexport type RegistryAuth = {\n token: string;\n user: string;\n};\n\n/**\n * Creates a user on the registry and returns the auth token.\n * Uses a unique username per run to avoid 409 conflicts.\n * Works against any running Verdaccio instance with htpasswd auth.\n */\nexport async function createUser(\n registryUrl: string,\n user = `e2e-user-${Date.now()}`,\n password = 'e2e-test-password'\n): Promise<RegistryAuth> {\n debug('creating user %s on %s', user, registryUrl);\n const url = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: user,\n password,\n _id: `org.couchdb.user:${user}`,\n type: 'user',\n roles: [],\n date: new Date().toISOString(),\n }),\n });\n\n const body = (await response.json()) as any;\n\n if (response.status === 201 || response.status === 200 || response.status === 409) {\n const token = body.token;\n if (token) {\n debug('user %s authenticated, token obtained', user);\n return { token, user };\n }\n }\n\n throw new Error(\n `Failed to create/login user \"${user}\" on ${registryUrl}: ${response.status} ${JSON.stringify(body)}`\n );\n}\n\n/**\n * Ping the registry to check it's alive.\n * Uses @verdaccio/registry-cli ping.\n */\nexport async function pingRegistry(registryUrl: string): Promise<boolean> {\n debug('pinging %s', registryUrl);\n try {\n const result = await ping(registryUrl);\n return result.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;AAGA,IAAM,QAAQ,WAAW,oCAAoC;;;;;;AAY7D,eAAsB,WACpB,aACA,OAAO,YAAY,KAAK,KAAK,IAC7B,WAAW,qBACY;AACvB,OAAM,0BAA0B,MAAM,YAAY;CAClD,MAAM,MAAM,GAAG,YAAY,2BAA2B,mBAAmB,KAAK;CAC9E,MAAM,WAAW,MAAM,MAAM,KAAK;EAChC,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU;GACnB,MAAM;GACN;GACA,KAAK,oBAAoB;GACzB,MAAM;GACN,OAAO,EAAE;GACT,uBAAM,IAAI,MAAM,EAAC,aAAa;GAC/B,CAAC;EACH,CAAC;CAEF,MAAM,OAAQ,MAAM,SAAS,MAAM;AAEnC,KAAI,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;EACjF,MAAM,QAAQ,KAAK;AACnB,MAAI,OAAO;AACT,SAAM,yCAAyC,KAAK;AACpD,UAAO;IAAE;IAAO;IAAM;;;AAI1B,OAAM,IAAI,MACR,gCAAgC,KAAK,OAAO,YAAY,IAAI,SAAS,OAAO,GAAG,KAAK,UAAU,KAAK,GACpG;;;;;;AAOH,eAAsB,aAAa,aAAuC;AACxE,OAAM,cAAc,YAAY;AAChC,KAAI;AAEF,UADe,MAAM,KAAK,YAAY,EACxB;SACR;AACN,SAAO"}
1
+ {"version":3,"file":"registry-client.js","names":[],"sources":["../../../src/utils/registry-client.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport { ping } from '@verdaccio/registry-cli';\n\nconst debug = buildDebug('verdaccio:e2e-cli:registry-client');\n\nexport type RegistryAuth = {\n token: string;\n user: string;\n};\n\n/**\n * Creates a user on the registry and returns the auth token.\n * Uses a unique username per run to avoid 409 conflicts.\n * Works against any running Verdaccio instance with htpasswd auth.\n */\nexport async function createUser(\n registryUrl: string,\n user = `e2e-user-${Date.now()}`,\n password = 'e2e-test-password'\n): Promise<RegistryAuth> {\n debug('creating user %s on %s', user, registryUrl);\n const url = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: user,\n password,\n _id: `org.couchdb.user:${user}`,\n type: 'user',\n roles: [],\n date: new Date().toISOString(),\n }),\n });\n\n const body = (await response.json()) as any;\n\n if (response.status === 201 || response.status === 200 || response.status === 409) {\n const token = body.token;\n if (token) {\n debug('user %s authenticated, token obtained', user);\n return { token, user };\n }\n }\n\n throw new Error(\n `Failed to create/login user \"${user}\" on ${registryUrl}: ${response.status} ${JSON.stringify(body)}`\n );\n}\n\n/**\n * Ping the registry to check it's alive.\n * Uses @verdaccio/registry-cli ping.\n */\nexport async function pingRegistry(registryUrl: string): Promise<boolean> {\n debug('pinging %s', registryUrl);\n try {\n const result = await ping(registryUrl);\n return result.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;AAIA,IAAM,QAAQ,WAAW,oCAAoC;;;;;;AAY7D,eAAsB,WACpB,aACA,OAAO,YAAY,KAAK,KAAK,IAC7B,WAAW,qBACY;AACvB,OAAM,0BAA0B,MAAM,YAAY;CAClD,MAAM,MAAM,GAAG,YAAY,2BAA2B,mBAAmB,KAAK;CAC9E,MAAM,WAAW,MAAM,MAAM,KAAK;EAChC,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU;GACnB,MAAM;GACN;GACA,KAAK,oBAAoB;GACzB,MAAM;GACN,OAAO,EAAE;GACT,uBAAM,IAAI,MAAM,EAAC,aAAa;GAC/B,CAAC;EACH,CAAC;CAEF,MAAM,OAAQ,MAAM,SAAS,MAAM;AAEnC,KAAI,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;EACjF,MAAM,QAAQ,KAAK;AACnB,MAAI,OAAO;AACT,SAAM,yCAAyC,KAAK;AACpD,UAAO;IAAE;IAAO;IAAM;;;AAI1B,OAAM,IAAI,MACR,gCAAgC,KAAK,OAAO,YAAY,IAAI,SAAS,OAAO,GAAG,KAAK,UAAU,KAAK,GACpG;;;;;;AAOH,eAAsB,aAAa,aAAuC;AACxE,OAAM,cAAc,YAAY;AAChC,KAAI;AAEF,UADe,MAAM,KAAK,YAAY,EACxB;SACR;AACN,SAAO"}
package/build/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare function main(argv?: string[]): Promise<void>;
2
2
  export { allTests } from './tests';
3
3
  export { allScenarios } from './scenarios';
4
- export { createNpmAdapter, createPnpmAdapter, createYarnClassicAdapter, createYarnModernAdapter } from './adapters';
4
+ export { createBunAdapter, createDenoAdapter, createNpmAdapter, createPnpmAdapter, createYarnClassicAdapter, createYarnModernAdapter, } from './adapters';
5
5
  export { runAll, runSuite } from './runner';
6
6
  export type { PackageManagerAdapter, TestDefinition, TestContext, CliOptions } from './types';
@@ -1,5 +1,5 @@
1
- import { TestDefinition } from '../types';
2
1
  import { allScenarios, installMultipleDepsScenario } from '../scenarios';
2
+ import { TestDefinition } from '../types';
3
3
  import { auditTest } from './audit';
4
4
  import { ciTest } from './ci';
5
5
  import { deprecateTest } from './deprecate';
package/build/types.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { SpawnOptions } from 'child_process';
3
2
  export type ExecOutput = {
4
3
  stdout: string;
@@ -46,8 +45,8 @@ export type TestDefinition = {
46
45
  export interface PackageManagerAdapter {
47
46
  /** Display name, e.g. "npm@10" */
48
47
  name: string;
49
- /** Package manager type: npm, pnpm, yarn-classic, yarn-modern */
50
- type: 'npm' | 'pnpm' | 'yarn-classic' | 'yarn-modern';
48
+ /** Package manager type: npm, pnpm, yarn-classic, yarn-modern, bun, deno */
49
+ type: 'npm' | 'pnpm' | 'yarn-classic' | 'yarn-modern' | 'bun' | 'deno';
51
50
  /** Resolved path to the binary */
52
51
  bin: string;
53
52
  /** Commands this PM supports */
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { SpawnOptions } from 'child_process';
3
2
  import { ExecOutput } from '../types';
4
3
  export declare function setVerbose(v: boolean): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/e2e-cli",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "CLI tool to run Verdaccio e2e tests against any running registry",
@@ -35,7 +35,7 @@
35
35
  "devDependencies": {
36
36
  "@types/debug": "4.1.12",
37
37
  "@types/js-yaml": "4.0.9",
38
- "@types/node": "20.19.0",
38
+ "@types/node": "24.12.4",
39
39
  "vite": "8.0.1"
40
40
  },
41
41
  "scripts": {