@verdaccio/e2e-cli 2.4.0 → 2.6.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 (50) hide show
  1. package/build/cjs/adapters/pnpm.cjs +31 -2
  2. package/build/cjs/adapters/pnpm.cjs.map +1 -1
  3. package/build/cjs/adapters/yarn-modern.cjs +52 -3
  4. package/build/cjs/adapters/yarn-modern.cjs.map +1 -1
  5. package/build/cjs/reporter.cjs +57 -4
  6. package/build/cjs/reporter.cjs.map +1 -1
  7. package/build/cjs/runner.cjs +40 -6
  8. package/build/cjs/runner.cjs.map +1 -1
  9. package/build/cjs/tests/deprecate.cjs +82 -42
  10. package/build/cjs/tests/deprecate.cjs.map +1 -1
  11. package/build/cjs/tests/index.cjs +2 -0
  12. package/build/cjs/tests/index.cjs.map +1 -1
  13. package/build/cjs/tests/login.cjs +117 -0
  14. package/build/cjs/tests/login.cjs.map +1 -0
  15. package/build/cjs/tests/ping.cjs +15 -3
  16. package/build/cjs/tests/ping.cjs.map +1 -1
  17. package/build/cjs/tests/publish.cjs +3 -1
  18. package/build/cjs/tests/publish.cjs.map +1 -1
  19. package/build/cjs/tests/unpublish.cjs +1 -1
  20. package/build/cjs/tests/unpublish.cjs.map +1 -1
  21. package/build/cjs/utils/registry-client.cjs +3 -5
  22. package/build/cjs/utils/registry-client.cjs.map +1 -1
  23. package/build/esm/adapters/pnpm.js +31 -2
  24. package/build/esm/adapters/pnpm.js.map +1 -1
  25. package/build/esm/adapters/yarn-modern.js +52 -3
  26. package/build/esm/adapters/yarn-modern.js.map +1 -1
  27. package/build/esm/reporter.js +56 -5
  28. package/build/esm/reporter.js.map +1 -1
  29. package/build/esm/runner.js +41 -7
  30. package/build/esm/runner.js.map +1 -1
  31. package/build/esm/tests/deprecate.js +81 -42
  32. package/build/esm/tests/deprecate.js.map +1 -1
  33. package/build/esm/tests/index.js +2 -0
  34. package/build/esm/tests/index.js.map +1 -1
  35. package/build/esm/tests/login.js +113 -0
  36. package/build/esm/tests/login.js.map +1 -0
  37. package/build/esm/tests/ping.js +15 -3
  38. package/build/esm/tests/ping.js.map +1 -1
  39. package/build/esm/tests/publish.js +3 -1
  40. package/build/esm/tests/publish.js.map +1 -1
  41. package/build/esm/tests/unpublish.js +1 -1
  42. package/build/esm/tests/unpublish.js.map +1 -1
  43. package/build/esm/utils/registry-client.js +3 -5
  44. package/build/esm/utils/registry-client.js.map +1 -1
  45. package/build/reporter.d.ts +2 -1
  46. package/build/tests/index.d.ts +2 -1
  47. package/build/tests/login.d.ts +2 -0
  48. package/build/types.d.ts +11 -0
  49. package/build/utils/registry-client.d.ts +1 -0
  50. package/package.json +2 -1
@@ -1,3 +1,4 @@
1
+ import { appendFileSync } from "fs";
1
2
  //#region src/reporter.ts
