From aa200e4d63f8720738ca4c07bd5a38c2f89a402f Mon Sep 17 00:00:00 2001 From: Jeff Valore Date: Tue, 30 Jan 2018 07:08:31 -0500 Subject: [PATCH] feat(publish): Publish command uses publishConfig.access in package.json (#5290) * feat(publish): Publish command uses publishConfig.access in package.json For npm compatability, `yarn publish` should check `publishConfig.access` in package.json and use it as if the `--access` option was passed. #5279 * WIP: CI test failure debugging * WIP: CI test failure debugging * WIP: CI test failure debugging * fix CI errors by mocking npm password prompt * use jest expect().toBeCalledWith() for publish command tests --- __tests__/__mocks__/npm-registry.js | 10 ++ __tests__/commands/publish.js | 101 ++++++++++++++++++ .../fixtures/publish/minimal/package.json | 4 + .../fixtures/publish/public/package.json | 7 ++ src/cli/commands/publish.js | 11 +- 5 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 __tests__/__mocks__/npm-registry.js create mode 100644 __tests__/commands/publish.js create mode 100644 __tests__/fixtures/publish/minimal/package.json create mode 100644 __tests__/fixtures/publish/public/package.json diff --git a/__tests__/__mocks__/npm-registry.js b/__tests__/__mocks__/npm-registry.js new file mode 100644 index 0000000000..c5f54cb2a7 --- /dev/null +++ b/__tests__/__mocks__/npm-registry.js @@ -0,0 +1,10 @@ +/* @flow */ + +import Registry from '../../src/registries/base-registry.js'; +import type {RegistryRequestOptions} from '../../src/registries/base-registry.js'; + +export default class NpmRegistry extends Registry { + request(pathname: string, opts?: RegistryRequestOptions = {}, packageName: ?string): Promise<*> { + return new Promise(resolve => resolve()); + } +} diff --git a/__tests__/commands/publish.js b/__tests__/commands/publish.js new file mode 100644 index 0000000000..aaee892112 --- /dev/null +++ b/__tests__/commands/publish.js @@ -0,0 +1,101 @@ +/* @flow */ + +import {run as buildRun} from './_helpers.js'; +import {run as publish} from '../../src/cli/commands/publish.js'; +import {ConsoleReporter} from '../../src/reporters/index.js'; + +const path = require('path'); + +const fixturesLoc = path.join(__dirname, '..', 'fixtures', 'publish'); + +const setupMocks = function(config) { + // Mock actual network request so that no package is actually published. + // $FlowFixMe + config.registries.npm.request = jest.fn(); + config.registries.npm.request.mockReturnValue( + new Promise(resolve => { + resolve({status: 200}); + }), + ); + + // Mock the npm login name. Otherwise yarn will prompt for it and break CI. + // $FlowFixMe + config.registries.npm.getAuth = jest.fn(); + config.registries.npm.getAuth.mockReturnValue('test'); +}; + +const runPublish = buildRun.bind( + null, + ConsoleReporter, + fixturesLoc, + async (args, flags, config, reporter, lockfile, getStdout): Promise => { + setupMocks(config); + await publish(config, reporter, flags, args); + return getStdout(); + }, +); + +test.concurrent('publish should default access to undefined', () => { + return runPublish([], {newVersion: '0.0.1'}, 'minimal', config => { + expect(config.registries.npm.request).toBeCalledWith( + expect.any(String), + expect.objectContaining({ + body: expect.objectContaining({ + access: undefined, + }), + }), + ); + }); +}); + +test.concurrent('publish should accept `--access restricted` argument', () => { + return runPublish([], {newVersion: '0.0.1', access: 'restricted'}, 'minimal', config => { + expect(config.registries.npm.request).toBeCalledWith( + expect.any(String), + expect.objectContaining({ + body: expect.objectContaining({ + access: 'restricted', + }), + }), + ); + }); +}); + +test.concurrent('publish should accept `--access public` argument', () => { + return runPublish([], {newVersion: '0.0.1', access: 'public'}, 'minimal', config => { + expect(config.registries.npm.request).toBeCalledWith( + expect.any(String), + expect.objectContaining({ + body: expect.objectContaining({ + access: 'public', + }), + }), + ); + }); +}); + +test.concurrent('publish should use publishConfig.access in package manifest', () => { + return runPublish([], {newVersion: '0.0.1'}, 'public', config => { + expect(config.registries.npm.request).toBeCalledWith( + expect.any(String), + expect.objectContaining({ + body: expect.objectContaining({ + access: 'public', + }), + }), + ); + }); +}); + +test.concurrent('publish should allow `--access` to override publishConfig.access', () => { + return runPublish([], {newVersion: '0.0.1', access: 'restricted'}, 'public', config => { + expect(config.registries.npm.request).toBeCalledWith( + expect.any(String), + expect.objectContaining({ + body: expect.objectContaining({ + access: 'restricted', + }), + }), + ); + }); +}); diff --git a/__tests__/fixtures/publish/minimal/package.json b/__tests__/fixtures/publish/minimal/package.json new file mode 100644 index 0000000000..1545befa16 --- /dev/null +++ b/__tests__/fixtures/publish/minimal/package.json @@ -0,0 +1,4 @@ +{ + "name": "test", + "version": "0.0.0" +} diff --git a/__tests__/fixtures/publish/public/package.json b/__tests__/fixtures/publish/public/package.json new file mode 100644 index 0000000000..0de9c86d5d --- /dev/null +++ b/__tests__/fixtures/publish/public/package.json @@ -0,0 +1,7 @@ +{ + "name": "test", + "version": "0.0.0", + "publishConfig": { + "access": "public" + } +} diff --git a/src/cli/commands/publish.js b/src/cli/commands/publish.js index ee16431e4b..d53ed1ebe7 100644 --- a/src/cli/commands/publish.js +++ b/src/cli/commands/publish.js @@ -27,8 +27,15 @@ export function hasWrapper(commander: Object, args: Array): boolean { } async function publish(config: Config, pkg: any, flags: Object, dir: string): Promise { + let access = flags.access; + + // if no access level is provided, check package.json for `publishConfig.access` + // see: https://docs.npmjs.com/files/package.json#publishconfig + if (!access && pkg && pkg.publishConfig && pkg.publishConfig.access) { + access = pkg.publishConfig.access; + } + // validate access argument - const access = flags.access; if (access && access !== 'public' && access !== 'restricted') { throw new MessageError(config.reporter.lang('invalidAccess')); } @@ -69,7 +76,7 @@ async function publish(config: Config, pkg: any, flags: Object, dir: string): Pr // create body const root = { _id: pkg.name, - access: flags.access, + access, name: pkg.name, description: pkg.description, 'dist-tags': {