/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/npm/test/lib/utils/reify-output.js
613 строк
16 KB
npm CLI robot
deps: upgrade npm to 11.19.0
04 авг 2026, 22:27
Не верифицирован
04 авг 2026, 22:27
c8fa0b1
Код
Авторство
О чём код?
const t = require('tap') const mockNpm = require('../../fixtures/mock-npm') const reifyOutput = require('../../../lib/utils/reify-output.js') t.cleanSnapshot = str => str.replace(/in [0-9]+m?s/g, 'in {TIME}') const mockReify = async (t, reify, { command, ...config } = {}) => { const mock = await mockNpm(t, { command, config, }) // Hack to adapt existing fake test. Make npm.command // return whatever was passed in to this function. // What it should be doing is npm.exec(command) but that // breaks most of these tests because they don't expect // a command to actually run. Object.defineProperty(mock.npm, 'command', { get () { return command }, enumerable: true, }) reifyOutput(mock.npm, reify) mock.npm.finish() return mock.joinedOutput() } t.test('missing info', async t => { const out = await mockReify(t, { actualTree: { children: [], }, diff: { children: [], }, }) t.notMatch( out, 'looking for funding', 'should not print fund message if missing info' ) }) t.test('even more missing info', async t => { const out = await mockReify(t, { actualTree: { children: [], }, }) t.notMatch( out, 'looking for funding', 'should not print fund message if missing info' ) }) t.test('single package', async t => { const out = await mockReify(t, { // a report with an error is the same as no report at all, if // the command is not 'audit' auditReport: { error: { message: 'no audit for youuuuu', }, }, actualTree: { name: 'foo', package: { name: 'foo', version: '1.0.0', }, edgesOut: new Map([ ['bar', { to: { name: 'bar', package: { name: 'bar', version: '1.0.0', funding: { type: 'foo', url: 'http://example.com' }, }, }, }], ]), }, diff: { children: [], }, }) t.match( out, '1 package is looking for funding', 'should print single package message' ) }) t.test('no message when funding config is false', async t => { const out = await mockReify(t, { actualTree: { name: 'foo', package: { name: 'foo', version: '1.0.0', }, edgesOut: new Map([ ['bar', { to: { name: 'bar', package: { name: 'bar', version: '1.0.0', funding: { type: 'foo', url: 'http://example.com' }, }, }, }], ]), }, diff: { children: [], }, }, { fund: false }) t.notMatch(out, 'looking for funding', 'should not print funding info') }) t.test('print appropriate message for many packages', async t => { const out = await mockReify(t, { actualTree: { name: 'foo', package: { name: 'foo', version: '1.0.0', }, edgesOut: new Map([ ['bar', { to: { name: 'bar', package: { name: 'bar', version: '1.0.0', funding: { type: 'foo', url: 'http://example.com' }, }, }, }], ['lorem', { to: { name: 'lorem', package: { name: 'lorem', version: '1.0.0', funding: { type: 'foo', url: 'http://example.com' }, }, }, }], ['ipsum', { to: { name: 'ipsum', package: { name: 'ipsum', version: '1.0.0', funding: { type: 'foo', url: 'http://example.com' }, }, }, }], ]), }, diff: { children: [], }, }) t.match( out, '3 packages are looking for funding', 'should print single package message' ) }) t.test('showing and not showing audit report', async t => { const auditReport = { toJSON: () => auditReport, auditReportVersion: 2, vulnerabilities: { minimist: { name: 'minimist', severity: 'low', via: [ { id: 1179, url: 'https://npmjs.com/advisories/1179', title: 'Prototype Pollution', severity: 'low', vulnerable_versions: '<0.2.1 || >=1.0.0 <1.2.3', }, ], effects: [], range: '<0.2.1 || >=1.0.0 <1.2.3', nodes: [ 'node_modules/minimist', ], fixAvailable: true, }, }, metadata: { vulnerabilities: { info: 0, low: 1, moderate: 0, high: 0, critical: 0, total: 1, }, dependencies: { prod: 1, dev: 0, optional: 0, peer: 0, peerOptional: 0, total: 1, }, }, } t.test('no output when silent', async t => { const out = await mockReify(t, { actualTree: { inventory: { size: 999 }, children: [] }, auditReport, diff: { children: [ { action: 'ADD', ideal: { location: 'loc' } }, ], }, }, { silent: true }) t.equal(out, '', 'should not get output when silent') }) t.test('output when not silent', async t => { const out = await mockReify(t, { actualTree: { inventory: new Map(), children: [] }, auditReport, diff: { children: [ { action: 'ADD', ideal: { location: 'loc' } }, ], }, }) t.match(out, /Run `npm audit` for details\.$/, 'got audit report') }) for (const json of [true, false]) { t.test(`json=${json}`, async t => { t.test('set exit code when cmd is audit', async t => { await mockReify(t, { actualTree: { inventory: new Map(), children: [] }, auditReport, diff: { children: [ { action: 'ADD', ideal: { location: 'loc' } }, ], }, }, { command: 'audit', 'audit-level': 'low' }) t.equal(process.exitCode, 1, 'set exit code') }) t.test('do not set exit code when cmd is install', async t => { await mockReify(t, { actualTree: { inventory: new Map(), children: [] }, auditReport, diff: { children: [ { action: 'ADD', ideal: { location: 'loc' } }, ], }, }, { command: 'install', 'audit-level': 'low' }) t.notOk(process.exitCode, 'did not set exit code') }) }) } }) t.test('packages changed message', async t => { // return a test function that builds up the mock and snapshots output const testCase = async (t, added, removed, changed, audited, json, command) => { const mock = { actualTree: { inventory: { size: audited, has: () => true }, children: [], }, auditReport: audited ? { toJSON: () => mock.auditReport, vulnerabilities: {}, metadata: { vulnerabilities: { total: 0, }, }, } : null, diff: { children: [ { action: 'some random unexpected junk' }, ], }, } for (let i = 0; i < added; i++) { mock.diff.children.push({ action: 'ADD', ideal: { path: `test/${i}`, name: `@npmcli/pkg${i}`, location: 'loc', package: { version: `1.0.${i}` } } }) } for (let i = 0; i < removed; i++) { mock.diff.children.push({ action: 'REMOVE', actual: { path: `test/${i}`, name: `@npmcli/pkg${i}`, location: 'loc', package: { version: `1.0.${i}` } } }) } for (let i = 0; i < changed; i++) { const actual = { path: `test/a/${i}`, name: `@npmcli/pkg${i}`, location: 'loc', package: { version: `1.0.${i}` } } const ideal = { path: `test/i/${i}`, name: `@npmcli/pkg${i}`, location: 'loc', package: { version: `1.1.${i}` } } mock.diff.children.push({ action: 'CHANGE', actual, ideal }) } const out = await mockReify(t, mock, { json, command }) t.matchSnapshot(out, JSON.stringify({ added, removed, changed, audited, json, })) } const cases = [] for (const added of [0, 1, 2]) { for (const removed of [0, 1, 2]) { for (const changed of [0, 1, 2]) { for (const audited of [0, 1, 2]) { for (const json of [true, false]) { cases.push([added, removed, changed, audited, json, 'install']) } } } } } // add case for when audit is the command cases.push([0, 0, 0, 2, true, 'audit']) cases.push([0, 0, 0, 2, false, 'audit']) for (const c of cases) { await t.test('', t => testCase(t, ...c)) } }) t.test('added packages should be looked up within returned tree', async t => { t.test('has added pkg in inventory', async t => { const out = await mockReify(t, { actualTree: { name: 'foo', inventory: { has: () => true, }, }, diff: { children: [ { action: 'ADD', ideal: { path: 'test/baz', name: 'baz', package: { version: '1.0.0' } } }, ], }, }) t.matchSnapshot(out) }) t.test('missing added pkg in inventory', async t => { const out = await mockReify(t, { actualTree: { name: 'foo', inventory: { has: () => false, }, }, diff: { children: [ { action: 'ADD', ideal: { path: 'test/baz', name: 'baz', package: { version: '1.0.0' } } }, ], }, }) t.matchSnapshot(out) }) t.test('linked store package counted though absent from actualTree', async t => { const out = await mockReify(t, { actualTree: { name: 'foo', inventory: { has: () => false, }, }, diff: { children: [ { action: 'ADD', ideal: { path: 'test/baz', name: 'baz', isInStore: true, isLink: false, package: { version: '1.0.0' } } }, { action: 'ADD', ideal: { path: 'test/baz-link', name: 'baz', isInStore: false, isLink: true, package: { version: '1.0.0' } } }, ], }, }) t.matchSnapshot(out) }) }) t.test('prints dedupe difference on dry-run', async t => { const mock = { actualTree: { name: 'foo', inventory: { has: () => false, }, }, diff: { children: [ { action: 'ADD', ideal: { path: 'test/foo', name: 'foo', package: { version: '1.0.0' } } }, { action: 'REMOVE', actual: { path: 'test/foo', name: 'bar', package: { version: '1.0.0' } } }, { action: 'CHANGE', actual: { path: 'test/a/bar', name: 'bar', package: { version: '1.0.0' } }, ideal: { path: 'test/i/bar', name: 'bar', package: { version: '2.1.0' } }, }, ], }, } const out = await mockReify(t, mock, { 'dry-run': true, }) t.matchSnapshot(out, 'diff table') }) t.test('prints dedupe difference on long', async t => { const mock = { actualTree: { name: 'foo', inventory: { has: () => false, }, }, diff: { children: [ { action: 'ADD', ideal: { path: 'test/foo', name: 'foo', package: { version: '1.0.0' } } }, { action: 'REMOVE', actual: { path: 'test/bar', name: 'bar', package: { version: '1.0.0' } } }, { action: 'CHANGE', actual: { path: 'test/a/bar', name: 'bar', package: { version: '1.0.0' } }, ideal: { path: 'test/i/bar', name: 'bar', package: { version: '2.1.0' } }, }, ], }, } const out = await mockReify(t, mock, { long: true, }) t.matchSnapshot(out, 'diff table') }) t.test('prints unreviewed install scripts summary', async t => { const mockReifyWithExtras = async (t, reify, extras, { command, ...config } = {}) => { const mock = await mockNpm(t, { command, config }) Object.defineProperty(mock.npm, 'command', { get () { return command }, enumerable: true, }) reifyOutput(mock.npm, reify, extras) mock.npm.finish() return mock } const baseReify = { actualTree: { name: 'host', inventory: { has: () => false } }, diff: { children: [] }, } const unreviewedScripts = [ { node: { packageName: 'canvas', name: 'canvas', version: '2.11.0', path: '/x/canvas' }, scripts: { install: 'node-gyp rebuild' }, }, { node: { packageName: 'sharp', name: 'sharp', version: '0.33.2', path: '/x/sharp' }, scripts: { preinstall: 'pre', postinstall: 'post' }, }, ] const mock = await mockReifyWithExtras(t, baseReify, { unreviewedScripts }) const warn = mock.logs.warn.byTitle('install-scripts').join('\n') t.match(warn, /2 packages have install scripts not yet covered/) t.match(warn, /canvas@2\.11\.0 \(install: node-gyp rebuild\)/) t.match(warn, /sharp@0\.33\.2 \(preinstall: pre; postinstall: post\)/) t.match(warn, /npm install-scripts ls/) }) t.test('global install suggests --allow-scripts, not approve-scripts', async t => { const mockReifyWithExtras = async (t, reify, extras, config = {}) => { const mock = await mockNpm(t, { config }) reifyOutput(mock.npm, reify, extras) mock.npm.finish() return mock } const baseReify = { actualTree: { name: 'host', inventory: { has: () => false } }, diff: { children: [] }, } const unreviewedScripts = [ { node: { packageName: 'canvas', name: 'canvas', version: '2.11.0', path: '/x/canvas' }, scripts: { install: 'node-gyp rebuild' }, }, { node: { packageName: 'sharp', name: 'sharp', version: '0.33.2', path: '/x/sharp' }, scripts: { preinstall: 'pre', postinstall: 'post' }, }, ] const mock = await mockReifyWithExtras(t, baseReify, { unreviewedScripts }, { global: true }) const warn = mock.logs.warn.byTitle('install-scripts').join('\n') t.match(warn, /2 packages have install scripts not yet covered/) t.match(warn, /canvas@2\.11\.0 \(install: node-gyp rebuild\)/) t.match(warn, /npm install -g --allow-scripts=canvas,sharp/) t.match(warn, /npm config set allow-scripts=canvas,sharp/) t.notMatch(warn, /approve-scripts/) }) t.test('single unreviewed script uses singular wording', async t => { const mockReifyWithExtras = async (t, reify, extras) => { const mock = await mockNpm(t, {}) reifyOutput(mock.npm, reify, extras) mock.npm.finish() return mock } const mock = await mockReifyWithExtras( t, { actualTree: { inventory: { has: () => false } }, diff: { children: [] } }, { unreviewedScripts: [{ node: { packageName: 'one', name: 'one', version: '1.0.0', path: '/x' }, scripts: { install: 'do' }, }], } ) t.match(mock.logs.warn.byTitle('install-scripts').join('\n'), /1 package has install scripts/) }) t.test('json output includes unreviewedScripts', async t => { const mock = await mockNpm(t, { config: { json: true } }) reifyOutput(mock.npm, { actualTree: { inventory: { size: 0 } }, diff: null, }, { unreviewedScripts: [{ node: { packageName: 'pkg', name: 'pkg', version: '1.0.0', path: '/x' }, scripts: { install: 'cmd' }, }], }) mock.npm.finish() const parsed = JSON.parse(mock.joinedOutput()) t.match(parsed.unreviewedScripts, [{ name: 'pkg', version: '1.0.0', path: '/x', scripts: { install: 'cmd' }, }]) }) t.test('unreviewed script with node.name only (no packageName) still renders', async t => { const mock = await mockNpm(t, {}) reifyOutput(mock.npm, { actualTree: { inventory: { has: () => false } }, diff: { children: [] }, }, { unreviewedScripts: [{ node: { name: 'fallback', path: '/x' }, // no packageName, no version scripts: { install: 'cmd' }, }], }) mock.npm.finish() t.match(mock.logs.warn.byTitle('install-scripts').join('\n'), / fallback \(install: cmd\)/) }) t.test('json output includes node.name when packageName is missing', async t => { const mock = await mockNpm(t, { config: { json: true } }) reifyOutput(mock.npm, { actualTree: { inventory: { size: 0 } }, diff: null, }, { unreviewedScripts: [{ node: { name: 'fallback', path: '/x' }, scripts: { install: 'cmd' }, }], }) mock.npm.finish() const parsed = JSON.parse(mock.joinedOutput()) t.equal(parsed.unreviewedScripts[0].name, 'fallback') })