2
3
  var COLORS = {
3
4
  reset: "\x1B[0m",
@@ -13,21 +14,31 @@ function formatDuration(ms) {
13
14
  return `${(ms / 1e3).toFixed(1)}s`;
14
15
  }
15
16
  function reportTestStart(adapterName, testName) {
16
- process.stdout.write(` ${COLORS.dim}running${COLORS.reset} ${adapterName} > ${testName}...`);
17
+ process.stdout.write(` ${COLORS.dim}running${COLORS.reset} ${adapterName} > ${testName}...\n`);
17
18
  }
18
- function reportTestResult(result) {
19
- if (result.passed) process.stdout.write(`\r ${COLORS.green}PASS${COLORS.reset} ${result.name} ${COLORS.dim}(${formatDuration(result.duration)})${COLORS.reset}\n`);
19
+ function reportTestResult(result, hasSubTests = false) {
20
+ if (result.passed) if (!hasSubTests) process.stdout.write(` ${COLORS.green}✓${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\n`);
21
+ else process.stdout.write(` ${COLORS.green}PASS${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\n`);
20
22
  else {
21
- process.stdout.write(`\r ${COLORS.red}FAIL${COLORS.reset} ${result.name} ${COLORS.dim}(${formatDuration(result.duration)})${COLORS.reset}\n`);
23
+ process.stdout.write(` ${COLORS.red}FAIL${COLORS.reset} ${result.name} ${COLORS.dim}${formatDuration(result.duration)}${COLORS.reset}\n`);
22
24
  if (result.error) {
23
25
  const lines = result.error.split("\n");
24
26
  for (const line of lines) console.log(` ${COLORS.red}${line}${COLORS.reset}`);
27
+ if (process.env.GITHUB_ACTIONS) {
28
+ const msg = result.error.split("\n")[0];
29
+ console.log(`::error title=E2E ${result.name}::${msg}`);
30
+ }
25
31
  }
26
32
  }
27
33
  }
28
34
  function reportSuiteStart(adapterName) {
29
35
  console.log(`\n${COLORS.bold}${COLORS.cyan}${adapterName}${COLORS.reset}`);
30
36
  }
37
+ function reportSubTestResult(label, passed, duration) {
38
+ const icon = passed ? `${COLORS.bold}${COLORS.green}✓${COLORS.reset}` : `${COLORS.bold}${COLORS.red}✗${COLORS.reset}`;
39
+ const dur = duration !== void 0 ? ` ${COLORS.dim}${formatDuration(duration)}${COLORS.reset}` : "";
40
+ process.stdout.write(`\n ${icon} ${COLORS.bold}${label}${COLORS.reset}${dur}\n\n`);
41
+ }
31
42
  function reportSkipped(testName) {
32
43
  console.log(` ${COLORS.yellow}SKIP${COLORS.reset} ${testName}`);
33
44
  }
@@ -49,8 +60,48 @@ function reportSummary(results) {
49
60
  }
50
61
  console.log(`\n Total: ${COLORS.green}${totalPassed} passed${COLORS.reset}, ${COLORS.red}${totalFailed} failed${COLORS.reset}, ${COLORS.yellow}${totalSkipped} skipped${COLORS.reset} ${COLORS.dim}(${formatDuration(totalDuration)})${COLORS.reset}`);
51
62
  console.log(`${COLORS.bold}${"=".repeat(50)}${COLORS.reset}\n`);
63
+ const summaryFile = process.env.GITHUB_STEP_SUMMARY;
64
+ if (summaryFile) writeGitHubSummary(summaryFile, results);
65
+ }
66
+ function writeGitHubSummary(summaryFile, results) {
67
+ const lines = [];
68
+ lines.push("## E2E Test Results\n");
69
+ for (const suite of results) {
70
+ const status = suite.failed > 0 ? "❌" : "✅";
71
+ lines.push(`### ${status} ${suite.adapter}\n`);
72
+ lines.push("| Test | Status | Duration |");
73
+ lines.push("|------|--------|----------|");
74
+ for (const test of suite.tests) {
75
+ const icon = test.passed ? "✅" : "❌";
76
+ const dur = formatDuration(test.duration);
77
+ if (test.subTests && test.subTests.length > 0) {
78
+ lines.push(`| **${test.name}** | ${icon} | ${dur} |`);
79
+ for (const sub of test.subTests) {
80
+ const subIcon = sub.passed ? "✅" : "❌";
81
+ const subDur = formatDuration(sub.duration);
82
+ const label = sub.passed ? sub.label : `${sub.label}: ${sub.error?.split("\n")[0] ?? ""}`;
83
+ lines.push(`|     ${label} | ${subIcon} | ${subDur} |`);
84
+ }
85
+ } else {
86
+ const label = test.passed ? test.name : `${test.name}: ${test.error?.split("\n")[0] ?? ""}`;
87
+ lines.push(`| ${label} | ${icon} | ${dur} |`);
88
+ }
89
+ }
90
+ lines.push("");
91
+ }
92
+ let totalPassed = 0;
93
+ let totalFailed = 0;
94
+ let totalSkipped = 0;
95
+ for (const suite of results) {
96
+ totalPassed += suite.passed;
97
+ totalFailed += suite.failed;
98
+ totalSkipped += suite.skipped;
99
+ }
100
+ const emoji = totalFailed > 0 ? "❌" : "✅";
101
+ lines.push(`${emoji} **${totalPassed} passed**, **${totalFailed} failed**, **${totalSkipped} skipped**\n`);
102
+ appendFileSync(summaryFile, lines.join("\n"));
52
103
  }
53
104
  //#endregion
54
- export { reportSkipped, reportSuiteStart, reportSummary, reportTestResult, reportTestStart };
105
+ export { reportSkipped, reportSubTestResult, reportSuiteStart, reportSummary, reportTestResult, reportTestStart };
55
106
 
56
107
  //# sourceMappingURL=reporter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.js","names":[],"sources":["../../src/reporter.ts"],"sourcesContent":["import { 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\nexport function reportTestResult(result: TestResult): void {\n if (result.passed) {\n process.stdout.write(\n `\\r ${COLORS.green}PASS${COLORS.reset} ${result.name} ${COLORS.dim}(${formatDuration(result.duration)})${COLORS.reset}\\n`\n );\n } else {\n process.stdout.write(\n `\\r ${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 }\n }\n}\n\nexport function reportSuiteStart(adapterName: string): void {\n console.log(`\\n${COLORS.bold}${COLORS.cyan}${adapterName}${COLORS.reset}`);\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"],"mappings":";AAEA,IAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,KAAK;CACL,MAAM;CACP;AAED,SAAS,eAAe,IAAoB;AAC1C,KAAI,KAAK,IAAM,QAAO,GAAG,GAAG;AAC5B,QAAO,IAAI,KAAK,KAAM,QAAQ,EAAE,CAAC;;AAGnC,SAAgB,gBAAgB,aAAqB,UAAwB;AAC3E,SAAQ,OAAO,MACb,KAAK,OAAO,IAAI,SAAS,OAAO,MAAM,GAAG,YAAY,KAAK,SAAS,KACpE;;AAGH,SAAgB,iBAAiB,QAA0B;AACzD,KAAI,OAAO,OACT,SAAQ,OAAO,MACb,OAAO,OAAO,MAAM,MAAM,OAAO,MAAM,GAAG,OAAO,KAAK,GAAG,OAAO,IAAI,GAAG,eAAe,OAAO,SAAS,CAAC,GAAG,OAAO,MAAM,IACxH;MACI;AACL,UAAQ,OAAO,MACb,OAAO,OAAO,IAAI,MAAM,OAAO,MAAM,GAAG,OAAO,KAAK,GAAG,OAAO,IAAI,GAAG,eAAe,OAAO,SAAS,CAAC,GAAG,OAAO,MAAM,IACtH;AACD,MAAI,OAAO,OAAO;GAChB,MAAM,QAAQ,OAAO,MAAM,MAAM,KAAK;AACtC,QAAK,MAAM,QAAQ,MACjB,SAAQ,IAAI,OAAO,OAAO,MAAM,OAAO,OAAO,QAAQ;;;;AAM9D,SAAgB,iBAAiB,aAA2B;AAC1D,SAAQ,IAAI,KAAK,OAAO,OAAO,OAAO,OAAO,cAAc,OAAO,QAAQ;;AAG5E,SAAgB,cAAc,UAAwB;AACpD,SAAQ,IAAI,KAAK,OAAO,OAAO,MAAM,OAAO,MAAM,GAAG,WAAW;;AAGlE,SAAgB,cAAc,SAA8B;AAC1D,SAAQ,IAAI,KAAK,OAAO,OAAO,IAAI,OAAO,GAAG,GAAG,OAAO,QAAQ;AAC/D,SAAQ,IAAI,GAAG,OAAO,KAAK,SAAS,OAAO,MAAM,IAAI;CAErD,IAAI,cAAc;CAClB,IAAI,cAAc;CAClB,IAAI,eAAe;CACnB,IAAI,gBAAgB;AAEpB,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,SAAS,MAAM,SAAS,IAAI,OAAO,MAAM,OAAO;EACtD,MAAM,OAAO,MAAM,SAAS,IAAI,MAAM;AACtC,UAAQ,IACN,KAAK,SAAS,OAAO,OAAO,MAAM,GAAG,MAAM,QAAQ,IAC9C,OAAO,QAAQ,MAAM,OAAO,SAAS,OAAO,MAAM,IAClD,OAAO,MAAM,MAAM,OAAO,SAAS,OAAO,MAAM,IAChD,OAAO,SAAS,MAAM,QAAQ,UAAU,OAAO,MAAM,GACrD,OAAO,IAAI,GAAG,eAAe,MAAM,SAAS,CAAC,GAAG,OAAO,QAC7D;AACD,iBAAe,MAAM;AACrB,iBAAe,MAAM;AACrB,kBAAgB,MAAM;AACtB,mBAAiB,MAAM;;AAGzB,SAAQ,IAAI,cAAc,OAAO,QAAQ,YAAY,SAAS,OAAO,MAAM,IACtE,OAAO,MAAM,YAAY,SAAS,OAAO,MAAM,IAC/C,OAAO,SAAS,aAAa,UAAU,OAAO,MAAM,GACpD,OAAO,IAAI,GAAG,eAAe,cAAc,CAAC,GAAG,OAAO,QAAQ;AACnE,SAAQ,IAAI,GAAG,OAAO,OAAO,IAAI,OAAO,GAAG,GAAG,OAAO,MAAM,IAAI"}
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,4 +1,4 @@
1
- import { reportSkipped, reportSuiteStart, reportSummary, reportTestResult, reportTestStart } from "./reporter.js";
1
+ import { reportSkipped, reportSubTestResult, reportSuiteStart, reportSummary, reportTestResult, reportTestStart } from "./reporter.js";
2
2
  import buildDebug from "debug";
3
3
  import { URL } from "url";
4
4
  //#region src/runner.ts
@@ -12,13 +12,41 @@ function getPort(registryUrl) {
12
12
  }
13
13
  }
