@verdaccio/e2e-cli 2.10.1 → 2.10.3
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.
- package/build/cjs/scenarios/install-multiple-deps.cjs +3 -2
- package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -1
- package/build/cjs/tests/deprecate.cjs +2 -2
- package/build/cjs/tests/deprecate.cjs.map +1 -1
- package/build/cjs/tests/info.cjs +2 -1
- package/build/cjs/tests/info.cjs.map +1 -1
- package/build/cjs/tests/login.cjs +17 -8
- package/build/cjs/tests/login.cjs.map +1 -1
- package/build/cjs/utils/info.cjs +25 -0
- package/build/cjs/utils/info.cjs.map +1 -0
- package/build/cjs/utils/project.cjs +1 -1
- package/build/cjs/utils/project.cjs.map +1 -1
- package/build/esm/scenarios/install-multiple-deps.js +3 -2
- package/build/esm/scenarios/install-multiple-deps.js.map +1 -1
- package/build/esm/tests/deprecate.js +2 -2
- package/build/esm/tests/deprecate.js.map +1 -1
- package/build/esm/tests/info.js +2 -1
- package/build/esm/tests/info.js.map +1 -1
- package/build/esm/tests/login.js +17 -8
- package/build/esm/tests/login.js.map +1 -1
- package/build/esm/utils/info.js +24 -0
- package/build/esm/utils/info.js.map +1 -0
- package/build/esm/utils/project.js +1 -1
- package/build/esm/utils/project.js.map +1 -1
- package/build/utils/index.d.ts +1 -0
- package/build/utils/info.d.ts +14 -0
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_info = require("../utils/info.cjs");
|
|
2
3
|
let debug = require("debug");
|
|
3
4
|
debug = require_runtime.__toESM(debug, 1);
|
|
4
5
|
let assert = require("assert");
|
|
@@ -30,9 +31,9 @@ function parseInfoOutput(stdout, adapterType) {
|
|
|
30
31
|
const obj = JSON.parse(line);
|
|
31
32
|
if (obj.type === "inspect" && obj.data) return obj.data;
|
|
32
33
|
} catch {}
|
|
33
|
-
return JSON.parse(stdout);
|
|
34
|
+
return require_info.normalizeInfo(JSON.parse(stdout));
|
|
34
35
|
}
|
|
35
|
-
return JSON.parse(stdout);
|
|
36
|
+
return require_info.normalizeInfo(JSON.parse(stdout));
|
|
36
37
|
}
|
|
37
38
|
async function publishSeedPackage(ctx, pkgName, version, dependencies = {}) {
|
|
38
39
|
debug$1("publishing seed package %s@%s", pkgName, version);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-multiple-deps.cjs","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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"}
|
|
1
|
+
{"version":3,"file":"install-multiple-deps.cjs","names":[],"sources":["../../../src/scenarios/install-multiple-deps.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { normalizeInfo } from '../utils/info';\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 normalizeInfo(JSON.parse(stdout));\n }\n return normalizeInfo(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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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_info = require("../utils/info.cjs");
|
|
2
3
|
let debug = require("debug");
|
|
3
4
|
debug = require_runtime.__toESM(debug, 1);
|
|
4
5
|
let path = require("path");
|
|
@@ -24,8 +25,7 @@ async function deprecate(ctx, tempFolder, packageVersion, message) {
|
|
|
24
25
|
}
|
|
25
26
|
async function getInfo(ctx, tempFolder, pkgName) {
|
|
26
27
|
debug$1("fetching info for %s", pkgName);
|
|
27
|
-
const
|
|
28
|
-
const parsed = JSON.parse(resp.stdout);
|
|
28
|
+
const parsed = require_info.parseInfoJson((await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl))).stdout);
|
|
29
29
|
debug$1("info for %s: deprecated=%s", pkgName, parsed.deprecated ?? "(not set)");
|
|
30
30
|
return parsed;
|
|
31
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deprecate.cjs","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 =
|
|
1
|
+
{"version":3,"file":"deprecate.cjs","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';\nimport { parseInfoJson } from '../utils/info';\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 = parseInfoJson(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":";;;;;;;;;AAQA,IAAM,WAAA,GAAA,MAAA,SAAmB,mCAAmC;AAE5D,eAAe,WAAW,KAAkB,YAAoB,SAAiB;CAC/E,IAAI,IAAI,QAAQ,SAAS,eAAe;EACtC,QAAM,8CAA8C,QAAQ;EAC5D,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,YAAY,EAAE,UAAU;;CAExD,QAAM,yBAAyB,SAAS,WAAW;CACnD,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;CACD,QAAM,6BAA6B,QAAQ;;AAG7C,eAAe,UACb,KACA,YACA,gBACA,SACA;CACA,QAAM,mCAAmC,gBAAgB,WAAW,wBAAwB;CAC5F,MAAM,WAAW,IAAI,QAAQ,SAAS,gBAAgB,EAAE,GAAG,CAAC,SAAS;CACrE,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,aACA,gBACA,SACA,GAAG,UACH,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;CACD,QAAM,sCAAsC,eAAe;;AAG7D,eAAe,QAAQ,KAAkB,YAAoB,SAAiB;CAC5E,QAAM,wBAAwB,QAAQ;CAQtC,MAAM,SAAS,aAAA,eAAc,MAPV,IAAI,QAAQ,KAC7B,EAAE,KAAK,YAAY,EACnB,QACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C,EACiC,OAAO;CACzC,QAAM,8BAA8B,SAAS,OAAO,cAAc,YAAY;CAC9E,OAAO;;AAGT,eAAe,YAAY,KAAkB,YAAoB;CAC/D,QAAM,iCAAiC,WAAW;CAClD,IAAI,IAAI,QAAQ,SAAS,eAAe;EAGtC,MAAM,WAAA,GAAA,KAAA,MAAe,YAAY,eAAe;EAChD,MAAM,MAAM,KAAK,MAAM,OAAA,GAAA,YAAA,UAAe,SAAS,OAAO,CAAC;EACvD,MAAM,CAAC,OAAO,OAAO,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;EAChE,IAAI,UAAU,GAAG,MAAM,GAAG,QAAQ,EAAE,GAAG;EACvC,QAAM,mBAAmB,SAAS,IAAI,QAAQ;EAC9C,OAAA,GAAA,YAAA,WAAgB,SAAS,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC;QAEtD,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,YAAY,EACnB,WACA,SACA,GAAG,IAAI,QAAQ,YAAY,IAAI,YAAY,CAC5C;;AAIL,eAAe,wBACb,KACA,SACA,SACiB;CACjB,QAAM,+BAA+B,SAAS,QAAQ;CACtD,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,MACL;CACD,IAAI,IAAI,QAAQ,cAAc;EAC5B,QAAM,0CAA0C,WAAW;EAC3D,MAAM,IAAI,QAAQ,aAAa,YAAY,gBAAgB;;CAE7D,OAAO;;AAGT,eAAe,cAAc,KAAiC;CAC5D,MAAM,KAAK,IAAI;CAEf,MAAM,UAAU;CAEhB,MAAM,IAAI,QAAQ,4BAA4B,YAAY;EACxD,MAAM,UAAU,mBAAmB;EACnC,MAAM,KAAK,MAAM,wBAAwB,KAAK,SAAS,QAAQ;EAC/D,MAAM,WAAW,KAAK,IAAI,QAAQ;EAClC,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,SAAS,QAAQ;EACrD,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ;EAC5C,OAAA,QAAO,YAAY,KAAK,MAAM,QAAQ;EACtC,OAAA,QAAO,YAAY,KAAK,YAAY,SAAS,+BAA+B;GAC5E;CAEF,MAAM,IAAI,QAAQ,gBAAgB,YAAY;EAC5C,MAAM,UAAU,mBAAmB;EACnC,MAAM,KAAK,MAAM,wBAAwB,KAAK,SAAS,QAAQ;EAC/D,MAAM,WAAW,KAAK,IAAI,QAAQ;EAClC,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,SAAS,QAAQ;EACrD,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ;EAC7C,OAAA,QAAO,YAAY,MAAM,YAAY,QAAQ;EAC7C,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,SAAS,GAAG;EAChD,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ;EAC7C,OAAA,QAAO,YAAY,MAAM,YAAY,KAAA,GAAW,kCAAkC;GAClF;CAEF,MAAM,IAAI,QAAQ,+BAA+B,YAAY;EAC3D,MAAM,UAAU,mBAAmB;EACnC,MAAM,KAAK,MAAM,wBAAwB,KAAK,SAAS,QAAQ;EAC/D,MAAM,WAAW,KAAK,IAAI,QAAQ;EAClC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,MAAM,YAAY,KAAK,GAAG;GAC1B,MAAM,WAAW,KAAK,IAAI,QAAQ;;EAEpC,MAAM,UAAU,KAAK,IAAI,SAAS,QAAQ;EAC1C,KAAK,MAAM,KAAK;GAAC;GAAS;GAAS;GAAS;GAAQ,EAAE;GACpD,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,GAAG,IAAI;GACtD,OAAA,QAAO,YAAY,KAAK,YAAY,SAAS,WAAW,EAAE,uBAAuB;;EAEnF,MAAM,YAAY,KAAK,GAAG;EAC1B,MAAM,WAAW,KAAK,IAAI,QAAQ;EAClC,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,QAAQ;EACvD,OAAA,QAAO,YAAY,KAAK,YAAY,KAAA,GAAW,uCAAuC;GACtF;;AAGJ,IAAa,gBAAgC;CAC3C,MAAM;CACN,UAAU,CAAC,YAAY;CACvB,KAAK;CACN"}
|
package/build/cjs/tests/info.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_project = require("../utils/project.cjs");
|
|
3
|
+
const require_info = require("../utils/info.cjs");
|
|
3
4
|
let path = require("path");
|
|
4
5
|
let fs_promises = require("fs/promises");
|
|
5
6
|
let assert = require("assert");
|
|
@@ -25,7 +26,7 @@ async function testInfo(ctx) {
|
|
|
25
26
|
const output = resp.stdout + resp.stderr;
|
|
26
27
|
assert.default.ok(output.includes("verdaccio"), "Expected deno info output to reference verdaccio");
|
|
27
28
|
} else {
|
|
28
|
-
const parsedBody =
|
|
29
|
+
const parsedBody = require_info.parseInfoJson(resp.stdout);
|
|
29
30
|
assert.default.strictEqual(parsedBody.name, "verdaccio", "Expected package name \"verdaccio\"");
|
|
30
31
|
assert.default.ok(parsedBody.dependencies !== void 0, "Expected \"dependencies\" to be defined");
|
|
31
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"info.cjs","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 =
|
|
1
|
+
{"version":3,"file":"info.cjs","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 { parseInfoJson } from '../utils/info';\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 = parseInfoJson(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":";;;;;;;;AAQA,eAAe,SAAS,KAAiC;CAGvD,MAAM,aAAa,MAAM,gBAAA,iBAAiB,YAAY;CACtD,OAAA,GAAA,YAAA,YAAA,GAAA,KAAA,MACO,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;CAED,IAAI,IAAI,QAAQ,SAAS,gBAAgB;EAEvC,MAAM,WADQ,KAAK,OAAO,MAAM,KAAK,CAAC,OAAO,QAC5B,CAAM,MAAM,MAAM;GACjC,IAAI;IAEF,OADY,KAAK,MAAM,EAChB,CAAI,SAAS;WACd;IACN,OAAO;;IAET;EACF,OAAA,QAAO,GAAG,UAAU,4DAA0D;QACzE,IAAI,IAAI,QAAQ,SAAS,QAAQ;EAEtC,MAAM,SAAS,KAAK,SAAS,KAAK;EAClC,OAAA,QAAO,GAAG,OAAO,SAAS,YAAY,EAAE,mDAAmD;QACtF;EACL,MAAM,aAAa,aAAA,cAAc,KAAK,OAAO;EAC7C,OAAA,QAAO,YAAY,WAAW,MAAM,aAAa,sCAAoC;EACrF,OAAA,QAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,0CAAwC;;;AAI7F,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
|
|
@@ -59,12 +59,15 @@ async function testLogin(ctx) {
|
|
|
59
59
|
const who = await yarnWhoami(ctx, tf);
|
|
60
60
|
assert.default.ok(who.includes(user1), `Expected whoami "${user1}" but got "${who}"`);
|
|
61
61
|
});
|
|
62
|
-
await ctx.subTest("login
|
|
62
|
+
await ctx.subTest("legacy re-login is rejected without basic auth", async () => {
|
|
63
63
|
const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
let loginFailed = false;
|
|
65
|
+
try {
|
|
66
|
+
await yarnLogin(ctx, tf, user1, password1, email1);
|
|
67
|
+
} catch {
|
|
68
|
+
loginFailed = true;
|
|
69
|
+
}
|
|
70
|
+
assert.default.ok(loginFailed, "Legacy re-login for an existing user should fail without Basic auth");
|
|
68
71
|
});
|
|
69
72
|
await ctx.subTest("wrong password fails", async () => {
|
|
70
73
|
const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);
|
|
@@ -78,6 +81,9 @@ async function testLogin(ctx) {
|
|
|
78
81
|
});
|
|
79
82
|
await ctx.subTest("login then publish", async () => {
|
|
80
83
|
const pkgName = `@verdaccio/login-pub-${id}`;
|
|
84
|
+
const publishUser = `login-pub-${id}`;
|
|
85
|
+
const publishPassword = "publish-password-123";
|
|
86
|
+
const publishEmail = `${publishUser}@test.example.com`;
|
|
81
87
|
const tf = await require_project.createTempFolder(`login-pub-${id}`);
|
|
82
88
|
const yamlContent = js_yaml.default.dump({
|
|
83
89
|
npmRegistryServer: ctx.registryUrl,
|
|
@@ -88,19 +94,22 @@ async function testLogin(ctx) {
|
|
|
88
94
|
await (0, fs_promises.writeFile)((0, path.join)(tf, "package.json"), require_project.getPackageJSON(pkgName, "1.0.0"));
|
|
89
95
|
await (0, fs_promises.writeFile)((0, path.join)(tf, "README.md"), require_project.getREADME(pkgName));
|
|
90
96
|
if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tf, "npm-login");
|
|
91
|
-
await yarnLogin(ctx, tf,
|
|
97
|
+
await yarnLogin(ctx, tf, publishUser, publishPassword, publishEmail);
|
|
92
98
|
await ctx.adapter.exec({ cwd: tf }, "install");
|
|
93
99
|
const pubResp = await ctx.adapter.exec({ cwd: tf }, "publish");
|
|
94
100
|
assert.default.ok(pubResp.stdout.includes("Package archive published"), `Expected publish success but got "${pubResp.stdout}"`);
|
|
95
101
|
});
|
|
96
102
|
await ctx.subTest("switch users", async () => {
|
|
103
|
+
const switchUser1 = `login-switch-a-${id}`;
|
|
104
|
+
const switchPassword1 = "switch-password-123";
|
|
105
|
+
const switchEmail1 = `${switchUser1}@test.example.com`;
|
|
97
106
|
const user2 = `login-b-${id}`;
|
|
98
107
|
const password2 = "other-password-456";
|
|
99
108
|
const email2 = `${user2}@test.example.com`;
|
|
100
109
|
const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);
|
|
101
|
-
await yarnLogin(ctx, tf,
|
|
110
|
+
await yarnLogin(ctx, tf, switchUser1, switchPassword1, switchEmail1);
|
|
102
111
|
const who1 = await yarnWhoami(ctx, tf);
|
|
103
|
-
assert.default.ok(who1.includes(
|
|
112
|
+
assert.default.ok(who1.includes(switchUser1), `Expected "${switchUser1}" but got "${who1}"`);
|
|
104
113
|
await yarnLogin(ctx, tf, user2, password2, email2);
|
|
105
114
|
const who2 = await yarnWhoami(ctx, tf);
|
|
106
115
|
assert.default.ok(who2.includes(user2), `Expected "${user2}" after switch but got "${who2}"`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.cjs","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
|
|
1
|
+
{"version":3,"file":"login.cjs","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('legacy re-login is rejected without basic auth', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, password1, email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Legacy re-login for an existing user should fail without Basic auth');\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 publishUser = `login-pub-${id}`;\n const publishPassword = 'publish-password-123';\n const publishEmail = `${publishUser}@test.example.com`;\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, publishUser, publishPassword, publishEmail);\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 switchUser1 = `login-switch-a-${id}`;\n const switchPassword1 = 'switch-password-123';\n const switchEmail1 = `${switchUser1}@test.example.com`;\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, switchUser1, switchPassword1, switchEmail1);\n const who1 = await yarnWhoami(ctx, tf);\n assert.ok(who1.includes(switchUser1), `Expected \"${switchUser1}\" 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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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/utils/info.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize the parsed output of `npm/pnpm info <pkg> --json`.
|
|
4
|
+
*
|
|
5
|
+
* npm 12+ wraps the result in a single-element JSON array
|
|
6
|
+
* (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
|
|
7
|
+
* version-specific queries, whereas older npm versions return a bare object.
|
|
8
|
+
* When multiple results match a range, the array holds one entry per match;
|
|
9
|
+
* the last entry is the highest (latest) version, which is what the tests want.
|
|
10
|
+
*
|
|
11
|
+
* Returns the bare object as-is for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeInfo(parsed) {
|
|
14
|
+
if (Array.isArray(parsed)) return parsed[parsed.length - 1];
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
/** Parse `info --json` stdout and normalize to a single info object. */
|
|
18
|
+
function parseInfoJson(stdout) {
|
|
19
|
+
return normalizeInfo(JSON.parse(stdout));
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
exports.normalizeInfo = normalizeInfo;
|
|
23
|
+
exports.parseInfoJson = parseInfoJson;
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=info.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.cjs","names":[],"sources":["../../../src/utils/info.ts"],"sourcesContent":["/**\n * Normalize the parsed output of `npm/pnpm info <pkg> --json`.\n *\n * npm 12+ wraps the result in a single-element JSON array\n * (e.g. `[{ \"name\": \"verdaccio\", ... }]`) for both full-packument and\n * version-specific queries, whereas older npm versions return a bare object.\n * When multiple results match a range, the array holds one entry per match;\n * the last entry is the highest (latest) version, which is what the tests want.\n *\n * Returns the bare object as-is for backward compatibility.\n */\nexport function normalizeInfo(parsed: any): any {\n if (Array.isArray(parsed)) {\n return parsed[parsed.length - 1];\n }\n return parsed;\n}\n\n/** Parse `info --json` stdout and normalize to a single info object. */\nexport function parseInfoJson(stdout: string): any {\n return normalizeInfo(JSON.parse(stdout));\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,cAAc,QAAkB;CAC9C,IAAI,MAAM,QAAQ,OAAO,EACvB,OAAO,OAAO,OAAO,SAAS;CAEhC,OAAO;;;AAIT,SAAgB,cAAc,QAAqB;CACjD,OAAO,cAAc,KAAK,MAAM,OAAO,CAAC"}
|
|
@@ -39,7 +39,7 @@ function getREADME(packageName) {
|
|
|
39
39
|
}
|
|
40
40
|
async function prepareGenericEmptyProject(packageName, version, port, token, registryDomain, dependencies = {}, devDependencies = {}) {
|
|
41
41
|
debug$1("preparing generic project %o", packageName);
|
|
42
|
-
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}`;
|
|
42
|
+
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}\nmin-release-age=0`;
|
|
43
43
|
const tempFolder = await createTempFolder("temp-folder");
|
|
44
44
|
await (0, fs_promises.writeFile)((0, path.join)(tempFolder, "package.json"), getPackageJSON(packageName, version, dependencies, devDependencies));
|
|
45
45
|
await (0, fs_promises.writeFile)((0, path.join)(tempFolder, "README.md"), getREADME(packageName));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.cjs","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;;;AAMA,IAAM,WAAA,GAAA,MAAA,SAAmB,4BAA4B;AAErD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,IAAI;CACxD,QAAM,2BAA2B,UAAU;CAC3C,QAAA,GAAA,YAAA,UAAA,GAAA,KAAA,OAAA,GAAA,GAAA,SAA4B,EAAE,iBAAiB,UAAU,GAAG,CAAC;;AAG/D,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACpC;CACR,QAAM,4BAA4B,YAAY;CAe9C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,eACP;EACD;EACA;EACA,UAAU,CAAC,OAAO,MAAM;EACxB,QAAQ;EACR,SAAS;EAEW,CAAK;;AAG7B,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;;AAU7B,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACX;CACjC,QAAM,gCAAgC,YAAY;
|
|
1
|
+
{"version":3,"file":"project.cjs","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n // `min-release-age=0` keeps the harness hermetic: npm 12 added a release-age\n // cooldown that, if set in the developer's global ~/.npmrc, would reject the\n // just-published packages these tests install (ETARGET / \"no matching version\n // ... before <date>\"). Pinning it per-project overrides any global value.\n // It's an npm-only key — pnpm (minimum-release-age) and yarn ignore it.\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}\\nmin-release-age=0`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;;;AAMA,IAAM,WAAA,GAAA,MAAA,SAAmB,4BAA4B;AAErD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,IAAI;CACxD,QAAM,2BAA2B,UAAU;CAC3C,QAAA,GAAA,YAAA,UAAA,GAAA,KAAA,OAAA,GAAA,GAAA,SAA4B,EAAE,iBAAiB,UAAU,GAAG,CAAC;;AAG/D,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACpC;CACR,QAAM,4BAA4B,YAAY;CAe9C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,eACP;EACD;EACA;EACA,UAAU,CAAC,OAAO,MAAM;EACxB,QAAQ;EACR,SAAS;EAEW,CAAK;;AAG7B,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;;AAU7B,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACX;CACjC,QAAM,gCAAgC,YAAY;CAMlD,MAAM,YAAY,MAAc,OAAe,aAC7C,eAAe,KAAK,eAAe,MAAM,aAAa,SAAS;CACjE,MAAM,aAAa,MAAM,iBAAiB,cAAc;CACxD,OAAA,GAAA,YAAA,YAAA,GAAA,KAAA,MACO,YAAY,eAAe,EAChC,eAAe,aAAa,SAAS,cAAc,gBAAgB,CACpE;CACD,OAAA,GAAA,YAAA,YAAA,GAAA,KAAA,MAAqB,YAAY,YAAY,EAAE,UAAU,YAAY,CAAC;CACtE,OAAA,GAAA,YAAA,YAAA,GAAA,KAAA,MAAqB,YAAY,SAAS,EAAE,SAAS,MAAM,OAAO,eAAe,CAAC;CAClF,OAAO,EAAE,YAAY"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeInfo } from "../utils/info.js";
|
|
1
2
|
import buildDebug from "debug";
|
|
2
3
|
import assert from "assert";
|
|
3
4
|
//#region src/scenarios/install-multiple-deps.ts
|
|
@@ -27,9 +28,9 @@ function parseInfoOutput(stdout, adapterType) {
|
|
|
27
28
|
const obj = JSON.parse(line);
|
|
28
29
|
if (obj.type === "inspect" && obj.data) return obj.data;
|
|
29
30
|
} catch {}
|
|
30
|
-
return JSON.parse(stdout);
|
|
31
|
+
return normalizeInfo(JSON.parse(stdout));
|
|
31
32
|
}
|
|
32
|
-
return JSON.parse(stdout);
|
|
33
|
+
return normalizeInfo(JSON.parse(stdout));
|
|
33
34
|
}
|
|
34
35
|
async function publishSeedPackage(ctx, pkgName, version, dependencies = {}) {
|
|
35
36
|
debug("publishing seed package %s@%s", pkgName, version);
|
|
@@ -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(\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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"}
|
|
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';\nimport { normalizeInfo } from '../utils/info';\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 normalizeInfo(JSON.parse(stdout));\n }\n return normalizeInfo(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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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseInfoJson } from "../utils/info.js";
|
|
1
2
|
import buildDebug from "debug";
|
|
2
3
|
import { join } from "path";
|
|
3
4
|
import { readFile, writeFile } from "fs/promises";
|
|
@@ -21,8 +22,7 @@ async function deprecate(ctx, tempFolder, packageVersion, message) {
|
|
|
21
22
|
}
|
|
22
23
|
async function getInfo(ctx, tempFolder, pkgName) {
|
|
23
24
|
debug("fetching info for %s", pkgName);
|
|
24
|
-
const
|
|
25
|
-
const parsed = JSON.parse(resp.stdout);
|
|
25
|
+
const parsed = parseInfoJson((await ctx.adapter.exec({ cwd: tempFolder }, "info", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl))).stdout);
|
|
26
26
|
debug("info for %s: deprecated=%s", pkgName, parsed.deprecated ?? "(not set)");
|
|
27
27
|
return parsed;
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
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 =
|
|
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';\nimport { parseInfoJson } from '../utils/info';\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 = parseInfoJson(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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"}
|
package/build/esm/tests/info.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createTempFolder } from "../utils/project.js";
|
|
2
|
+
import { parseInfoJson } from "../utils/info.js";
|
|
2
3
|
import { join } from "path";
|
|
3
4
|
import { writeFile } from "fs/promises";
|
|
4
5
|
import assert from "assert";
|
|
@@ -23,7 +24,7 @@ async function testInfo(ctx) {
|
|
|
23
24
|
const output = resp.stdout + resp.stderr;
|
|
24
25
|
assert.ok(output.includes("verdaccio"), "Expected deno info output to reference verdaccio");
|
|
25
26
|
} else {
|
|
26
|
-
const parsedBody =
|
|
27
|
+
const parsedBody = parseInfoJson(resp.stdout);
|
|
27
28
|
assert.strictEqual(parsedBody.name, "verdaccio", "Expected package name \"verdaccio\"");
|
|
28
29
|
assert.ok(parsedBody.dependencies !== void 0, "Expected \"dependencies\" to be defined");
|
|
29
30
|
}
|
|
@@ -1 +1 @@
|
|
|
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 =
|
|
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 { parseInfoJson } from '../utils/info';\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 = parseInfoJson(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":";;;;;;AAQA,eAAe,SAAS,KAAiC;CAGvD,MAAM,aAAa,MAAM,iBAAiB,YAAY;CACtD,MAAM,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;CAED,IAAI,IAAI,QAAQ,SAAS,gBAAgB;EAEvC,MAAM,WADQ,KAAK,OAAO,MAAM,KAAK,CAAC,OAAO,QAC5B,CAAM,MAAM,MAAM;GACjC,IAAI;IAEF,OADY,KAAK,MAAM,EAChB,CAAI,SAAS;WACd;IACN,OAAO;;IAET;EACF,OAAO,GAAG,UAAU,4DAA0D;QACzE,IAAI,IAAI,QAAQ,SAAS,QAAQ;EAEtC,MAAM,SAAS,KAAK,SAAS,KAAK;EAClC,OAAO,GAAG,OAAO,SAAS,YAAY,EAAE,mDAAmD;QACtF;EACL,MAAM,aAAa,cAAc,KAAK,OAAO;EAC7C,OAAO,YAAY,WAAW,MAAM,aAAa,sCAAoC;EACrF,OAAO,GAAG,WAAW,iBAAiB,KAAA,GAAW,0CAAwC;;;AAI7F,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;CACN"}
|
package/build/esm/tests/login.js
CHANGED
|
@@ -55,12 +55,15 @@ async function testLogin(ctx) {
|
|
|
55
55
|
const who = await yarnWhoami(ctx, tf);
|
|
56
56
|
assert.ok(who.includes(user1), `Expected whoami "${user1}" but got "${who}"`);
|
|
57
57
|
});
|
|
58
|
-
await ctx.subTest("login
|
|
58
|
+
await ctx.subTest("legacy re-login is rejected without basic auth", async () => {
|
|
59
59
|
const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
let loginFailed = false;
|
|
61
|
+
try {
|
|
62
|
+
await yarnLogin(ctx, tf, user1, password1, email1);
|
|
63
|
+
} catch {
|
|
64
|
+
loginFailed = true;
|
|
65
|
+
}
|
|
66
|
+
assert.ok(loginFailed, "Legacy re-login for an existing user should fail without Basic auth");
|
|
64
67
|
});
|
|
65
68
|
await ctx.subTest("wrong password fails", async () => {
|
|
66
69
|
const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);
|
|
@@ -74,6 +77,9 @@ async function testLogin(ctx) {
|
|
|
74
77
|
});
|
|
75
78
|
await ctx.subTest("login then publish", async () => {
|
|
76
79
|
const pkgName = `@verdaccio/login-pub-${id}`;
|
|
80
|
+
const publishUser = `login-pub-${id}`;
|
|
81
|
+
const publishPassword = "publish-password-123";
|
|
82
|
+
const publishEmail = `${publishUser}@test.example.com`;
|
|
77
83
|
const tf = await createTempFolder(`login-pub-${id}`);
|
|
78
84
|
const yamlContent = YAML.dump({
|
|
79
85
|
npmRegistryServer: ctx.registryUrl,
|
|
@@ -84,19 +90,22 @@ async function testLogin(ctx) {
|
|
|
84
90
|
await writeFile(join(tf, "package.json"), getPackageJSON(pkgName, "1.0.0"));
|
|
85
91
|
await writeFile(join(tf, "README.md"), getREADME(pkgName));
|
|
86
92
|
if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tf, "npm-login");
|
|
87
|
-
await yarnLogin(ctx, tf,
|
|
93
|
+
await yarnLogin(ctx, tf, publishUser, publishPassword, publishEmail);
|
|
88
94
|
await ctx.adapter.exec({ cwd: tf }, "install");
|
|
89
95
|
const pubResp = await ctx.adapter.exec({ cwd: tf }, "publish");
|
|
90
96
|
assert.ok(pubResp.stdout.includes("Package archive published"), `Expected publish success but got "${pubResp.stdout}"`);
|
|
91
97
|
});
|
|
92
98
|
await ctx.subTest("switch users", async () => {
|
|
99
|
+
const switchUser1 = `login-switch-a-${id}`;
|
|
100
|
+
const switchPassword1 = "switch-password-123";
|
|
101
|
+
const switchEmail1 = `${switchUser1}@test.example.com`;
|
|
93
102
|
const user2 = `login-b-${id}`;
|
|
94
103
|
const password2 = "other-password-456";
|
|
95
104
|
const email2 = `${user2}@test.example.com`;
|
|
96
105
|
const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);
|
|
97
|
-
await yarnLogin(ctx, tf,
|
|
106
|
+
await yarnLogin(ctx, tf, switchUser1, switchPassword1, switchEmail1);
|
|
98
107
|
const who1 = await yarnWhoami(ctx, tf);
|
|
99
|
-
assert.ok(who1.includes(
|
|
108
|
+
assert.ok(who1.includes(switchUser1), `Expected "${switchUser1}" but got "${who1}"`);
|
|
100
109
|
await yarnLogin(ctx, tf, user2, password2, email2);
|
|
101
110
|
const who2 = await yarnWhoami(ctx, tf);
|
|
102
111
|
assert.ok(who2.includes(user2), `Expected "${user2}" after switch but got "${who2}"`);
|
|
@@ -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 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
|
|
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('legacy re-login is rejected without basic auth', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, password1, email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Legacy re-login for an existing user should fail without Basic auth');\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 publishUser = `login-pub-${id}`;\n const publishPassword = 'publish-password-123';\n const publishEmail = `${publishUser}@test.example.com`;\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, publishUser, publishPassword, publishEmail);\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 switchUser1 = `login-switch-a-${id}`;\n const switchPassword1 = 'switch-password-123';\n const switchEmail1 = `${switchUser1}@test.example.com`;\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, switchUser1, switchPassword1, switchEmail1);\n const who1 = await yarnWhoami(ctx, tf);\n assert.ok(who1.includes(switchUser1), `Expected \"${switchUser1}\" 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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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/utils/info.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize the parsed output of `npm/pnpm info <pkg> --json`.
|
|
4
|
+
*
|
|
5
|
+
* npm 12+ wraps the result in a single-element JSON array
|
|
6
|
+
* (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
|
|
7
|
+
* version-specific queries, whereas older npm versions return a bare object.
|
|
8
|
+
* When multiple results match a range, the array holds one entry per match;
|
|
9
|
+
* the last entry is the highest (latest) version, which is what the tests want.
|
|
10
|
+
*
|
|
11
|
+
* Returns the bare object as-is for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeInfo(parsed) {
|
|
14
|
+
if (Array.isArray(parsed)) return parsed[parsed.length - 1];
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
/** Parse `info --json` stdout and normalize to a single info object. */
|
|
18
|
+
function parseInfoJson(stdout) {
|
|
19
|
+
return normalizeInfo(JSON.parse(stdout));
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { normalizeInfo, parseInfoJson };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.js","names":[],"sources":["../../../src/utils/info.ts"],"sourcesContent":["/**\n * Normalize the parsed output of `npm/pnpm info <pkg> --json`.\n *\n * npm 12+ wraps the result in a single-element JSON array\n * (e.g. `[{ \"name\": \"verdaccio\", ... }]`) for both full-packument and\n * version-specific queries, whereas older npm versions return a bare object.\n * When multiple results match a range, the array holds one entry per match;\n * the last entry is the highest (latest) version, which is what the tests want.\n *\n * Returns the bare object as-is for backward compatibility.\n */\nexport function normalizeInfo(parsed: any): any {\n if (Array.isArray(parsed)) {\n return parsed[parsed.length - 1];\n }\n return parsed;\n}\n\n/** Parse `info --json` stdout and normalize to a single info object. */\nexport function parseInfoJson(stdout: string): any {\n return normalizeInfo(JSON.parse(stdout));\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,cAAc,QAAkB;CAC9C,IAAI,MAAM,QAAQ,OAAO,EACvB,OAAO,OAAO,OAAO,SAAS;CAEhC,OAAO;;;AAIT,SAAgB,cAAc,QAAqB;CACjD,OAAO,cAAc,KAAK,MAAM,OAAO,CAAC"}
|
|
@@ -37,7 +37,7 @@ function getREADME(packageName) {
|
|
|
37
37
|
}
|
|
38
38
|
async function prepareGenericEmptyProject(packageName, version, port, token, registryDomain, dependencies = {}, devDependencies = {}) {
|
|
39
39
|
debug("preparing generic project %o", packageName);
|
|
40
|
-
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}`;
|
|
40
|
+
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}\nmin-release-age=0`;
|
|
41
41
|
const tempFolder = await createTempFolder("temp-folder");
|
|
42
42
|
await writeFile(join(tempFolder, "package.json"), getPackageJSON(packageName, version, dependencies, devDependencies));
|
|
43
43
|
await writeFile(join(tempFolder, "README.md"), getREADME(packageName));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;AAMA,IAAM,QAAQ,WAAW,4BAA4B;AAErD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,IAAI;CACxD,MAAM,2BAA2B,UAAU;CAC3C,OAAO,QAAQ,KAAK,QAAQ,EAAE,iBAAiB,UAAU,GAAG,CAAC;;AAG/D,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACpC;CACR,MAAM,4BAA4B,YAAY;CAe9C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,eACP;EACD;EACA;EACA,UAAU,CAAC,OAAO,MAAM;EACxB,QAAQ;EACR,SAAS;EAEW,CAAK;;AAG7B,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;;AAU7B,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACX;CACjC,MAAM,gCAAgC,YAAY;
|
|
1
|
+
{"version":3,"file":"project.js","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n // `min-release-age=0` keeps the harness hermetic: npm 12 added a release-age\n // cooldown that, if set in the developer's global ~/.npmrc, would reject the\n // just-published packages these tests install (ETARGET / \"no matching version\n // ... before <date>\"). Pinning it per-project overrides any global value.\n // It's an npm-only key — pnpm (minimum-release-age) and yarn ignore it.\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}\\nmin-release-age=0`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;AAMA,IAAM,QAAQ,WAAW,4BAA4B;AAErD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,IAAI;CACxD,MAAM,2BAA2B,UAAU;CAC3C,OAAO,QAAQ,KAAK,QAAQ,EAAE,iBAAiB,UAAU,GAAG,CAAC;;AAG/D,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACpC;CACR,MAAM,4BAA4B,YAAY;CAe9C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,eACP;EACD;EACA;EACA,UAAU,CAAC,OAAO,MAAM;EACxB,QAAQ;EACR,SAAS;EAEW,CAAK;;AAG7B,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;;AAU7B,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,EAAE,EACzC,kBAA0C,EAAE,EACX;CACjC,MAAM,gCAAgC,YAAY;CAMlD,MAAM,YAAY,MAAc,OAAe,aAC7C,eAAe,KAAK,eAAe,MAAM,aAAa,SAAS;CACjE,MAAM,aAAa,MAAM,iBAAiB,cAAc;CACxD,MAAM,UACJ,KAAK,YAAY,eAAe,EAChC,eAAe,aAAa,SAAS,cAAc,gBAAgB,CACpE;CACD,MAAM,UAAU,KAAK,YAAY,YAAY,EAAE,UAAU,YAAY,CAAC;CACtE,MAAM,UAAU,KAAK,YAAY,SAAS,EAAE,SAAS,MAAM,OAAO,eAAe,CAAC;CAClF,OAAO,EAAE,YAAY"}
|
package/build/utils/index.d.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize the parsed output of `npm/pnpm info <pkg> --json`.
|
|
3
|
+
*
|
|
4
|
+
* npm 12+ wraps the result in a single-element JSON array
|
|
5
|
+
* (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
|
|
6
|
+
* version-specific queries, whereas older npm versions return a bare object.
|
|
7
|
+
* When multiple results match a range, the array holds one entry per match;
|
|
8
|
+
* the last entry is the highest (latest) version, which is what the tests want.
|
|
9
|
+
*
|
|
10
|
+
* Returns the bare object as-is for backward compatibility.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeInfo(parsed: any): any;
|
|
13
|
+
/** Parse `info --json` stdout and normalize to a single info object. */
|
|
14
|
+
export declare function parseInfoJson(stdout: string): any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/e2e-cli",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "CLI tool to run Verdaccio e2e tests against any running registry",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@verdaccio/registry-cli": "^1.1.0",
|
|
31
|
-
"debug": "4.4.
|
|
31
|
+
"debug": "4.4.3",
|
|
32
32
|
"get-port": "5.1.1",
|
|
33
|
-
"js-yaml": "4.
|
|
33
|
+
"js-yaml": "4.3.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/debug": "4.1.12",
|