diff --git a/README.md b/README.md index 1583fec..3dd38a9 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ to trust only that specific signing authority. Multiple CAs can be trusted by specifying an array of certificates instead of a single string. -See also [`opts.strict-ssl`](#opts-strict-ssl), [`opts.ca`](#opts-ca) and +See also [`opts.strictSSL`](#opts-strictSSL), [`opts.ca`](#opts-ca) and [`opts.key`](#opts-key) ##### `opts.cache` @@ -175,8 +175,8 @@ will be cached according to [IETF RFC 7234](https://tools.ietf.org/html/rfc7234) rules. This will speed up future requests, as well as make the cached data available offline if necessary/requested. -See also [`offline`](#opts-offline), [`prefer-offline`](#opts-prefer-offline), -and [`prefer-online`](#opts-prefer-online). +See also [`offline`](#opts-offline), [`preferOffline`](#opts-preferOffline), +and [`preferOnline`](#opts-preferOnline). ##### `opts.cert` @@ -197,7 +197,7 @@ It is _not_ the path to a certificate file (and there is no "certfile" option). See also: [`opts.ca`](#opts-ca) and [`opts.key`](#opts-key) -##### `opts.fetch-retries` +##### `opts.fetchRetries` * Type: Number * Default: 2 @@ -208,7 +208,7 @@ packages from the registry. See also [`opts.retry`](#opts-retry) to provide all retry options as a single object. -##### `opts.fetch-retry-factor` +##### `opts.fetchRetryFactor` * Type: Number * Default: 10 @@ -219,7 +219,7 @@ packages. See also [`opts.retry`](#opts-retry) to provide all retry options as a single object. -##### `opts.fetch-retry-mintimeout` +##### `opts.fetchRetryMintimeout` * Type: Number * Default: 10000 (10 seconds) @@ -230,7 +230,7 @@ packages. See also [`opts.retry`](#opts-retry) to provide all retry options as a single object. -##### `opts.fetch-retry-maxtimeout` +##### `opts.fetchRetryMaxtimeout` * Type: Number * Default: 60000 (1 minute) @@ -241,9 +241,8 @@ packages. See also [`opts.retry`](#opts-retry) to provide all retry options as a single object. -##### `opts.force-auth` +##### `opts.forceAuth` -* Alias: `opts.forceAuth` * Type: Object * Default: null @@ -269,9 +268,8 @@ Additional headers for the outgoing request. This option can also be used to override headers automatically generated by `npm-registry-fetch`, such as `Content-Type`. -##### `opts.ignore-body` +##### `opts.ignoreBody` -* Alias: `opts.ignoreBody` * Type: Boolean * Default: false @@ -298,9 +296,8 @@ previously-generated integrity hash for the saved request information, so `EINTEGRITY` errors can happen if [`opts.cache`](#opts-cache) is used, even if `opts.integrity` is not passed in. -##### `opts.is-from-ci` +##### `opts.isFromCI` -* Alias: `opts.isFromCI` * Type: Boolean * Default: Based on environment variables @@ -324,7 +321,7 @@ It is _not_ the path to a key file (and there is no "keyfile" option). See also: [`opts.ca`](#opts-ca) and [`opts.cert`](#opts-cert) -##### `opts.local-address` +##### `opts.localAddress` * Type: IP Address String * Default: null @@ -342,9 +339,8 @@ See also [`opts.proxy`](#opts-proxy) Logger object to use for logging operation details. Must have the same methods as `npmlog`. -##### `opts.map-json` +##### `opts.mapJSON` -* Alias: `mapJson`, `mapJSON` * Type: Function * Default: undefined @@ -352,9 +348,8 @@ When using `fetch.json.stream()` (NOT `fetch.json()`), this will be passed down to [`JSONStream`](https://npm.im/JSONStream) as the second argument to `JSONStream.parse`, and can be used to transform stream data before output. -##### `opts.maxsockets` +##### `opts.maxSockets` -* Alias: `opts.max-sockets` * Type: Integer * Default: 12 @@ -375,9 +370,8 @@ HTTP method to use for the outgoing request. Case-insensitive. If true, proxying will be disabled even if [`opts.proxy`](#opts-proxy) is used. -##### `opts.npm-session` +##### `opts.npmSession` -* Alias: `opts.npmSession` * Type: String * Default: null @@ -392,7 +386,7 @@ invocations of the CLI). Force offline mode: no network requests will be done during install. To allow `npm-registry-fetch` to fill in missing cache data, see -[`opts.prefer-offline`](#opts-prefer-offline). +[`opts.preferOffline`](#opts-preferOffline). This option is only really useful if you're also using [`opts.cache`](#opts-cache). @@ -408,7 +402,7 @@ account. ##### `opts.password` -* Alias: _password +* Alias: `_password` * Type: String * Default: null @@ -426,7 +420,7 @@ That is: See also [`opts.username`](#opts-username) -##### `opts.prefer-offline` +##### `opts.preferOffline` * Type: Boolean * Default: false @@ -438,7 +432,7 @@ will be requested from the server. To force full offline mode, use This option is generally only useful if you're also using [`opts.cache`](#opts-cache). -##### `opts.prefer-online` +##### `opts.preferOnline` * Type: Boolean * Default: false @@ -450,9 +444,8 @@ This option is generally only useful if you're also using [`opts.cache`](#opts-cache). -##### `opts.project-scope` +##### `opts.projectScope` -* Alias: `opts.projectScope` * Type: String * Default: null @@ -483,7 +476,6 @@ If the request URI already has a query string, it will be merged with ##### `opts.refer` -* Alias: `opts.referer` * Type: String * Default: null @@ -547,7 +539,7 @@ If provided, can be used to automatically configure [`opts.scope`](#opts-scope) based on a specific package name. Non-registry package specs will throw an error. -##### `opts.strict-ssl` +##### `opts.strictSSL` * Type: Boolean * Default: true @@ -580,7 +572,7 @@ Can be scoped to a registry by using a "nerf dart" for that registry. That is: } ``` -##### `opts.user-agent` +##### `opts.userAgent` * Type: String * Default: `'npm-registry-fetch@/node@+ ()'` @@ -612,4 +604,4 @@ See also [`opts.password`](#opts-password) * Default: null ** DEPRECATED ** This is a legacy authentication token supported only for -*compatibility. Please use [`opts.token`](#opts-token) instead. +compatibility. Please use [`opts.token`](#opts-token) instead. diff --git a/auth.js b/auth.js index 3198b9c..11c3bde 100644 --- a/auth.js +++ b/auth.js @@ -1,17 +1,14 @@ 'use strict' -const config = require('./config.js') +const defaultOpts = require('./default-opts.js') const url = require('url') module.exports = getAuth -function getAuth (registry, opts) { +function getAuth (registry, opts_ = {}) { if (!registry) { throw new Error('registry is required') } - opts = config(opts) + const opts = opts_.forceAuth ? opts_.forceAuth : { ...defaultOpts, ...opts_ } const AUTH = {} const regKey = registry && registryKey(registry) - if (opts.forceAuth) { - opts = opts.forceAuth - } const doKey = (key, alias) => addKey(opts, AUTH, regKey, key, alias) doKey('token') doKey('_authToken', 'token') diff --git a/check-response.js b/check-response.js index 3137ea7..933c8f7 100644 --- a/check-response.js +++ b/check-response.js @@ -1,12 +1,13 @@ 'use strict' -const config = require('./config.js') const errors = require('./errors.js') const LRU = require('lru-cache') const { Response } = require('minipass-fetch') +const defaultOpts = require('./default-opts.js') + module.exports = checkResponse -function checkResponse (method, res, registry, startTime, opts) { - opts = config(opts) +function checkResponse (method, res, registry, startTime, opts_ = {}) { + const opts = { ...defaultOpts, ...opts_ } if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache')) { opts.log.notice('', res.headers.get('npm-notice')) } diff --git a/config.js b/config.js deleted file mode 100644 index d6681d0..0000000 --- a/config.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict' - -const pkg = require('./package.json') -const figgyPudding = require('figgy-pudding') -const silentLog = require('./silentlog.js') -const ciDetect = require('@npmcli/ci-detect') - -const AUTH_REGEX = /^(?:.*:)?(token|_authToken|username|_password|password|email|always-auth|_auth|otp)$/ -const SCOPE_REGISTRY_REGEX = /@.*:registry$/gi -module.exports = figgyPudding({ - agent: {}, - algorithms: {}, - body: {}, - ca: {}, - cache: {}, - cert: {}, - 'fetch-retries': {}, - 'fetch-retry-factor': {}, - 'fetch-retry-maxtimeout': {}, - 'fetch-retry-mintimeout': {}, - 'force-auth': {}, - forceAuth: 'force-auth', - gzip: {}, - headers: {}, - 'https-proxy': {}, - 'ignore-body': {}, - ignoreBody: 'ignore-body', - integrity: {}, - 'is-from-ci': 'isFromCI', - isFromCI: { - default () { - return ciDetect() - } - }, - key: {}, - 'local-address': {}, - log: { - default: silentLog - }, - 'map-json': 'mapJson', - mapJSON: 'mapJson', - mapJson: {}, - 'max-sockets': 'maxsockets', - maxsockets: { - default: 12 - }, - memoize: {}, - method: { - default: 'GET' - }, - 'no-proxy': {}, - noproxy: {}, - 'npm-session': 'npmSession', - npmSession: {}, - offline: {}, - otp: {}, - 'prefer-offline': {}, - 'prefer-online': {}, - projectScope: {}, - 'project-scope': 'projectScope', - proxy: {}, - query: {}, - refer: {}, - referer: 'refer', - registry: { - default: 'https://registry.npmjs.org/' - }, - retry: {}, - scope: {}, - spec: {}, - 'strict-ssl': {}, - timeout: { - default: 30 * 1000 - }, - 'user-agent': { - default: `${ - pkg.name - }@${ - pkg.version - }/node@${ - process.version - }+${ - process.arch - } (${ - process.platform - })` - } -}, { - other (key) { - return key.match(AUTH_REGEX) || key.match(SCOPE_REGISTRY_REGEX) - } -}) diff --git a/default-opts.js b/default-opts.js new file mode 100644 index 0000000..4d51da4 --- /dev/null +++ b/default-opts.js @@ -0,0 +1,22 @@ +const pkg = require('./package.json') +const ciDetect = require('@npmcli/ci-detect') +module.exports = { + isFromCI: ciDetect(), + log: require('./silentlog.js'), + maxSockets: 12, + method: 'GET', + registry: 'https://registry.npmjs.org/', + timeout: 30 * 1000, + strictSSL: true, + noProxy: process.env.NOPROXY, + userAgent: `${pkg.name + }@${ + pkg.version + }/node@${ + process.version + }+${ + process.arch + } (${ + process.platform + })` +} diff --git a/index.js b/index.js index 47d0ab6..ffd8127 100644 --- a/index.js +++ b/index.js @@ -2,9 +2,7 @@ const Buffer = require('safe-buffer').Buffer -const ciDetect = require('@npmcli/ci-detect') const checkResponse = require('./check-response.js') -const config = require('./config.js') const getAuth = require('./auth.js') const fetch = require('make-fetch-happen') const JSONStream = require('minipass-json-stream') @@ -14,6 +12,8 @@ const url = require('url') const zlib = require('minizlib') const Minipass = require('minipass') +const defaultOpts = require('./default-opts.js') + // WhatWG URL throws if it's not fully resolved const urlIsValid = u => { try { @@ -24,12 +24,15 @@ const urlIsValid = u => { } module.exports = regFetch -function regFetch (uri, opts) { - opts = config(opts) - const registry = ( +function regFetch (uri, /* istanbul ignore next */ opts_ = {}) { + const opts = { + ...defaultOpts, + ...opts_ + } + const registry = opts.registry = ( (opts.spec && pickRegistry(opts.spec, opts)) || opts.registry || - /* istanbul ignore next: default set in figgy pudding config */ + /* istanbul ignore next */ 'https://registry.npmjs.org/' ) @@ -41,9 +44,7 @@ function regFetch (uri, opts) { }` } - const method = opts.method || - /* istanbul ignore next: default set in figgy pudding config */ - 'GET' + const method = opts.method || 'GET' // through that takes into account the scope, the prefix of `uri`, etc const startTime = Date.now() @@ -98,21 +99,21 @@ function regFetch (uri, opts) { headers, integrity: opts.integrity, key: opts.key, - localAddress: opts['local-address'], - maxSockets: opts.maxsockets, + localAddress: opts.localAddress, + maxSockets: opts.maxSockets, memoize: opts.memoize, method: method, - noProxy: opts['no-proxy'] || opts.noproxy, - proxy: opts['https-proxy'] || opts.proxy, + noProxy: opts.noProxy, + proxy: opts.httpsProxy || opts.proxy, referer: opts.refer, - retry: opts.retry != null ? opts.retry : { - retries: opts['fetch-retries'], - factor: opts['fetch-retry-factor'], - minTimeout: opts['fetch-retry-mintimeout'], - maxTimeout: opts['fetch-retry-maxtimeout'] + retry: opts.retry ? opts.retry : { + retries: opts.fetchRetries, + factor: opts.fetchRetryFactor, + minTimeout: opts.fetchRetryMintimeout, + maxTimeout: opts.fetchRetryMaxtimeout }, - strictSSL: !!opts['strict-ssl'], - timeout: opts.timeout + strictSSL: opts.strictSSL, + timeout: opts.timeout || 30 * 1000 }).then(res => checkResponse( method, res, registry, startTime, opts )) @@ -126,9 +127,9 @@ function fetchJSON (uri, opts) { } module.exports.json.stream = fetchJSONStream -function fetchJSONStream (uri, jsonPath, opts) { - opts = config(opts) - const parser = JSONStream.parse(jsonPath, opts.mapJson) +function fetchJSONStream (uri, jsonPath, /* istanbul ignore next */ opts_ = {}) { + const opts = { ...defaultOpts, ...opts_ } + const parser = JSONStream.parse(jsonPath, opts.mapJSON) regFetch(uri, opts).then(res => res.body.on('error', /* istanbul ignore next: unlikely and difficult to test */ @@ -138,9 +139,8 @@ function fetchJSONStream (uri, jsonPath, opts) { } module.exports.pickRegistry = pickRegistry -function pickRegistry (spec, opts) { +function pickRegistry (spec, opts = {}) { spec = npa(spec) - opts = config(opts) let registry = spec.scope && opts[spec.scope.replace(/^@?/, '@') + ':registry'] @@ -149,9 +149,7 @@ function pickRegistry (spec, opts) { } if (!registry) { - registry = opts.registry || - /* istanbul ignore next: default set by figgy pudding config */ - 'https://registry.npmjs.org/' + registry = opts.registry || 'https://registry.npmjs.org/' } return registry @@ -160,23 +158,21 @@ function pickRegistry (spec, opts) { function getCacheMode (opts) { return opts.offline ? 'only-if-cached' - : opts['prefer-offline'] + : opts.preferOffline ? 'force-cache' - : opts['prefer-online'] + : opts.preferOnline ? 'no-cache' : 'default' } function getHeaders (registry, uri, opts) { const headers = Object.assign({ - 'npm-in-ci': !!( - opts['is-from-ci'] || ciDetect() - ), - 'npm-scope': opts['project-scope'], - 'npm-session': opts['npm-session'], - 'user-agent': opts['user-agent'], + 'npm-in-ci': !!opts.isFromCI, + 'npm-scope': opts.projectScope, + 'npm-session': opts.npmSession, + 'user-agent': opts.userAgent, referer: opts.refer - }, opts.headers) + }, opts.headers || {}) const auth = getAuth(registry, opts) // If a tarball is hosted on a different place than the manifest, only send diff --git a/package.json b/package.json index ddc971a..4d795fb 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "license": "ISC", "dependencies": { "@npmcli/ci-detect": "^1.0.0", - "figgy-pudding": "^3.4.1", "lru-cache": "^5.1.1", "make-fetch-happen": "^7.1.0", "minipass": "^3.0.0", diff --git a/test/cache.js b/test/cache.js index 97f7092..82f10d2 100644 --- a/test/cache.js +++ b/test/cache.js @@ -2,7 +2,6 @@ const { promisify } = require('util') const statAsync = promisify(require('fs').stat) -const config = require('../config.js') const npmlog = require('npmlog') const path = require('path') const test = require('tap').test @@ -14,7 +13,7 @@ const testDir = require('./util/test-dir.js')(__filename) npmlog.level = process.env.LOGLEVEL || 'silent' const REGISTRY = 'https://mock.reg' -const OPTS = config({ +const OPTS = { log: npmlog, memoize: false, timeout: 0, @@ -26,7 +25,7 @@ const OPTS = config({ }, cache: path.join(testDir, '_cacache'), registry: REGISTRY -}) +} test('can cache GET requests', t => { tnock(t, REGISTRY) @@ -35,22 +34,22 @@ test('can cache GET requests', t => { .reply(200, { obj: 'value' }) return fetch.json('/normal', OPTS) .then(val => t.deepEqual(val, { obj: 'value' }, 'got expected response')) - .then(() => statAsync(OPTS.get('cache'))) + .then(() => statAsync(OPTS.cache)) .then(stat => t.ok(stat.isDirectory(), 'cache directory created')) .then(() => fetch.json('/normal', OPTS)) .then(val => t.deepEqual(val, { obj: 'value' }, 'response was cached')) }) -test('prefer-offline', t => { +test('preferOffline', t => { tnock(t, REGISTRY) - .get('/prefer-offline') + .get('/preferOffline') .times(1) .reply(200, { obj: 'value' }) - return fetch.json('/prefer-offline', OPTS.concat({ 'prefer-offline': true })) + return fetch.json('/preferOffline', { ...OPTS, preferOffline: true }) .then(val => t.deepEqual(val, { obj: 'value' }, 'got expected response')) - .then(() => statAsync(OPTS.get('cache'))) + .then(() => statAsync(OPTS.cache)) .then(stat => t.ok(stat.isDirectory(), 'cache directory created')) - .then(() => fetch.json('/prefer-offline', OPTS.concat({ 'prefer-offline': true }))) + .then(() => fetch.json('/preferOffline', { ...OPTS, preferOffline: true })) .then(val => t.deepEqual(val, { obj: 'value' }, 'response was cached')) }) @@ -61,24 +60,24 @@ test('offline', t => { .reply(200, { obj: 'value' }) return fetch.json('/offline', OPTS) .then(val => t.deepEqual(val, { obj: 'value' }, 'got expected response')) - .then(() => statAsync(OPTS.get('cache'))) + .then(() => statAsync(OPTS.cache)) .then(stat => t.ok(stat.isDirectory(), 'cache directory created')) - .then(() => fetch.json('/offline', OPTS.concat({ offline: true }))) + .then(() => fetch.json('/offline', { ...OPTS, offline: true })) .then(val => t.deepEqual(val, { obj: 'value' }, 'response was cached')) }) test('offline fails if not cached', t => - t.rejects(() => fetch('/offline-fails', OPTS.concat({ offline: true })))) + t.rejects(() => fetch('/offline-fails', { ...OPTS, offline: true }))) -test('prefer-online', t => { +test('preferOnline', t => { tnock(t, REGISTRY) - .get('/prefer-online') + .get('/preferOnline') .times(2) .reply(200, { obj: 'value' }) - return fetch.json('/prefer-online', OPTS) + return fetch.json('/preferOnline', OPTS) .then(val => t.deepEqual(val, { obj: 'value' }, 'got expected response')) - .then(() => statAsync(OPTS.get('cache'))) + .then(() => statAsync(OPTS.cache)) .then(stat => t.ok(stat.isDirectory(), 'cache directory created')) - .then(() => fetch.json('/prefer-online', OPTS.concat({ 'prefer-online': true }))) + .then(() => fetch.json('/preferOnline', { ...OPTS, preferOnline: true })) .then(val => t.deepEqual(val, { obj: 'value' }, 'response was refetched')) }) diff --git a/test/check-response.js b/test/check-response.js index 1be1436..9556608 100644 --- a/test/check-response.js +++ b/test/check-response.js @@ -1,7 +1,6 @@ const { Readable } = require('stream') const test = require('tap').test -const config = require('../config.js') const checkResponse = require('../check-response.js') const errors = require('./errors.js') const silentLog = require('../silentlog.js') @@ -27,7 +26,16 @@ test('any response error should be silent', t => { status: 400 }) t.rejects(checkResponse('get', res, 'registry', Date.now(), { ignoreBody: true }), errors.HttpErrorGeneral) - t.done() + t.end() +}) + +test('all checks are ok, nothing to report', t => { + const res = Object.assign({}, mockFetchRes, { + buffer: () => Promise.resolve(Buffer.from('ok')), + status: 400 + }) + t.rejects(checkResponse('get', res, 'registry', Date.now()), errors.HttpErrorGeneral) + t.end() }) test('log x-fetch-attempts header value', t => { @@ -37,13 +45,13 @@ test('log x-fetch-attempts header value', t => { headers, status: 400 }) - t.rejects(checkResponse('get', res, 'registry', Date.now(), config({ + t.rejects(checkResponse('get', res, 'registry', Date.now(), { log: Object.assign({}, silentLog, { http (header, msg) { t.ok(msg.endsWith('attempt #3'), 'should log correct number of attempts') } }) - }))) + })) t.plan(2) }) @@ -56,12 +64,12 @@ test('bad-formatted warning headers', t => { const res = Object.assign({}, mockFetchRes, { headers }) - t.ok(checkResponse('get', res, 'registry', Date.now(), config({ + t.ok(checkResponse('get', res, 'registry', Date.now(), { log: Object.assign({}, silentLog, { warn (header, msg) { t.fail('should not log warnings') } }) - }))) + })) t.plan(1) }) diff --git a/test/config.js b/test/config.js deleted file mode 100644 index 2029257..0000000 --- a/test/config.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict' - -const config = require('../config.js') -const npmlog = require('npmlog') -const { test } = require('tap') - -npmlog.level = process.env.LOGLEVEL || 'silent' - -test('isFromCI config option', t => { - const CI = process.env.CI - process.env.CI = false - t.teardown(t => { - process.env.CI = CI - }) - const OPTS = config({ - log: npmlog, - timeout: 0, - registry: 'https://mock.reg/' - }) - t.notOk(OPTS.isFromCI, 'should be false if not on a CI env') - t.end() -}) - -test('default timeout', t => { - const DEFAULT_OPTS = config({}) - t.equal(DEFAULT_OPTS.timeout, 30 * 1000, 'default timeout is 30s') - const SPECIFIED_OPTS = config({ - timeout: 15 * 1000 - }) - t.equal(SPECIFIED_OPTS.timeout, 15 * 1000, 'default timeout can be overridden') - t.end() -}) diff --git a/test/errors.js b/test/errors.js index 54d7de1..e3ab7c5 100644 --- a/test/errors.js +++ b/test/errors.js @@ -1,6 +1,5 @@ 'use strict' -const config = require('../config.js') const npa = require('npm-package-arg') const npmlog = require('npmlog') const test = require('tap').test @@ -9,7 +8,7 @@ const tnock = require('./util/tnock.js') const fetch = require('../index.js') npmlog.level = process.env.LOGLEVEL || 'silent' -const OPTS = config({ +const OPTS = { log: npmlog, timeout: 0, retry: { @@ -19,7 +18,7 @@ const OPTS = config({ maxTimeout: 10 }, registry: 'https://mock.reg/' -}) +} test('generic request errors', t => { tnock(t, OPTS.registry) @@ -69,9 +68,10 @@ test('pkgid with `opts.spec`', t => { tnock(t, OPTS.registry) .get('/ohno/_rewrite/ohyeah') .reply(400, 'failwhale!') - return fetch('/ohno/_rewrite/ohyeah', OPTS.concat({ + return fetch('/ohno/_rewrite/ohyeah', { + ...OPTS, spec: npa('foo@1.2.3') - })) + }) .then( () => { throw new Error('should not have succeeded!') }, err => t.equal(err.pkgid, 'foo@1.2.3', 'opts.spec used for pkgid') diff --git a/test/index.js b/test/index.js index 93818b4..2ad80ad 100644 --- a/test/index.js +++ b/test/index.js @@ -2,7 +2,6 @@ const Buffer = require('safe-buffer').Buffer -const config = require('../config.js') const Minipass = require('minipass') const npmlog = require('npmlog') const silentLog = require('../silentlog.js') @@ -14,7 +13,7 @@ const zlib = require('zlib') const fetch = require('../index.js') npmlog.level = process.env.LOGLEVEL || 'silent' -const OPTS = config({ +const OPTS = { // just to make sure we hit the second branch when // we are ACTUALLY in CI isFromCI: false, @@ -27,13 +26,16 @@ const OPTS = config({ maxTimeout: 10 }, registry: 'https://mock.reg/' -}) +} test('hello world', t => { tnock(t, OPTS.registry) .get('/hello') .reply(200, { hello: 'world' }) - return fetch('/hello', OPTS) + return fetch('/hello', { + method: false, // will fall back to GET if falsey, + ...OPTS + }) .then(res => { t.equal(res.status, 200, 'got successful response') return res.json() @@ -54,10 +56,11 @@ test('JSON body param', t => { }, 'got the JSON version of the body') return reqBody }) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: { hello: 'world' } - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200) @@ -81,10 +84,11 @@ test('buffer body param', t => { ) return reqBody }) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: Buffer.from('hello', 'utf8') - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200) @@ -110,10 +114,11 @@ test('stream body param', t => { }) const stream = new Minipass() setImmediate(() => stream.end(JSON.stringify({ hello: 'world' }))) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: stream - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200) @@ -135,11 +140,12 @@ test('JSON body param', t => { .post('/hello') // NOTE: can't really test the body itself here because nock freaks out. .reply(200) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: { hello: 'world' }, gzip: true - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200, 'request succeeded') @@ -166,11 +172,12 @@ test('gzip + buffer body param', t => { ) return reqBody }) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: Buffer.from('hello', 'utf8'), gzip: true - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200) @@ -201,7 +208,8 @@ test('gzip + stream body param', t => { }) const stream = new Minipass() setImmediate(() => stream.end(JSON.stringify({ hello: 'world' }))) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'POST', body: stream, gzip: true, @@ -209,7 +217,7 @@ test('gzip + stream body param', t => { everything: undefined, is: undefined } - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200) @@ -222,18 +230,20 @@ test('query strings', t => { tnock(t, OPTS.registry) .get('/hello?hi=there&who=wor%20ld') .reply(200, { hello: 'world' }) - return fetch.json('/hello?hi=there', OPTS.concat({ + return fetch.json('/hello?hi=there', { + ...OPTS, query: 'who=wor ld' - })).then(json => t.equal(json.hello, 'world', 'query-string merged')) + }).then(json => t.equal(json.hello, 'world', 'query-string merged')) }) test('query strings - undefined values', t => { tnock(t, OPTS.registry) .get('/hello?who=wor%20ld') .reply(200, { ok: true }) - return fetch.json('/hello', OPTS.concat({ + return fetch.json('/hello', { + ...OPTS, query: { hi: undefined, who: 'wor ld' } - })).then(json => t.ok(json.ok, 'undefined keys not included in query string')) + }).then(json => t.ok(json.ok, 'undefined keys not included in query string')) }) test('json()', t => { @@ -259,17 +269,18 @@ test('fetch.json.stream()', t => { }) }) -test('fetch.json.stream opts.mapJson', t => { +test('fetch.json.stream opts.mapJSON', t => { tnock(t, OPTS.registry).get('/hello').reply(200, { a: 1, b: 2, c: 3 }) - return fetch.json.stream('/hello', '*', OPTS.concat({ - mapJson (value, [key]) { + return fetch.json.stream('/hello', '*', { + ...OPTS, + mapJSON (value, [key]) { return [key, value] } - })).collect().then(data => { + }).collect().then(data => { t.deepEqual(data, [ ['a', 1], ['b', 2], @@ -279,11 +290,12 @@ test('fetch.json.stream opts.mapJson', t => { }) test('fetch.json.stream gets fetch error on stream', t => { - return t.rejects(fetch.json.stream('/hello', '*', OPTS.concat({ + return t.rejects(fetch.json.stream('/hello', '*', { + ...OPTS, body: Promise.reject(new Error('no body for you')), method: 'POST', gzip: true // make sure we don't gzip the promise, lol! - })).collect(), { + }).collect(), { message: 'no body for you' }) }) @@ -292,7 +304,7 @@ test('opts.ignoreBody', t => { tnock(t, OPTS.registry) .get('/hello') .reply(200, { hello: 'world' }) - return fetch('/hello', OPTS.concat({ ignoreBody: true })) + return fetch('/hello', { ...OPTS, ignoreBody: true }) .then(res => { t.equal(res.body, null, 'body omitted') }) @@ -302,9 +314,10 @@ test('method configurable', t => { tnock(t, OPTS.registry) .delete('/hello') .reply(200) - const opts = OPTS.concat({ + const opts = { + ...OPTS, method: 'DELETE' - }) + } return fetch('/hello', opts) .then(res => { t.equal(res.status, 200, 'successfully used DELETE method') @@ -317,14 +330,15 @@ test('npm-notice header logging', t => { .reply(200, { hello: 'world' }, { 'npm-notice': 'npm <3 u' }) - const opts = OPTS.concat({ + const opts = { + ...OPTS, log: Object.assign({}, silentLog, { notice (header, msg) { t.equal(header, '', 'empty log header thing') t.equal(msg, 'npm <3 u', 'logged out npm-notice at NOTICE level') } }) - }) + } t.plan(3) return fetch('/hello', opts) .then(res => t.equal(res.status, 200, 'got successful response')) @@ -337,13 +351,13 @@ test('optionally verifies request body integrity', t => { .times(2) .reply(200, 'hello') const integrity = ssri.fromData('hello') - return fetch('/hello', OPTS.concat({ integrity })) + return fetch('/hello', { ...OPTS, integrity }) .then(res => res.buffer()) .then(buf => t.equal( buf.toString('utf8'), 'hello', 'successfully got the right data') ) .then(() => { - return fetch('/hello', OPTS.concat({ integrity: 'sha1-nope' })) + return fetch('/hello', { ...OPTS, integrity: 'sha1-nope' }) .then(res => { t.ok(res.body, 'no error until body starts getting read') return res @@ -398,18 +412,20 @@ test('pickRegistry through opts.spec', t => { .get('/pkg') .times(2) .reply(200, { source: scopedReg }) - return fetch.json('/pkg', OPTS.concat({ + return fetch.json('/pkg', { + ...OPTS, spec: 'pkg@1.2.3', '@myscope:registry': scopedReg - })).then(json => t.equal( + }).then(json => t.equal( json.source, OPTS.registry, 'request made to main registry' - )).then(() => fetch.json('/pkg', OPTS.concat({ + )).then(() => fetch.json('/pkg', { + ...OPTS, spec: 'pkg@1.2.3', '@myscope:registry': scopedReg, scope: '@myscope' - }))).then(json => t.equal( + })).then(json => t.equal( json.source, scopedReg, 'request made to scope registry using opts.scope' @@ -427,14 +443,15 @@ test('log warning header info', t => { tnock(t, OPTS.registry) .get('/hello') .reply(200, { hello: 'world' }, { Warning: '199 - "ENOTFOUND" "Wed, 21 Oct 2015 07:28:00 GMT"' }) - const opts = OPTS.concat({ + const opts = { + ...OPTS, log: Object.assign({}, silentLog, { warn (header, msg) { t.equal(header, 'registry', 'expected warn log header') t.equal(msg, `Using stale data from ${OPTS.registry} because the host is inaccessible -- are you offline?`, 'logged out at WARNING level') } }) - }) + } t.plan(3) return fetch('/hello', opts) .then(res => t.equal(res.status, 200, 'got successful response'))