14
14
  async function runSingleTest(adapter, test, registryUrl, token, timeout) {
15
+ const port = getPort(registryUrl);
16
+ let hasSubTests = false;
17
+ const subResults = [];
15
18
  const ctx = {
16
19
  registryUrl,
17
20
  token,
18
- port: getPort(registryUrl),
21
+ port,
19
22
  exec: adapter.exec.bind(adapter),
20
23
  adapter,
21
- runId: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`
24
+ runId: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`,
25
+ async subTest(label, fn) {
26
+ hasSubTests = true;
27
+ const subStart = Date.now();
28
+ try {
29
+ await fn();
30
+ const duration = Date.now() - subStart;
31
+ subResults.push({
32
+ label,
33
+ passed: true,
34
+ duration
35
+ });
36
+ reportSubTestResult(label, true, duration);
37
+ } catch (err) {
38
+ const duration = Date.now() - subStart;
39
+ const error = err instanceof Error ? err.message : String(err);
40
+ subResults.push({
41
+ label,
42
+ passed: false,
43
+ duration,
44
+ error
45
+ });
46
+ reportSubTestResult(label, false, duration);
47
+ throw err;
48
+ }
49
+ }
22
50
  };
23
51
  const start = Date.now();
24
52
  return new Promise((resolve) => {
@@ -27,7 +55,9 @@ async function runSingleTest(adapter, test, registryUrl, token, timeout) {
27
55
  name: test.name,
28
56
  passed: false,
29
57
  duration: Date.now() - start,
30
- error: `Test timed out after ${timeout}ms`
58
+ error: `Test timed out after ${timeout}ms`,
59
+ hasSubTests,
60
+ subTests: subResults.length > 0 ? subResults : void 0
31
61
  });
32
62
  }, timeout);
33
63
  test.run(ctx).then(() => {
@@ -35,7 +65,9 @@ async function runSingleTest(adapter, test, registryUrl, token, timeout) {
35
65
  resolve({
36
66
  name: test.name,
37
67
  passed: true,
38
- duration: Date.now() - start
68
+ duration: Date.now() - start,
69
+ hasSubTests,
70
+ subTests: subResults.length > 0 ? subResults : void 0
39
71
  });
40
72
  }).catch((err) => {
41
73
  clearTimeout(timer);
@@ -43,7 +75,9 @@ async function runSingleTest(adapter, test, registryUrl, token, timeout) {
43
75
  name: test.name,
44
76
  passed: false,
45
77
  duration: Date.now() - start,
46
- error: err instanceof Error ? err.message : String(err)
78
+ error: err instanceof Error ? err.message : String(err),
79
+ hasSubTests,
80
+ subTests: subResults.length > 0 ? subResults : void 0
47
81
  });
48
82
  });
49
83
  });
@@ -67,7 +101,7 @@ async function runSuite(adapter, tests, registryUrl, token, options) {
67
101
  reportTestStart(adapter.name, test.name);
68
102
  const result = await runSingleTest(adapter, test, registryUrl, token, options.timeout);
69
103
  results.push(result);
70
- reportTestResult(result);
104
+ reportTestResult(result, result.hasSubTests);
71
105
  }
72
106
  const passed = results.filter((r) => r.passed).length;
73
107
  const failed = results.filter((r) => !r.passed).length;
