From cb5847e3203c52062485b5de68e4f6d29b33c361 Mon Sep 17 00:00:00 2001 From: nlf Date: Mon, 14 Dec 2020 14:59:19 -0800 Subject: add tests for npm hook PR-URL: https://github.com/npm/cli/pull/2349 Credit: @nlf Close: #2349 Reviewed-by: @ruyadorno --- test/lib/hook.js | 589 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 test/lib/hook.js (limited to 'test/lib') diff --git a/test/lib/hook.js b/test/lib/hook.js new file mode 100644 index 000000000..359904202 --- /dev/null +++ b/test/lib/hook.js @@ -0,0 +1,589 @@ +const { test } = require('tap') +const requireInject = require('require-inject') + +const npm = { + flatOptions: { + json: false, + parseable: false, + silent: false, + loglevel: 'info', + unicode: false, + }, +} + +const pkgTypes = { + semver: 'package', + '@npmcli': 'scope', + npm: 'owner', +} + +const now = Date.now() +let hookResponse = null +let hookArgs = null +const libnpmhook = { + add: async (pkg, uri, secret, opts) => { + hookArgs = { pkg, uri, secret, opts } + return { id: 1, name: pkg.replace(/^@/, ''), type: pkgTypes[pkg], endpoint: uri } + }, + ls: async (opts) => { + hookArgs = opts + let id = 0 + if (hookResponse) + return hookResponse + + return Object.keys(pkgTypes).map((name) => ({ + id: ++id, + name: name.replace(/^@/, ''), + type: pkgTypes[name], + endpoint: 'https://google.com', + last_delivery: id % 2 === 0 ? now : undefined, + })) + }, + rm: async (id, opts) => { + hookArgs = { id, opts } + const pkg = Object.keys(pkgTypes)[0] + return { id: 1, name: pkg.replace(/^@/, ''), type: pkgTypes[pkg], endpoint: 'https://google.com' } + }, + update: async (id, uri, secret, opts) => { + hookArgs = { id, uri, secret, opts } + const pkg = Object.keys(pkgTypes)[0] + return { id, name: pkg.replace(/^@/, ''), type: pkgTypes[pkg], endpoint: uri } + }, +} + +const output = [] +const hook = requireInject('../../lib/hook.js', { + '../../lib/npm.js': npm, + '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), + '../../lib/utils/output.js': (msg) => { + output.push(msg) + }, + libnpmhook, +}) + +test('npm hook no args', t => { + return hook([], (err) => { + t.match(err, /npm hook add/, 'throws usage with no arguments') + t.end() + }) +}) + +test('npm hook add', t => { + t.teardown(() => { + hookArgs = null + output.length = 0 + }) + + return hook(['add', 'semver', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + pkg: 'semver', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'provided the correct arguments to libnpmhook') + t.strictSame(output, ['+ semver -> https://google.com'], 'prints the correct output') + t.end() + }) +}) + +test('npm hook add - unicode output', t => { + npm.flatOptions.unicode = true + t.teardown(() => { + npm.flatOptions.unicode = false + hookArgs = null + output.length = 0 + }) + + return hook(['add', 'semver', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + pkg: 'semver', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'provided the correct arguments to libnpmhook') + t.strictSame(output, ['+ semver ➜ https://google.com'], 'prints the correct output') + t.end() + }) +}) + +test('npm hook add - json output', t => { + npm.flatOptions.json = true + t.teardown(() => { + npm.flatOptions.json = false + hookArgs = null + output.length = 0 + }) + + return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + pkg: '@npmcli', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'provided the correct arguments to libnpmhook') + t.strictSame(JSON.parse(output[0]), { + id: 1, + name: 'npmcli', + endpoint: 'https://google.com', + type: 'scope', + }, 'prints the correct json output') + t.end() + }) +}) + +test('npm hook add - parseable output', t => { + npm.flatOptions.parseable = true + t.teardown(() => { + npm.flatOptions.parseable = false + hookArgs = null + output.length = 0 + }) + + return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + pkg: '@npmcli', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'provided the correct arguments to libnpmhook') + t.strictSame(output[0].split(/\t/), [ + 'id', 'name', 'type', 'endpoint', + ], 'prints the correct parseable output headers') + t.strictSame(output[1].split(/\t/), [ + '1', 'npmcli', 'scope', 'https://google.com', + ], 'prints the correct parseable values') + t.end() + }) +}) + +test('npm hook add - silent output', t => { + npm.flatOptions.silent = true + t.teardown(() => { + npm.flatOptions.silent = false + hookArgs = null + output.length = 0 + }) + + return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + pkg: '@npmcli', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'provided the correct arguments to libnpmhook') + t.strictSame(output, [], 'printed no output') + t.end() + }) +}) + +test('npm hook ls', t => { + t.teardown(() => { + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + t.equal(output[0], 'You have 3 hooks configured.', 'prints the correct header') + const out = require('../../lib/utils/ansi-trim')(output[1]) + t.match(out, /semver.*https:\/\/google.com.*\n.*\n.*never triggered/, 'prints package hook') + t.match(out, /@npmcli.*https:\/\/google.com.*\n.*\n.*triggered just now/, 'prints scope hook') + t.match(out, /~npm.*https:\/\/google.com.*\n.*\n.*never triggered/, 'prints owner hook') + t.end() + }) +}) + +test('npm hook ls, no results', t => { + hookResponse = [] + t.teardown(() => { + hookResponse = null + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + t.equal(output[0], 'You don\'t have any hooks configured yet.', 'prints the correct result') + t.end() + }) +}) + +test('npm hook ls, single result', t => { + hookResponse = [{ + id: 1, + name: 'semver', + type: 'package', + endpoint: 'https://google.com', + }] + + t.teardown(() => { + hookResponse = null + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + t.equal(output[0], 'You have one hook configured.', 'prints the correct header') + const out = require('../../lib/utils/ansi-trim')(output[1]) + t.match(out, /semver.*https:\/\/google.com.*\n.*\n.*never triggered/, 'prints package hook') + t.end() + }) +}) + +test('npm hook ls - json output', t => { + npm.flatOptions.json = true + t.teardown(() => { + npm.flatOptions.json = false + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + const out = JSON.parse(output[0]) + t.match(out, [{ + id: 1, + name: 'semver', + type: 'package', + endpoint: 'https://google.com', + }, { + id: 2, + name: 'npmcli', + type: 'scope', + endpoint: 'https://google.com', + }, { + id: 3, + name: 'npm', + type: 'owner', + endpoint: 'https://google.com', + }], 'prints the correct output') + t.end() + }) +}) + +test('npm hook ls - parseable output', t => { + npm.flatOptions.parseable = true + t.teardown(() => { + npm.flatOptions.parseable = false + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + t.strictSame(output.map(line => line.split(/\t/)), [ + ['id', 'name', 'type', 'endpoint', 'last_delivery'], + ['1', 'semver', 'package', 'https://google.com', ''], + ['2', 'npmcli', 'scope', 'https://google.com', `${now}`], + ['3', 'npm', 'owner', 'https://google.com', ''], + ], 'prints the correct result') + t.end() + }) +}) + +test('npm hook ls - silent output', t => { + npm.flatOptions.silent = true + t.teardown(() => { + npm.flatOptions.silent = false + hookArgs = null + output.length = 0 + }) + + return hook(['ls'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + ...npm.flatOptions, + package: undefined, + }, 'received the correct arguments') + t.strictSame(output, [], 'printed no output') + t.end() + }) +}) + +test('npm hook rm', t => { + t.teardown(() => { + hookArgs = null + output.length = 0 + }) + + return hook(['rm', '1'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [ + '- semver X https://google.com', + ], 'printed the correct output') + t.end() + }) +}) + +test('npm hook rm - unicode output', t => { + npm.flatOptions.unicode = true + t.teardown(() => { + npm.flatOptions.unicode = false + hookArgs = null + output.length = 0 + }) + + return hook(['rm', '1'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [ + '- semver ✘ https://google.com', + ], 'printed the correct output') + t.end() + }) +}) + +test('npm hook rm - silent output', t => { + npm.flatOptions.silent = true + t.teardown(() => { + npm.flatOptions.silent = false + hookArgs = null + output.length = 0 + }) + + return hook(['rm', '1'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [], 'printed no output') + t.end() + }) +}) + +test('npm hook rm - json output', t => { + npm.flatOptions.json = true + t.teardown(() => { + npm.flatOptions.json = false + hookArgs = null + output.length = 0 + }) + + return hook(['rm', '1'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(JSON.parse(output[0]), { + id: 1, + name: 'semver', + type: 'package', + endpoint: 'https://google.com', + }, 'printed correct output') + t.end() + }) +}) + +test('npm hook rm - parseable output', t => { + npm.flatOptions.parseable = true + t.teardown(() => { + npm.flatOptions.parseable = false + hookArgs = null + output.length = 0 + }) + + return hook(['rm', '1'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output.map(line => line.split(/\t/)), [ + ['id', 'name', 'type', 'endpoint'], + ['1', 'semver', 'package', 'https://google.com'], + ], 'printed correct output') + t.end() + }) +}) + +test('npm hook update', t => { + t.teardown(() => { + hookArgs = null + output.length = 0 + }) + + return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [ + '+ semver -> https://google.com', + ], 'printed the correct output') + t.end() + }) +}) + +test('npm hook update - unicode', t => { + npm.flatOptions.unicode = true + t.teardown(() => { + npm.flatOptions.unicode = false + hookArgs = null + output.length = 0 + }) + + return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [ + '+ semver ➜ https://google.com', + ], 'printed the correct output') + t.end() + }) +}) + +test('npm hook update - json output', t => { + npm.flatOptions.json = true + t.teardown(() => { + npm.flatOptions.json = false + hookArgs = null + output.length = 0 + }) + + return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(JSON.parse(output[0]), { + id: '1', + name: 'semver', + type: 'package', + endpoint: 'https://google.com', + }, 'printed the correct output') + t.end() + }) +}) + +test('npm hook update - parseable output', t => { + npm.flatOptions.parseable = true + t.teardown(() => { + npm.flatOptions.parseable = false + hookArgs = null + output.length = 0 + }) + + return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output.map(line => line.split(/\t/)), [ + ['id', 'name', 'type', 'endpoint'], + ['1', 'semver', 'package', 'https://google.com'], + ], 'printed the correct output') + t.end() + }) +}) + +test('npm hook update - silent output', t => { + npm.flatOptions.silent = true + t.teardown(() => { + npm.flatOptions.silent = false + hookArgs = null + output.length = 0 + }) + + return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => { + if (err) + throw err + + t.strictSame(hookArgs, { + id: '1', + uri: 'https://google.com', + secret: 'some-secret', + opts: npm.flatOptions, + }, 'received the correct arguments') + t.strictSame(output, [], 'printed no output') + t.end() + }) +}) -- cgit v1.2.3