@@ -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, SuiteResult, TestContext, TestDefinition, TestResult } from './types';\nimport { reportSkipped, 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> {\n const port = getPort(registryUrl);\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 };\n\n const start = Date.now();\n\n return new Promise<TestResult>((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 });\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 });\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 });\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);\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":";;;;AAMA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,SAAS,QAAQ,aAA6B;AAC5C,KAAI;EACF,MAAM,MAAM,IAAI,IAAI,YAAY;AAChC,SAAO,SAAS,IAAI,MAAM,GAAG,KAAK,IAAI,aAAa,WAAW,MAAM;SAC9D;AACN,SAAO;;;AAIX,eAAe,cACb,SACA,MACA,aACA,OACA,SACqB;CAErB,MAAM,MAAmB;EACvB;EACA;EACA,MAJW,QAAQ,YAAY;EAK/B,MAAM,QAAQ,KAAK,KAAK,QAAQ;EAChC;EACA,OAAO,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;EAC3E;CAED,MAAM,QAAQ,KAAK,KAAK;AAExB,QAAO,IAAI,SAAqB,YAAY;EAC1C,MAAM,QAAQ,iBAAiB;AAC7B,WAAQ;IACN,MAAM,KAAK;IACX,QAAQ;IACR,UAAU,KAAK,KAAK,GAAG;IACvB,OAAO,wBAAwB,QAAQ;IACxC,CAAC;KACD,QAAQ;AAEX,OACG,IAAI,IAAI,CACR,WAAW;AACV,gBAAa,MAAM;AACnB,WAAQ;IACN,MAAM,KAAK;IACX,QAAQ;IACR,UAAU,KAAK,KAAK,GAAG;IACxB,CAAC;IACF,CACD,OAAO,QAAQ;AACd,gBAAa,MAAM;AACnB,WAAQ;IACN,MAAM,KAAK;IACX,QAAQ;IACR,UAAU,KAAK,KAAK,GAAG;IACvB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CAAC;IACF;GACJ;;AAGJ,eAAsB,SACpB,SACA,OACA,aACA,OACA,SACsB;CACtB,MAAM,aAAa,KAAK,KAAK;CAC7B,MAAM,UAAwB,EAAE;CAChC,IAAI,UAAU;AAEd,kBAAiB,QAAQ,KAAK;AAE9B,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,QAAQ,cAAc,QAAQ,WAAW,SAAS;OAChD,CAAC,QAAQ,WAAW,SAAS,KAAK,KAAK,CACzC;;AAKJ,MAAI,KAAK;OACa,KAAK,SAAS,QAAQ,QAAQ,CAAC,QAAQ,SAAS,IAAI,IAAI,CAAC,CAC7D,SAAS,GAAG;AAC1B,kBAAc,KAAK,KAAK;AACxB;AACA;;;AAIJ,kBAAgB,QAAQ,MAAM,KAAK,KAAK;EACxC,MAAM,SAAS,MAAM,cAAc,SAAS,MAAM,aAAa,OAAO,QAAQ,QAAQ;AACtF,UAAQ,KAAK,OAAO;AACpB,mBAAiB,OAAO;;CAG1B,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,OAAO,CAAC;CAC/C,MAAM,SAAS,QAAQ,QAAQ,MAAM,CAAC,EAAE,OAAO,CAAC;AAEhD,QAAO;EACL,SAAS,QAAQ;EACjB,OAAO;EACP;EACA;EACA;EACA,UAAU,KAAK,KAAK,GAAG;EACxB;;AAGH,eAAsB,OACpB,UACA,OACA,aACA,OACA,SACuD;AACvD,OAAM,qCAAqC,SAAS,QAAQ,MAAM,OAAO;CACzE,MAAM,UAAyB,EAAE;AAGjC,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,SAAS,MAAM,SAAS,SAAS,OAAO,aAAa,OAAO,QAAQ;AAC1E,UAAQ,KAAK,OAAO;;AAGtB,eAAc,QAAQ;AAGtB,QAAO;EAAE;EAAS,UADE,QAAQ,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,EAAE,GACvB,IAAI,IAAI;EAAG"}
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,58 +1,97 @@
1
+ import buildDebug from "debug";
2
+ import { join } from "path";
3
+ import { readFile, writeFile } from "fs/promises";
1
4
  import assert from "assert";
2
5
  //#region src/tests/deprecate.ts
6
+ var debug = buildDebug("verdaccio:e2e-cli:test:deprecate");
3
7
  async function publishPkg(ctx, tempFolder, pkgName) {
8
+ if (ctx.adapter.type === "yarn-modern") {
9
+ debug("running yarn install before publish for %s", pkgName);
10
+ await ctx.adapter.exec({ cwd: tempFolder }, "install");
11
+ }
12
+ debug("publishing %s from %s", pkgName, tempFolder);
4
13
  await ctx.adapter.exec({ cwd: tempFolder }, "publish", ...ctx.adapter.registryArg(ctx.registryUrl));
14
+ debug("published %s successfully", pkgName);
5
15
  }
6
16
  async function deprecate(ctx, tempFolder, packageVersion, message) {
7
- await ctx.adapter.exec({ cwd: tempFolder }, "deprecate", packageVersion, message, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
17
+ debug("deprecating %s with message: %s", packageVersion, message || "(empty, un-deprecate)");
18
+ const jsonFlag = ctx.adapter.type === "yarn-modern" ? [] : ["--json"];
19
+ await ctx.adapter.exec({ cwd: tempFolder }, "deprecate", packageVersion, message, ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
20
+ debug("deprecate command completed for %s", packageVersion);
8
21
  }
9
- async function getInfo(ctx, pkgName) {
10
- const resp = await ctx.adapter.exec({}, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
11
- return JSON.parse(resp.stdout);
22
+ async function getInfo(ctx, tempFolder, pkgName) {
23
+ debug("fetching info for %s", pkgName);
24
+ const resp = await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
25
+ const parsed = JSON.parse(resp.stdout);
26
+ debug("info for %s: deprecated=%s", pkgName, parsed.deprecated ?? "(not set)");
27
+ return parsed;
12
28
  }
13
29
  async function bumpVersion(ctx, tempFolder) {
14
- await ctx.adapter.exec({ cwd: tempFolder }, "version", "minor", ...ctx.adapter.registryArg(ctx.registryUrl));
30
+ debug("bumping version (minor) in %s", tempFolder);
31
+ if (ctx.adapter.type === "yarn-modern") {
32
+ const pkgPath = join(tempFolder, "package.json");
33
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
34
+ const [major, minor, patch] = pkg.version.split(".").map(Number);
35
+ pkg.version = `${major}.${minor + 1}.${patch}`;
36
+ debug("bumped %s to %s", pkgPath, pkg.version);
37
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
38
+ } else await ctx.adapter.exec({ cwd: tempFolder }, "version", "minor", ...ctx.adapter.registryArg(ctx.registryUrl));
39
+ }
40
+ async function prepareDeprecateProject(ctx, pkgName, version) {
41
+ debug("preparing project for %s@%s", pkgName, version);
42
+ const { tempFolder } = await ctx.adapter.prepareProject(pkgName, version, ctx.registryUrl, ctx.port, ctx.token);
43
+ if (ctx.adapter.importPlugin) {
44
+ debug("importing npm-deprecate plugin into %s", tempFolder);
45
+ await ctx.adapter.importPlugin(tempFolder, "npm-deprecate");
46
+ }
47
+ return tempFolder;
15
48
  }
16
49
  async function testDeprecate(ctx) {
17
50
  const id = ctx.runId;
18
- const pkgName1 = `@verdaccio/dep1-${id}`;
19
51
  const message = "some message";
20
- const { tempFolder: tf1 } = await ctx.adapter.prepareProject(pkgName1, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
21
- await publishPkg(ctx, tf1, pkgName1);
22
- await deprecate(ctx, tf1, `${pkgName1}@1.0.0`, message);
23
- const info1 = await getInfo(ctx, pkgName1);
24
- assert.strictEqual(info1.name, pkgName1);
25
- assert.strictEqual(info1.deprecated, message, "Package should be deprecated");
26
- const pkgName2 = `@verdaccio/dep2-${id}`;
27
- const { tempFolder: tf2 } = await ctx.adapter.prepareProject(pkgName2, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
28
- await publishPkg(ctx, tf2, pkgName2);
29
- await deprecate(ctx, tf2, `${pkgName2}@1.0.0`, message);
30
- const info2a = await getInfo(ctx, pkgName2);
31
- assert.strictEqual(info2a.deprecated, message);
32
- await deprecate(ctx, tf2, `${pkgName2}@1.0.0`, "");
33
- const info2b = await getInfo(ctx, pkgName2);
34
- assert.strictEqual(info2b.deprecated, void 0, "Package should be un-deprecated");
35
- const pkgName3 = `@verdaccio/dep3-${id}`;
36
- const { tempFolder: tf3 } = await ctx.adapter.prepareProject(pkgName3, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
37
- await publishPkg(ctx, tf3, pkgName3);
38
- for (let i = 0; i < 3; i++) {
39
- await bumpVersion(ctx, tf3);
40
- await publishPkg(ctx, tf3, pkgName3);
41
- }
42
- await deprecate(ctx, tf3, pkgName3, message);
43
- for (const v of [
44
- "1.0.0",
45
- "1.1.0",
46
- "1.2.0",
47
- "1.3.0"
48
- ]) {
49
- const info = await getInfo(ctx, `${pkgName3}@${v}`);
50
- assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);
51
- }
52
- await bumpVersion(ctx, tf3);
53
- await publishPkg(ctx, tf3, pkgName3);
54
- const info3 = await getInfo(ctx, `${pkgName3}@1.4.0`);
55
- assert.strictEqual(info3.deprecated, void 0, "New version should not be deprecated");
52
+ await ctx.subTest("deprecate single version", async () => {
53
+ const pkgName = `@verdaccio/dep1-${id}`;
54
+ const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
55
+ await publishPkg(ctx, tf, pkgName);
56
+ await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);
57
+ const info = await getInfo(ctx, tf, pkgName);
58
+ assert.strictEqual(info.name, pkgName);
59
+ assert.strictEqual(info.deprecated, message, "Package should be deprecated");
60
+ });
61
+ await ctx.subTest("un-deprecate", async () => {
62
+ const pkgName = `@verdaccio/dep2-${id}`;
63
+ const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
64
+ await publishPkg(ctx, tf, pkgName);
65
+ await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);
66
+ const info1 = await getInfo(ctx, tf, pkgName);
67
+ assert.strictEqual(info1.deprecated, message);
68
+ await deprecate(ctx, tf, `${pkgName}@1.0.0`, "");
69
+ const info2 = await getInfo(ctx, tf, pkgName);
70
+ assert.strictEqual(info2.deprecated, void 0, "Package should be un-deprecated");
71
+ });
72
+ await ctx.subTest("deprecate multiple versions", async () => {
73
+ const pkgName = `@verdaccio/dep3-${id}`;
74
+ const tf = await prepareDeprecateProject(ctx, pkgName, "1.0.0");
75
+ await publishPkg(ctx, tf, pkgName);
76
+ for (let i = 0; i < 3; i++) {
77
+ await bumpVersion(ctx, tf);
78
+ await publishPkg(ctx, tf, pkgName);
79
+ }
80
+ await deprecate(ctx, tf, pkgName, message);
81
+ for (const v of [
82
+ "1.0.0",
83
+ "1.1.0",
84
+ "1.2.0",
85
+ "1.3.0"
86
+ ]) {
87
+ const info = await getInfo(ctx, tf, `${pkgName}@${v}`);
88
+ assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);
89
+ }
90
+ await bumpVersion(ctx, tf);
91
+ await publishPkg(ctx, tf, pkgName);
92
+ const info = await getInfo(ctx, tf, `${pkgName}@1.4.0`);
93
+ assert.strictEqual(info.deprecated, void 0, "New version should not be deprecated");
94
+ });
56
95
  }
57
96
  var deprecateTest = {
58
97
  name: "deprecate",
@@ -1 +1 @@
1
- {"version":3,"file":"deprecate.js","names":[],"sources":["../../../src/tests/deprecate.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, pkgName: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function deprecate(\n ctx: TestContext,\n tempFolder: string,\n packageVersion: string,\n message: string\n) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'deprecate',\n packageVersion,\n message,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function getInfo(ctx: TestContext, pkgName: string) {\n const resp = await ctx.adapter.exec(\n {},\n 'info',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n return JSON.parse(resp.stdout);\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testDeprecate(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: deprecate a single version\n const pkgName1 = `@verdaccio/dep1-${id}`;\n const message = 'some message';\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 publishPkg(ctx, tf1, pkgName1);\n await deprecate(ctx, tf1, `${pkgName1}@1.0.0`, message);\n const info1 = await getInfo(ctx, pkgName1);\n assert.strictEqual(info1.name, pkgName1);\n assert.strictEqual(info1.deprecated, message, 'Package should be deprecated');\n\n // Test 2: un-deprecate\n const pkgName2 = `@verdaccio/dep2-${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 publishPkg(ctx, tf2, pkgName2);\n await deprecate(ctx, tf2, `${pkgName2}@1.0.0`, message);\n const info2a = await getInfo(ctx, pkgName2);\n assert.strictEqual(info2a.deprecated, message);\n // empty string = undeprecate\n await deprecate(ctx, tf2, `${pkgName2}@1.0.0`, '');\n const info2b = await getInfo(ctx, pkgName2);\n assert.strictEqual(info2b.deprecated, undefined, 'Package should be un-deprecated');\n\n // Test 3: deprecate multiple versions\n const pkgName3 = `@verdaccio/dep3-${id}`;\n const { tempFolder: tf3 } = await ctx.adapter.prepareProject(\n pkgName3,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf3, pkgName3);\n for (let i = 0; i < 3; i++) {\n await bumpVersion(ctx, tf3);\n await publishPkg(ctx, tf3, pkgName3);\n }\n await deprecate(ctx, tf3, pkgName3, message);\n for (const v of ['1.0.0', '1.1.0', '1.2.0', '1.3.0']) {\n const info = await getInfo(ctx, `${pkgName3}@${v}`);\n assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);\n }\n // publish a new version after deprecation — should NOT be deprecated\n await bumpVersion(ctx, tf3);\n await publishPkg(ctx, tf3, pkgName3);\n const info3 = await getInfo(ctx, `${pkgName3}@1.4.0`);\n assert.strictEqual(info3.deprecated, undefined, 'New version should not be deprecated');\n}\n\nexport const deprecateTest: TestDefinition = {\n name: 'deprecate',\n requires: ['deprecate'],\n run: testDeprecate,\n};\n"],"mappings":";;AAIA,eAAe,WAAW,KAAkB,YAAoB,SAAiB;AAC/E,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;;AAGH,eAAe,UACb,KACA,YACA,gBACA,SACA;AACA,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,aACA,gBACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;;AAGH,eAAe,QAAQ,KAAkB,SAAiB;CACxD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,EACF,QACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;AACD,QAAO,KAAK,MAAM,KAAK,OAAO;;AAGhC,eAAe,YAAY,KAAkB,YAAoB;AAC/D,OAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,SACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;;AAGH,eAAe,cAAc,KAAiC;CAC5D,MAAM,KAAK,IAAI;CAGf,MAAM,WAAW,mBAAmB;CACpC,MAAM,UAAU;CAChB,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,WAAW,KAAK,KAAK,SAAS;AACpC,OAAM,UAAU,KAAK,KAAK,GAAG,SAAS,SAAS,QAAQ;CACvD,MAAM,QAAQ,MAAM,QAAQ,KAAK,SAAS;AAC1C,QAAO,YAAY,MAAM,MAAM,SAAS;AACxC,QAAO,YAAY,MAAM,YAAY,SAAS,+BAA+B;CAG7E,MAAM,WAAW,mBAAmB;CACpC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,WAAW,KAAK,KAAK,SAAS;AACpC,OAAM,UAAU,KAAK,KAAK,GAAG,SAAS,SAAS,QAAQ;CACvD,MAAM,SAAS,MAAM,QAAQ,KAAK,SAAS;AAC3C,QAAO,YAAY,OAAO,YAAY,QAAQ;AAE9C,OAAM,UAAU,KAAK,KAAK,GAAG,SAAS,SAAS,GAAG;CAClD,MAAM,SAAS,MAAM,QAAQ,KAAK,SAAS;AAC3C,QAAO,YAAY,OAAO,YAAY,KAAA,GAAW,kCAAkC;CAGnF,MAAM,WAAW,mBAAmB;CACpC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;AACD,OAAM,WAAW,KAAK,KAAK,SAAS;AACpC,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,WAAW,KAAK,KAAK,SAAS;;AAEtC,OAAM,UAAU,KAAK,KAAK,UAAU,QAAQ;AAC5C,MAAK,MAAM,KAAK;EAAC;EAAS;EAAS;EAAS;EAAQ,EAAE;EACpD,MAAM,OAAO,MAAM,QAAQ,KAAK,GAAG,SAAS,GAAG,IAAI;AACnD,SAAO,YAAY,KAAK,YAAY,SAAS,WAAW,EAAE,uBAAuB;;AAGnF,OAAM,YAAY,KAAK,IAAI;AAC3B,OAAM,WAAW,KAAK,KAAK,SAAS;CACpC,MAAM,QAAQ,MAAM,QAAQ,KAAK,GAAG,SAAS,QAAQ;AACrD,QAAO,YAAY,MAAM,YAAY,KAAA,GAAW,uCAAuC;;AAGzF,IAAa,gBAAgC;CAC3C,MAAM;CACN,UAAU,CAAC,YAAY;CACvB,KAAK;CACN"}
1
+ {"version":3,"file":"deprecate.js","names":[],"sources":["../../../src/tests/deprecate.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:deprecate');\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, pkgName: string) {\n if (ctx.adapter.type === 'yarn-modern') {\n debug('running yarn install before publish for %s', pkgName);\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n debug('publishing %s from %s', pkgName, tempFolder);\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published %s successfully', pkgName);\n}\n\nasync function deprecate(\n ctx: TestContext,\n tempFolder: string,\n packageVersion: string,\n message: string\n) {\n debug('deprecating %s with message: %s', packageVersion, message || '(empty, un-deprecate)');\n const jsonFlag = ctx.adapter.type === 'yarn-modern' ? [] : ['--json'];\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'deprecate',\n packageVersion,\n message,\n ...jsonFlag,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('deprecate command completed for %s', packageVersion);\n}\n\nasync function getInfo(ctx: TestContext, tempFolder: string, pkgName: string) {\n debug('fetching info for %s', pkgName);\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'info',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const parsed = JSON.parse(resp.stdout);\n debug('info for %s: deprecated=%s', pkgName, parsed.deprecated ?? '(not set)');\n return parsed;\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n debug('bumping version (minor) in %s', tempFolder);\n if (ctx.adapter.type === 'yarn-modern') {\n // Bump version by editing package.json directly to avoid\n // requiring the version plugin (not bundled in Yarn 3)\n const pkgPath = join(tempFolder, 'package.json');\n const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));\n const [major, minor, patch] = pkg.version.split('.').map(Number);\n pkg.version = `${major}.${minor + 1}.${patch}`;\n debug('bumped %s to %s', pkgPath, pkg.version);\n await writeFile(pkgPath, JSON.stringify(pkg, null, 2));\n } else {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n }\n}\n\nasync function prepareDeprecateProject(\n ctx: TestContext,\n pkgName: string,\n version: string\n): Promise<string> {\n debug('preparing project for %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 );\n if (ctx.adapter.importPlugin) {\n debug('importing npm-deprecate plugin into %s', tempFolder);\n await ctx.adapter.importPlugin(tempFolder, 'npm-deprecate');\n }\n return tempFolder;\n}\n\nasync function testDeprecate(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n const message = 'some message';\n\n await ctx.subTest('deprecate single version', async () => {\n const pkgName = `@verdaccio/dep1-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);\n const info = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info.name, pkgName);\n assert.strictEqual(info.deprecated, message, 'Package should be deprecated');\n });\n\n await ctx.subTest('un-deprecate', async () => {\n const pkgName = `@verdaccio/dep2-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, message);\n const info1 = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info1.deprecated, message);\n await deprecate(ctx, tf, `${pkgName}@1.0.0`, '');\n const info2 = await getInfo(ctx, tf, pkgName);\n assert.strictEqual(info2.deprecated, undefined, 'Package should be un-deprecated');\n });\n\n await ctx.subTest('deprecate multiple versions', async () => {\n const pkgName = `@verdaccio/dep3-${id}`;\n const tf = await prepareDeprecateProject(ctx, pkgName, '1.0.0');\n await publishPkg(ctx, tf, pkgName);\n for (let i = 0; i < 3; i++) {\n await bumpVersion(ctx, tf);\n await publishPkg(ctx, tf, pkgName);\n }\n await deprecate(ctx, tf, pkgName, message);\n for (const v of ['1.0.0', '1.1.0', '1.2.0', '1.3.0']) {\n const info = await getInfo(ctx, tf, `${pkgName}@${v}`);\n assert.strictEqual(info.deprecated, message, `Version ${v} should be deprecated`);\n }\n await bumpVersion(ctx, tf);\n await publishPkg(ctx, tf, pkgName);\n const info = await getInfo(ctx, tf, `${pkgName}@1.4.0`);\n assert.strictEqual(info.deprecated, undefined, 'New version should not be deprecated');\n });\n}\n\nexport const deprecateTest: TestDefinition = {\n name: 'deprecate',\n requires: ['deprecate'],\n run: testDeprecate,\n};\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { deprecateTest } from "./deprecate.js";
3
3
  import { distTagsTest } from "./dist-tags.js";
4
4
  import { infoTest } from "./info.js";
5
5
  import { installTest } from "./install.js";
6
+ import { loginTest } from "./login.js";
6
7
  import { pingTest } from "./ping.js";
7
8
  import { publishTest } from "./publish.js";
8
9
  import { searchTest } from "./search.js";
@@ -16,6 +17,7 @@ var allTests = [
16
17
  infoTest,
17
18
  deprecateTest,
18
19
  distTagsTest,
20
+ loginTest,
19
21
  pingTest,
20
22
  searchTest,
21
23
  starTest,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import { TestDefinition } from '../types';\n\nimport { auditTest } from './audit';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\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 auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n];\n\nexport {\n publishTest,\n installTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n};\n"],"mappings":";;;;;;;;;;;AAaA,IAAa,WAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import { TestDefinition } from '../types';\n\nimport { auditTest } from './audit';\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 auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n];\n\nexport {\n publishTest,\n installTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n starTest,\n unpublishTest,\n};\n"],"mappings":";;;;;;;;;;;;AAcA,IAAa,WAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
@@ -0,0 +1,113 @@
1
+ import { createTempFolder, getPackageJSON, getREADME } from "../utils/project.js";
2
+ import buildDebug from "debug";
3
+ import { join } from "path";
4
+ import { writeFile } from "fs/promises";
5
+ import YAML from "js-yaml";
6
+ import assert from "assert";
7
+ //#region src/tests/login.ts
8
+ var debug = buildDebug("verdaccio:e2e-cli:test:login");
9
+ /**
10
+ * Creates a project with registry config but WITHOUT auth token,
11
+ * so whoami will use the token saved by login to ~/.yarnrc.yml.
12
+ */
13
+ async function prepareLoginProject(ctx, name) {
14
+ debug("preparing project for %s (no auth token)", name);
15
+ const tempFolder = await createTempFolder(name);
16
+ const yamlContent = YAML.dump({
17
+ npmRegistryServer: ctx.registryUrl,
18
+ enableImmutableInstalls: false,
19
+ unsafeHttpWhitelist: ["localhost"]
20
+ });
21
+ await writeFile(join(tempFolder, ".yarnrc.yml"), yamlContent);
22
+ await writeFile(join(tempFolder, "package.json"), JSON.stringify({
23
+ name,
24
+ version: "1.0.0"
25
+ }));
26
+ if (ctx.adapter.importPlugin) {
27
+ debug("importing npm-login plugin into %s", tempFolder);
28
+ await ctx.adapter.importPlugin(tempFolder, "npm-login");
29
+ }
30
+ return tempFolder;
31
+ }
32
+ async function yarnLogin(ctx, cwd, user, password, email) {
33
+ debug("yarn npm login as %s", user);
34
+ return ctx.adapter.exec({ cwd }, "login", "--auth-type=legacy", `--user=${user}`, `--password=${password}`, `--email=${email}`);
35
+ }
36
+ async function yarnWhoami(ctx, cwd) {
37
+ const resp = await ctx.adapter.exec({ cwd }, "whoami");
38
+ debug("yarn npm whoami: %s", resp.stdout);
39
+ return resp.stdout;
40
+ }
41
+ async function testLogin(ctx) {
42
+ const type = ctx.adapter.type;
43
+ const id = ctx.runId;
44
+ if (type !== "yarn-modern") {
45
+ debug("login test only supported for yarn-modern, skipping %s", type);
46
+ return;
47
+ }
48
+ const user1 = `login-a-${id}`;
49
+ const password1 = "test-password-123";
50
+ const email1 = `${user1}@test.example.com`;
51
+ await ctx.subTest("create new user + whoami", async () => {
52
+ const tf = await prepareLoginProject(ctx, `verdaccio-login1-${id}`);
53
+ const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);
54
+ assert.ok(loginResp.stdout.includes("Logged in") || loginResp.stdout.includes("token saved"), `Expected login success message but got "${loginResp.stdout}"`);
55
+ const who = await yarnWhoami(ctx, tf);
56
+ assert.ok(who.includes(user1), `Expected whoami "${user1}" but got "${who}"`);
57
+ });
58
+ await ctx.subTest("login existing user", async () => {
59
+ const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);
60
+ const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);
61
+ assert.ok(loginResp.stdout.includes("Logged in") || loginResp.stdout.includes("token saved"), `Expected login success for existing user but got "${loginResp.stdout}"`);
62
+ const who = await yarnWhoami(ctx, tf);
63
+ assert.ok(who.includes(user1), `Expected "${user1}" but got "${who}"`);
64
+ });
65
+ await ctx.subTest("wrong password fails", async () => {
66
+ const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);
67
+ let loginFailed = false;
68
+ try {
69
+ await yarnLogin(ctx, tf, user1, "wrong-password", email1);
70
+ } catch {
71
+ loginFailed = true;
72
+ }
73
+ assert.ok(loginFailed, "Login with wrong password should have failed");
74
+ });
75
+ await ctx.subTest("login then publish", async () => {
76
+ const pkgName = `@verdaccio/login-pub-${id}`;
77
+ const tf = await createTempFolder(`login-pub-${id}`);
78
+ const yamlContent = YAML.dump({
79
+ npmRegistryServer: ctx.registryUrl,
80
+ enableImmutableInstalls: false,
81
+ unsafeHttpWhitelist: ["localhost"]
82
+ });
83
+ await writeFile(join(tf, ".yarnrc.yml"), yamlContent);
84
+ await writeFile(join(tf, "package.json"), getPackageJSON(pkgName, "1.0.0"));
85
+ await writeFile(join(tf, "README.md"), getREADME(pkgName));
86
+ if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tf, "npm-login");
87
+ await yarnLogin(ctx, tf, user1, password1, email1);
88
+ await ctx.adapter.exec({ cwd: tf }, "install");
89
+ const pubResp = await ctx.adapter.exec({ cwd: tf }, "publish");
90
+ assert.ok(pubResp.stdout.includes("Package archive published"), `Expected publish success but got "${pubResp.stdout}"`);
91
+ });
92
+ await ctx.subTest("switch users", async () => {
93
+ const user2 = `login-b-${id}`;
94
+ const password2 = "other-password-456";
95
+ const email2 = `${user2}@test.example.com`;
96
+ const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);
97
+ await yarnLogin(ctx, tf, user1, password1, email1);
98
+ const who1 = await yarnWhoami(ctx, tf);
99
+ assert.ok(who1.includes(user1), `Expected "${user1}" but got "${who1}"`);
100
+ await yarnLogin(ctx, tf, user2, password2, email2);
101
+ const who2 = await yarnWhoami(ctx, tf);
102
+ assert.ok(who2.includes(user2), `Expected "${user2}" after switch but got "${who2}"`);
103
+ });
104
+ }
105
+ var loginTest = {
106
+ name: "login",
107
+ requires: ["login"],
108
+ run: testLogin
109
+ };
110
+ //#endregion
111
+ export { loginTest };
112
+
113
+ //# sourceMappingURL=login.js.map
@@ -0,0 +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,9 +1,21 @@
1
1
  import assert from "assert";
2
2
  //#region src/tests/ping.ts
3
3
  async function testPing(ctx) {
4
- const resp = await ctx.adapter.exec({}, "ping", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
5
- const parsedBody = JSON.parse(resp.stdout);
6
- assert.strictEqual(parsedBody.registry, ctx.registryUrl + "/", `Expected registry "${ctx.registryUrl}/" but got "${parsedBody.registry}"`);
4
+ const type = ctx.adapter.type;
5
+ const needsProject = type === "yarn-modern";
6
+ let cwd;
7
+ if (needsProject) {
8
+ const { tempFolder } = await ctx.adapter.prepareProject(`verdaccio-ping-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
9
+ cwd = tempFolder;
10
+ if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tempFolder, "npm-ping");
11
+ }
12
+ const jsonFlag = type === "yarn-modern" ? [] : ["--json"];
13
+ const resp = await ctx.adapter.exec({ cwd }, "ping", ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
14
+ if (type === "yarn-modern") assert.ok(true, "ping succeeded");
15
+ else {
16
+ const parsedBody = JSON.parse(resp.stdout);
17
+ assert.strictEqual(parsedBody.registry, ctx.registryUrl + "/", `Expected registry "${ctx.registryUrl}/" but got "${parsedBody.registry}"`);
18
+ }
7
19
  }
8
20
  var pingTest = {
9
21
  name: "ping",