From 026c63316026c1f8c361ac594416cc1bc210f613 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Tue, 11 Jan 2022 13:11:55 -0800 Subject: [PATCH] fix: quirks with version commands and config retrieval (#423) * fix: quirks with version commands not always handling errors properly * fix: use the right accessor for getting config data * fix: pr feedback * fix: pr feedback * fix: pr feedback * fix: pr feedback * Update __tests__/cmds/whoami.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/versions.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/login.test.js Co-authored-by: Kanad Gupta * Update __tests__/cmds/login.test.js Co-authored-by: Kanad Gupta * fix: prettier * fix: test adjustments * fix: one more test update * fix: disabling color in all tests due to snapshot issues Co-authored-by: Kanad Gupta --- __tests__/__snapshots__/index.test.js.snap | 178 +++++++++--------- .../cmds/__snapshots__/login.test.js.snap | 5 + .../cmds/__snapshots__/openapi.test.js.snap | 16 +- .../cmds/__snapshots__/validate.test.js.snap | 32 ++-- .../cmds/__snapshots__/versions.test.js.snap | 23 +++ .../cmds/__snapshots__/whoami.test.js.snap | 3 + __tests__/cmds/docs.test.js | 30 ++- __tests__/cmds/login.test.js | 88 ++++----- __tests__/cmds/logout.test.js | 20 +- __tests__/cmds/open.test.js | 2 +- __tests__/cmds/openapi.test.js | 30 +-- __tests__/cmds/versions.test.js | 148 +++++++-------- __tests__/cmds/whoami.test.js | 20 +- __tests__/set-node-env.js | 3 + src/cmds/docs/edit.js | 6 +- src/cmds/docs/index.js | 8 +- src/cmds/login.js | 10 +- src/cmds/logout.js | 2 +- src/cmds/open.js | 8 +- src/cmds/openapi.js | 6 +- src/cmds/versions/create.js | 6 +- src/cmds/versions/delete.js | 15 +- src/cmds/versions/index.js | 14 +- src/cmds/versions/update.js | 12 +- src/cmds/whoami.js | 2 +- src/index.js | 6 +- src/lib/help.js | 10 +- src/lib/versionSelect.js | 6 +- 28 files changed, 350 insertions(+), 359 deletions(-) create mode 100644 __tests__/cmds/__snapshots__/login.test.js.snap create mode 100644 __tests__/cmds/__snapshots__/versions.test.js.snap create mode 100644 __tests__/cmds/__snapshots__/whoami.test.js.snap diff --git a/__tests__/__snapshots__/index.test.js.snap b/__tests__/__snapshots__/index.test.js.snap index 9ff826790..6422ad648 100644 --- a/__tests__/__snapshots__/index.test.js.snap +++ b/__tests__/__snapshots__/index.test.js.snap @@ -4,30 +4,30 @@ exports[`cli --help should not surface args that are designated as hidden 1`] = " Upload, or resync, your OpenAPI/Swagger definition to ReadMe. -Usage +Usage rdme openapi [file] [options] -Options +Options - --key string Project API key - --id string Unique identifier for your API definition. Use this if you're re-uploading an + --key string Project API key + --id string Unique identifier for your API definition. Use this if you're re-uploading an existing API definition - --version string Project version - -h, --help Display this usage guide + --version string Project version + -h, --help Display this usage guide -Related commands +Related commands - $ rdme validate Validate your OpenAPI/Swagger definition. - $ rdme swagger Alias for \`rdme openapi\`. [deprecated] + $ rdme validate Validate your OpenAPI/Swagger definition. + $ rdme swagger Alias for \`rdme openapi\`. [deprecated] " `; exports[`cli --help should print help 1`] = ` " - πŸ“– rdme + πŸ“– rdme - a utlity for interacting with ReadMe + a utlity for interacting with ReadMe . .\\\\ /. ’ β€˜ β€˜ β€˜ @@ -51,46 +51,46 @@ exports[`cli --help should print help 1`] = ` β€˜READn’ \\\\._./ \\\\__./ \\\\.../ β€˜MEE*’ * /* -Usage +Usage rdme [arguments] -Options +Options - -h, --help Display this usage guide - -v, --version Show the current rdme version + -h, --help Display this usage guide + -v, --version Show the current rdme version -Administration +Administration - $ rdme login Login to a ReadMe project. - $ rdme logout Logs the currently authenticated user out of ReadMe. - $ rdme whoami Displays the current user and project authenticated with ReadMe. + $ rdme login Login to a ReadMe project. + $ rdme logout Logs the currently authenticated user out of ReadMe. + $ rdme whoami Displays the current user and project authenticated with ReadMe. -Upload OpenAPI/Swagger definitions +Upload OpenAPI/Swagger definitions - $ rdme openapi Upload, or resync, your OpenAPI/Swagger definition to ReadMe. - $ rdme validate Validate your OpenAPI/Swagger definition. - $ rdme swagger Alias for \`rdme openapi\`. [deprecated] + $ rdme openapi Upload, or resync, your OpenAPI/Swagger definition to ReadMe. + $ rdme validate Validate your OpenAPI/Swagger definition. + $ rdme swagger Alias for \`rdme openapi\`. [deprecated] -Documentation +Documentation - $ rdme docs Sync a folder of markdown files to your ReadMe project. - $ rdme docs:edit Edit a single file from your ReadMe project without saving locally. + $ rdme docs Sync a folder of markdown files to your ReadMe project. + $ rdme docs:edit Edit a single file from your ReadMe project without saving locally. -Versions +Versions - $ rdme versions List versions available in your project or get a version by SemVer + $ rdme versions List versions available in your project or get a version by SemVer (https://semver.org/). - $ rdme versions:create Create a new version for your project. - $ rdme versions:update Update an existing version for your project. - $ rdme versions:delete Delete a version associated with your ReadMe project. + $ rdme versions:create Create a new version for your project. + $ rdme versions:update Update an existing version for your project. + $ rdme versions:delete Delete a version associated with your ReadMe project. -Other useful commands +Other useful commands - $ rdme oas Helpful OpenAPI generation tooling. - $ rdme open Open your current ReadMe project in the browser. + $ rdme oas Helpful OpenAPI generation tooling. + $ rdme open Open your current ReadMe project in the browser. -Run rdme help  for help with a specific command. +Run rdme help for help with a specific command. To get more help with ReadMe, check out our docs at https://docs.readme.com. " @@ -98,9 +98,9 @@ To get more help with ReadMe, check out our docs at https://docs.readme.com. exports[`cli --help should print help for the \`-H\` alias 1`] = ` " - πŸ“– rdme + πŸ“– rdme - a utlity for interacting with ReadMe + a utlity for interacting with ReadMe . .\\\\ /. ’ β€˜ β€˜ β€˜ @@ -124,46 +124,46 @@ exports[`cli --help should print help for the \`-H\` alias 1`] = ` β€˜READn’ \\\\._./ \\\\__./ \\\\.../ β€˜MEE*’ * /* -Usage +Usage rdme [arguments] -Options +Options - -h, --help Display this usage guide - -v, --version Show the current rdme version + -h, --help Display this usage guide + -v, --version Show the current rdme version -Administration +Administration - $ rdme login Login to a ReadMe project. - $ rdme logout Logs the currently authenticated user out of ReadMe. - $ rdme whoami Displays the current user and project authenticated with ReadMe. + $ rdme login Login to a ReadMe project. + $ rdme logout Logs the currently authenticated user out of ReadMe. + $ rdme whoami Displays the current user and project authenticated with ReadMe. -Upload OpenAPI/Swagger definitions +Upload OpenAPI/Swagger definitions - $ rdme openapi Upload, or resync, your OpenAPI/Swagger definition to ReadMe. - $ rdme validate Validate your OpenAPI/Swagger definition. - $ rdme swagger Alias for \`rdme openapi\`. [deprecated] + $ rdme openapi Upload, or resync, your OpenAPI/Swagger definition to ReadMe. + $ rdme validate Validate your OpenAPI/Swagger definition. + $ rdme swagger Alias for \`rdme openapi\`. [deprecated] -Documentation +Documentation - $ rdme docs Sync a folder of markdown files to your ReadMe project. - $ rdme docs:edit Edit a single file from your ReadMe project without saving locally. + $ rdme docs Sync a folder of markdown files to your ReadMe project. + $ rdme docs:edit Edit a single file from your ReadMe project without saving locally. -Versions +Versions - $ rdme versions List versions available in your project or get a version by SemVer + $ rdme versions List versions available in your project or get a version by SemVer (https://semver.org/). - $ rdme versions:create Create a new version for your project. - $ rdme versions:update Update an existing version for your project. - $ rdme versions:delete Delete a version associated with your ReadMe project. + $ rdme versions:create Create a new version for your project. + $ rdme versions:update Update an existing version for your project. + $ rdme versions:delete Delete a version associated with your ReadMe project. -Other useful commands +Other useful commands - $ rdme oas Helpful OpenAPI generation tooling. - $ rdme open Open your current ReadMe project in the browser. + $ rdme oas Helpful OpenAPI generation tooling. + $ rdme open Open your current ReadMe project in the browser. -Run rdme help  for help with a specific command. +Run rdme help for help with a specific command. To get more help with ReadMe, check out our docs at https://docs.readme.com. " @@ -173,22 +173,22 @@ exports[`cli --help should print usage for a given command 1`] = ` " Upload, or resync, your OpenAPI/Swagger definition to ReadMe. -Usage +Usage rdme openapi [file] [options] -Options +Options - --key string Project API key - --id string Unique identifier for your API definition. Use this if you're re-uploading an + --key string Project API key + --id string Unique identifier for your API definition. Use this if you're re-uploading an existing API definition - --version string Project version - -h, --help Display this usage guide + --version string Project version + -h, --help Display this usage guide -Related commands +Related commands - $ rdme validate Validate your OpenAPI/Swagger definition. - $ rdme swagger Alias for \`rdme openapi\`. [deprecated] + $ rdme validate Validate your OpenAPI/Swagger definition. + $ rdme swagger Alias for \`rdme openapi\`. [deprecated] " `; @@ -196,22 +196,22 @@ exports[`cli --help should print usage for a given command if supplied as \`help " Upload, or resync, your OpenAPI/Swagger definition to ReadMe. -Usage +Usage rdme openapi [file] [options] -Options +Options - --key string Project API key - --id string Unique identifier for your API definition. Use this if you're re-uploading an + --key string Project API key + --id string Unique identifier for your API definition. Use this if you're re-uploading an existing API definition - --version string Project version - -h, --help Display this usage guide + --version string Project version + -h, --help Display this usage guide -Related commands +Related commands - $ rdme validate Validate your OpenAPI/Swagger definition. - $ rdme swagger Alias for \`rdme openapi\`. [deprecated] + $ rdme validate Validate your OpenAPI/Swagger definition. + $ rdme swagger Alias for \`rdme openapi\`. [deprecated] " `; @@ -219,21 +219,21 @@ exports[`cli --help should show related commands for a subcommands help menu 1`] " List versions available in your project or get a version by SemVer (https://semver.org/). -Usage +Usage rdme versions [options] -Options +Options - --key string Project API key - --version string A specific project version to view - --raw Return raw output from the API instead of in a \\"pretty\\" format. - -h, --help Display this usage guide + --key string Project API key + --version string A specific project version to view + --raw Return raw output from the API instead of in a \\"pretty\\" format. + -h, --help Display this usage guide -Related commands +Related commands - $ rdme versions:create Create a new version for your project. - $ rdme versions:update Update an existing version for your project. - $ rdme versions:delete Delete a version associated with your ReadMe project. + $ rdme versions:create Create a new version for your project. + $ rdme versions:update Update an existing version for your project. + $ rdme versions:delete Delete a version associated with your ReadMe project. " `; diff --git a/__tests__/cmds/__snapshots__/login.test.js.snap b/__tests__/cmds/__snapshots__/login.test.js.snap new file mode 100644 index 000000000..e39ee3c50 --- /dev/null +++ b/__tests__/cmds/__snapshots__/login.test.js.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`rdme login should post to /login on the API 1`] = `"Successfully logged in as user@example.com to the subdomain project."`; + +exports[`rdme login should send 2fa token if provided 1`] = `"Successfully logged in as user@example.com to the subdomain project."`; diff --git a/__tests__/cmds/__snapshots__/openapi.test.js.snap b/__tests__/cmds/__snapshots__/openapi.test.js.snap index 98997fea1..2cdff9232 100644 --- a/__tests__/cmds/__snapshots__/openapi.test.js.snap +++ b/__tests__/cmds/__snapshots__/openapi.test.js.snap @@ -3,15 +3,15 @@ exports[`rdme openapi error handling should throw an error if an invalid OpenAPI 3.1 definition is supplied 1`] = ` [SyntaxError: OpenAPI schema validation failed. -REQUIRED must have required property 'name' +REQUIRED must have required property 'name' -  24 | "components": { -  25 | "securitySchemes": { -> 26 | "tlsAuth": { -  | ^ ☹️ name is missing here! -  27 | "type": "mutualTLS" -  28 | } -  29 | }] + 24 | "components": { + 25 | "securitySchemes": { +> 26 | "tlsAuth": { + | ^ ☹️ name is missing here! + 27 | "type": "mutualTLS" + 28 | } + 29 | }] `; exports[`rdme openapi should bundle and upload the expected content 1`] = ` diff --git a/__tests__/cmds/__snapshots__/validate.test.js.snap b/__tests__/cmds/__snapshots__/validate.test.js.snap index eab278312..9b9bb403f 100644 --- a/__tests__/cmds/__snapshots__/validate.test.js.snap +++ b/__tests__/cmds/__snapshots__/validate.test.js.snap @@ -3,27 +3,27 @@ exports[`rdme validate error handling should throw an error if an in valid Swagger definition is supplied 1`] = ` [Error: Swagger schema validation failed. -ADDITIONAL PROPERTY must NOT have additional properties +ADDITIONAL PROPERTY must NOT have additional properties -  23 | "description": "Find out more", -  24 | "url": "http://swagger.io", -> 25 | "this-shouldnt-be-here": true -  | ^^^^^^^^^^^^^^^^^^^^^^^ 😲 this-shouldnt-be-here is not expected to be here! -  26 | } -  27 | } -  28 | ],] + 23 | "description": "Find out more", + 24 | "url": "http://swagger.io", +> 25 | "this-shouldnt-be-here": true + | ^^^^^^^^^^^^^^^^^^^^^^^ 😲 this-shouldnt-be-here is not expected to be here! + 26 | } + 27 | } + 28 | ],] `; exports[`rdme validate error handling should throw an error if an invalid OpenAPI 3.1 definition is supplied 1`] = ` [Error: OpenAPI schema validation failed. -REQUIRED must have required property 'name' +REQUIRED must have required property 'name' -  24 | "components": { -  25 | "securitySchemes": { -> 26 | "tlsAuth": { -  | ^ ☹️ name is missing here! -  27 | "type": "mutualTLS" -  28 | } -  29 | }] + 24 | "components": { + 25 | "securitySchemes": { +> 26 | "tlsAuth": { + | ^ ☹️ name is missing here! + 27 | "type": "mutualTLS" + 28 | } + 29 | }] `; diff --git a/__tests__/cmds/__snapshots__/versions.test.js.snap b/__tests__/cmds/__snapshots__/versions.test.js.snap new file mode 100644 index 000000000..38d68adfa --- /dev/null +++ b/__tests__/cmds/__snapshots__/versions.test.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`rdme versions* rdme versions should get a specific version object if version flag provided 1`] = ` +"Version: 1.0.0 +Codename: None +Created on: 2019-06-17T22:39:56.462Z +Released on: undefined +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Is deprecated β”‚ Is hidden β”‚ Is beta β”‚ Is stable β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ no β”‚ no β”‚ no β”‚ yes β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜" +`; + +exports[`rdme versions* rdme versions should make a request to get a list of existing versions 1`] = ` +"β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Version β”‚ Codename β”‚ Is deprecated β”‚ Is hidden β”‚ Is beta β”‚ Is stable β”‚ Created on β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 1.0.0 β”‚ None β”‚ no β”‚ no β”‚ no β”‚ yes β”‚ 2019-06-17T22:39:56.462Z β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 2.0.0 β”‚ None β”‚ no β”‚ no β”‚ no β”‚ yes β”‚ 2019-06-17T22:39:56.462Z β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜" +`; diff --git a/__tests__/cmds/__snapshots__/whoami.test.js.snap b/__tests__/cmds/__snapshots__/whoami.test.js.snap new file mode 100644 index 000000000..1ce5bfa1c --- /dev/null +++ b/__tests__/cmds/__snapshots__/whoami.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`rdme whoami should return the authenticated user 1`] = `"You are currently logged in as email@example.com to the subdomain project."`; diff --git a/__tests__/cmds/docs.test.js b/__tests__/cmds/docs.test.js index 26f3351d6..fd1bc1376 100644 --- a/__tests__/cmds/docs.test.js +++ b/__tests__/cmds/docs.test.js @@ -18,7 +18,7 @@ const category = 'CATEGORY_ID'; const apiSetting = 'API_SETTING_ID'; function getNockWithVersionHeader(v) { - return nock(config.host, { + return nock(config.get('host'), { reqheaders: { 'x-readme-version': v, }, @@ -35,12 +35,10 @@ describe('rdme docs', () => { afterAll(() => nock.cleanAll()); it('should error if no api key provided', () => { - expect.assertions(1); return expect(docs.run({})).rejects.toThrow('No project API key provided. Please use `--key`.'); }); it('should error if no folder provided', () => { - expect.assertions(1); return expect(docs.run({ key, version: '1.0.0' })).rejects.toThrow( 'No folder provided. Usage `rdme docs [options]`.' ); @@ -105,7 +103,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { category, slug: anotherDoc.slug, body: anotherDoc.doc.content }); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -139,7 +137,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { category, slug: anotherDoc.slug, lastUpdatedHash: anotherDoc.hash }); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -177,7 +175,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(201, { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -246,7 +244,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(400, errorObject); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -289,7 +287,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(201, { slug: doc.data.slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -335,7 +333,7 @@ describe('rdme docs:edit', () => { .basicAuth({ user: key }) .reply(200, { category, slug }); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -367,9 +365,9 @@ describe('rdme docs:edit', () => { help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }; - const getMock = nock(config.host).get(`/api/v1/docs/${slug}`).reply(404, errorObject); + const getMock = nock(config.get('host')).get(`/api/v1/docs/${slug}`).reply(404, errorObject); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -381,7 +379,6 @@ describe('rdme docs:edit', () => { }); it('should error if doc fails validation', async () => { - expect.assertions(2); const slug = 'getting-started'; const body = 'abcdef'; @@ -392,11 +389,11 @@ describe('rdme docs:edit', () => { help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }; - const getMock = nock(config.host).get(`/api/v1/docs/${slug}`).reply(200, { body }); + const getMock = nock(config.get('host')).get(`/api/v1/docs/${slug}`).reply(200, { body }); - const putMock = nock(config.host).put(`/api/v1/docs/${slug}`).reply(400, errorObject); + const putMock = nock(config.get('host')).put(`/api/v1/docs/${slug}`).reply(400, errorObject); - const versionMock = nock(config.host) + const versionMock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); @@ -416,11 +413,10 @@ describe('rdme docs:edit', () => { }); it('should handle error if $EDITOR fails', async () => { - expect.assertions(1); const slug = 'getting-started'; const body = 'abcdef'; - const getMock = nock(config.host) + const getMock = nock(config.get('host')) .get(`/api/v1/docs/${slug}`) .reply(200, { body }) .get(`/api/v1/version/${version}`) diff --git a/__tests__/cmds/login.test.js b/__tests__/cmds/login.test.js index c7e44cd19..cf11e1edd 100644 --- a/__tests__/cmds/login.test.js +++ b/__tests__/cmds/login.test.js @@ -2,6 +2,11 @@ const nock = require('nock'); const config = require('config'); const configStore = require('../../src/lib/configstore'); const cmd = require('../../src/cmds/login'); +const APIError = require('../../src/lib/apiError'); + +const email = 'user@example.com'; +const password = '123456'; +const project = 'subdomain'; describe('rdme login', () => { beforeAll(() => nock.disableNetConnect()); @@ -11,87 +16,68 @@ describe('rdme login', () => { afterEach(() => configStore.clear()); it('should error if no project provided', () => { - return expect(cmd.run({})).rejects.toThrow('No project subdomain provided. Please use `--project`.'); + return expect(cmd.run({})).rejects.toStrictEqual( + new Error('No project subdomain provided. Please use `--project`.') + ); }); it('should error if email is invalid', () => { - expect.assertions(1); - return expect(cmd.run({ project: 'subdomain', email: 'this-is-not-an-email' })).rejects.toThrow( - 'You must provide a valid email address.' + return expect(cmd.run({ project: 'subdomain', email: 'this-is-not-an-email' })).rejects.toStrictEqual( + new Error('You must provide a valid email address.') ); }); - it('should post to /login on the API', () => { - expect.assertions(3); - const email = 'dom@readme.io'; - const password = '123456'; - const project = 'subdomain'; + it('should post to /login on the API', async () => { const apiKey = 'abcdefg'; - const mock = nock(config.host).post('/api/v1/login', { email, password, project }).reply(200, { apiKey }); + const mock = nock(config.get('host')).post('/api/v1/login', { email, password, project }).reply(200, { apiKey }); - return cmd.run({ email, password, project }).then(() => { - mock.done(); - expect(configStore.get('apiKey')).toBe(apiKey); - expect(configStore.get('email')).toBe(email); - expect(configStore.get('project')).toBe(project); - configStore.clear(); - }); - }); + await expect(cmd.run({ email, password, project })).resolves.toMatchSnapshot(); + mock.done(); - it('should error if invalid credentials are given', () => { - expect.assertions(2); - const email = 'dom@readme.io'; - const password = '123456'; - const project = 'subdomain'; + expect(configStore.get('apiKey')).toBe(apiKey); + expect(configStore.get('email')).toBe(email); + expect(configStore.get('project')).toBe(project); + configStore.clear(); + }); - const mock = nock(config.host).post('/api/v1/login', { email, password, project }).reply(401, { + it('should error if invalid credentials are given', async () => { + const errorResponse = { error: 'LOGIN_INVALID', message: 'Either your email address or password is incorrect', suggestion: 'You can reset your password at https://dash.readme.com/forgot', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); + }; - return cmd.run({ email, password, project }).catch(err => { - expect(err.code).toBe('LOGIN_INVALID'); - expect(err.message).toContain('Either your email address or password is incorrect'); - mock.done(); - }); - }); + const mock = nock(config.get('host')).post('/api/v1/login', { email, password, project }).reply(401, errorResponse); - it('should error if missing two factor token', () => { - expect.assertions(2); - const email = 'dom@readme.io'; - const password = '123456'; - const project = 'subdomain'; + await expect(cmd.run({ email, password, project })).rejects.toStrictEqual(new APIError(errorResponse)); + mock.done(); + }); - const mock = nock(config.host).post('/api/v1/login', { email, password, project }).reply(401, { + it('should error if missing two factor token', async () => { + const errorResponse = { error: 'LOGIN_TWOFACTOR', message: 'You must provide a two-factor code', suggestion: 'You can do it via the API using `token`, or via the CLI using `rdme login --2fa`', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); + }; + + const mock = nock(config.get('host')).post('/api/v1/login', { email, password, project }).reply(401, errorResponse); - return cmd.run({ email, password, project }).catch(err => { - mock.done(); - expect(err.code).toBe('LOGIN_TWOFACTOR'); - expect(err.message).toContain('You must provide a two-factor code'); - }); + await expect(cmd.run({ email, password, project })).rejects.toStrictEqual(new APIError(errorResponse)); + mock.done(); }); - it('should send 2fa token if provided', () => { - const email = 'dom@readme.io'; - const password = '123456'; - const project = 'subdomain'; + it('should send 2fa token if provided', async () => { const token = '123456'; - const mock = nock(config.host) + const mock = nock(config.get('host')) .post('/api/v1/login', { email, password, project, token }) .reply(200, { apiKey: '123' }); - return cmd.run({ email, password, project, token }).then(() => { - mock.done(); - }); + await expect(cmd.run({ email, password, project, token })).resolves.toMatchSnapshot(); + mock.done(); }); it.todo('should error if trying to access a project that is not yours'); diff --git a/__tests__/cmds/logout.test.js b/__tests__/cmds/logout.test.js index 91f7ced6d..647984c27 100644 --- a/__tests__/cmds/logout.test.js +++ b/__tests__/cmds/logout.test.js @@ -8,20 +8,20 @@ describe('rdme logout', () => { configStore.delete('email'); configStore.delete('project'); - return cmd.run({}).then(msg => { - expect(msg).toBe( - `You have logged out of ReadMe. Please use \`${config.cli} ${loginCmd.command}\` to login again.` - ); - }); + return expect(cmd.run({})).resolves.toBe( + `You have logged out of ReadMe. Please use \`${config.get('cli')} ${loginCmd.command}\` to login again.` + ); }); - it('should log the user out', () => { + it('should log the user out', async () => { configStore.set('email', 'email@example.com'); configStore.set('project', 'subdomain'); - return cmd.run({}).then(() => { - expect(configStore.get('email')).toBeUndefined(); - expect(configStore.get('project')).toBeUndefined(); - }); + await expect(cmd.run({})).resolves.toBe( + `You have logged out of ReadMe. Please use \`${config.get('cli')} ${loginCmd.command}\` to login again.` + ); + + expect(configStore.get('email')).toBeUndefined(); + expect(configStore.get('project')).toBeUndefined(); }); }); diff --git a/__tests__/cmds/open.test.js b/__tests__/cmds/open.test.js index df0d94c9a..117a19887 100644 --- a/__tests__/cmds/open.test.js +++ b/__tests__/cmds/open.test.js @@ -8,7 +8,7 @@ describe('rdme open', () => { it('should error if no project provided', () => { configStore.delete('project'); - return expect(cmd.run({})).rejects.toThrow(`Please login using \`${config.cli} ${loginCmd.command}\`.`); + return expect(cmd.run({})).rejects.toThrow(`Please login using \`${config.get('cli')} ${loginCmd.command}\`.`); }); it('should open the project', () => { diff --git a/__tests__/cmds/openapi.test.js b/__tests__/cmds/openapi.test.js index 4c3b0421a..67bef3ee5 100644 --- a/__tests__/cmds/openapi.test.js +++ b/__tests__/cmds/openapi.test.js @@ -10,7 +10,7 @@ const APIError = require('../../src/lib/apiError'); const key = 'API_KEY'; const id = '5aa0409b7cf527a93bfb44df'; const version = '1.0.0'; -const exampleRefLocation = `${config.host}/project/example-project/1.0.1/refs/ex`; +const exampleRefLocation = `${config.get('host')}/project/example-project/1.0.1/refs/ex`; const successfulMessageBase = [ '', `\t${chalk.green(exampleRefLocation)}`, @@ -59,7 +59,7 @@ describe('rdme openapi', () => { ['OpenAPI 3.1', 'json', '3.1'], ['OpenAPI 3.1', 'yaml', '3.1'], ])('should support uploading a %s definition (format: %s)', async (_, format, specVersion) => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .get('/api/v1/api-specification') .basicAuth({ user: key }) .reply(200, []) @@ -86,7 +86,7 @@ describe('rdme openapi', () => { it('should discover and upload an API definition if none is provided', async () => { promptHandler.createOasPrompt.mockResolvedValue({ option: 'create' }); - const mock = nock(config.host) + const mock = nock(config.get('host')) .get('/api/v1/version') .basicAuth({ user: key }) .reply(200, [{ version }]) @@ -127,7 +127,7 @@ describe('rdme openapi', () => { ['OpenAPI 3.1', 'json', '3.1'], ['OpenAPI 3.1', 'yaml', '3.1'], ])('should support updating a %s definition (format: %s)', async (_, format, specVersion) => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .put(`/api/v1/api-specification/${id}`, body => body.match('form-data; name="spec"')) .basicAuth({ user: key }) .reply(201, { _id: 1 }, { location: exampleRefLocation }); @@ -145,7 +145,7 @@ describe('rdme openapi', () => { }); it('should still support `token`', async () => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .put(`/api/v1/api-specification/${id}`, body => body.match('form-data; name="spec"')) .basicAuth({ user: key }) .reply(201, { _id: 1 }, { location: exampleRefLocation }); @@ -169,7 +169,7 @@ describe('rdme openapi', () => { }); it('should return warning if providing `id` and `version`', async () => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .put(`/api/v1/api-specification/${id}`, body => body.match('form-data; name="spec"')) .basicAuth({ user: key }) .reply(201, { _id: 1 }, { location: exampleRefLocation }); @@ -208,7 +208,7 @@ describe('rdme openapi', () => { ], }; - const mock = nock(config.host).get(`/api/v1/version/${invalidVersion}`).reply(404, errorObject); + const mock = nock(config.get('host')).get(`/api/v1/version/${invalidVersion}`).reply(404, errorObject); await expect( openapi.run({ @@ -227,7 +227,7 @@ describe('rdme openapi', () => { newVersion: '1.0.1', }); - const mock = nock(config.host) + const mock = nock(config.get('host')) .get('/api/v1/version') .basicAuth({ user: key }) .reply(200, [{ version: '1.0.0' }]) @@ -251,7 +251,7 @@ describe('rdme openapi', () => { it('should bundle and upload the expected content', async () => { let requestBody = null; - const mock = nock(config.host) + const mock = nock(config.get('host')) .get('/api/v1/api-specification') .basicAuth({ user: key }) .reply(200, []) @@ -301,7 +301,7 @@ describe('rdme openapi', () => { ], }; - const mock = nock(config.host).get('/api/v1/version').reply(401, errorObject); + const mock = nock(config.get('host')).get('/api/v1/version').reply(401, errorObject); await expect( openapi.run({ key, spec: require.resolve('@readme/oas-examples/3.1/json/petstore.json') }) @@ -311,7 +311,7 @@ describe('rdme openapi', () => { }); it('should error if no file was provided or able to be discovered', async () => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version: '1.0.0' }); @@ -341,7 +341,7 @@ describe('rdme openapi', () => { help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }; - const mock = nock(config.host) + const mock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version: '1.0.0' }) @@ -373,7 +373,7 @@ describe('rdme openapi', () => { help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }; - const mock = nock(config.host) + const mock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version: '1.0.0' }) @@ -393,7 +393,7 @@ describe('rdme openapi', () => { }); it('should error if API errors (generic upload error)', async () => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version: '1.0.0' }) @@ -413,7 +413,7 @@ describe('rdme openapi', () => { }); it('should error if API errors (request timeout)', async () => { - const mock = nock(config.host) + const mock = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version: '1.0.0' }) diff --git a/__tests__/cmds/versions.test.js b/__tests__/cmds/versions.test.js index 441fbe02f..9d5a61a6e 100644 --- a/__tests__/cmds/versions.test.js +++ b/__tests__/cmds/versions.test.js @@ -1,6 +1,7 @@ const nock = require('nock'); const config = require('config'); const promptHandler = require('../../src/lib/prompts'); +const APIError = require('../../src/lib/apiError'); const versions = require('../../src/cmds/versions'); const createVersion = require('../../src/cmds/versions/create'); @@ -40,63 +41,56 @@ describe('rdme versions*', () => { describe('rdme versions', () => { it('should error if no api key provided', () => { - return versions.run({}).catch(err => { - expect(err.message).toBe('No project API key provided. Please use `--key`.'); - }); + return expect(versions.run({})).rejects.toStrictEqual( + new Error('No project API key provided. Please use `--key`.') + ); }); it('should make a request to get a list of existing versions', async () => { - const mockRequest = nock(config.host) + const mockRequest = nock(config.get('host')) .get('/api/v1/version') .basicAuth({ user: key }) .reply(200, [versionPayload, version2Payload]); - const table = await versions.run({ key }); - expect(table).toContain(version); - expect(table).toContain(version2); + await expect(versions.run({ key })).resolves.toMatchSnapshot(); mockRequest.done(); }); it('should make a request to get a list of existing versions and return them in a raw format', async () => { - const mockRequest = nock(config.host) + const mockRequest = nock(config.get('host')) .get('/api/v1/version') .basicAuth({ user: key }) .reply(200, [versionPayload, version2Payload]); - const response = await versions.run({ key, raw: true }); - expect(response).toStrictEqual(JSON.stringify([versionPayload, version2Payload], null, 2)); + const raw = await versions.run({ key, raw: true }); + expect(raw).toStrictEqual(JSON.stringify([versionPayload, version2Payload], null, 2)); mockRequest.done(); }); - it('should get a specific version object if version flag provided', () => { - const mockRequest = nock(config.host) + it('should get a specific version object if version flag provided', async () => { + const mockRequest = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, versionPayload); - return versions.run({ key, version }).then(table => { - expect(table).toContain(version); - expect(table).not.toContain(version2); - mockRequest.done(); - }); + await expect(versions.run({ key, version })).resolves.toMatchSnapshot(); + mockRequest.done(); }); - it('should get a specific version object if version flag provided and return it in a raw format', () => { - const mockRequest = nock(config.host) + it('should get a specific version object if version flag provided and return it in a raw format', async () => { + const mockRequest = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, versionPayload); - return versions.run({ key, version, raw: true }).then(response => { - expect(response).toStrictEqual(JSON.stringify(versionPayload, null, 2)); - mockRequest.done(); - }); + const raw = await versions.run({ key, version, raw: true }); + expect(raw).toStrictEqual(JSON.stringify(versionPayload, null, 2)); + mockRequest.done(); }); }); describe('rdme versions:create', () => { it('should error if no api key provided', () => { - expect.assertions(1); return createVersion.run({}).catch(err => { expect(err.message).toBe('No project API key provided. Please use `--key`.'); }); @@ -109,7 +103,7 @@ describe('rdme versions*', () => { from: '1.0.0', }); - const mockRequest = nock(config.host) + const mockRequest = nock(config.get('host')) .get('/api/v1/version') .basicAuth({ user: key }) .reply(200, [{ version }, { version }]) @@ -122,87 +116,89 @@ describe('rdme versions*', () => { }); }); - it('should catch any post request errors', () => { + it('should catch any post request errors', async () => { expect.assertions(1); promptHandler.createVersionPrompt.mockResolvedValue({ is_stable: false, is_beta: false, }); - const mockRequest = nock(config.host).post(`/api/v1/version`).basicAuth({ user: key }).reply(400, { + const errorResponse = { error: 'VERSION_EMPTY', message: 'You need to include an x-readme-version header', suggestion: '...a suggestion to resolve the issue...', help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }); + }; - return createVersion.run({ key, version, fork: '0.0.5' }).catch(err => { - expect(err.message).toContain('You need to include an x-readme-version header'); - mockRequest.done(); - }); + const mockRequest = nock(config.get('host')) + .post('/api/v1/version') + .basicAuth({ user: key }) + .reply(400, errorResponse); + + await expect(createVersion.run({ key, version, fork: '0.0.5' })).rejects.toStrictEqual( + new APIError(errorResponse) + ); + mockRequest.done(); }); }); describe('rdme versions:delete', () => { it('should error if no api key provided', () => { - expect.assertions(1); - return deleteVersion.run({}).catch(err => { - expect(err.message).toBe('No project API key provided. Please use `--key`.'); - }); + return expect(deleteVersion.run({})).rejects.toStrictEqual( + new Error('No project API key provided. Please use `--key`.') + ); }); - it('should delete a specific version', () => { - const mockRequest = nock(config.host) + it('should delete a specific version', async () => { + const mockRequest = nock(config.get('host')) .delete(`/api/v1/version/${version}`) .basicAuth({ user: key }) - .reply(200) + .reply(200, { removed: true }) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); - return deleteVersion.run({ key, version }).then(() => { - mockRequest.done(); - }); + await expect(deleteVersion.run({ key, version })).resolves.toBe('Version 1.0.0 deleted successfully.'); + mockRequest.done(); }); - it('should catch any request errors', () => { - const mockRequest = nock(config.host) + it('should catch any request errors', async () => { + const errorResponse = { + error: 'VERSION_NOTFOUND', + message: + "The version you specified ({version}) doesn't match any of the existing versions ({versions_list}) in ReadMe.", + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }; + + const mockRequest = nock(config.get('host')) .delete(`/api/v1/version/${version}`) .basicAuth({ user: key }) - .reply(404, { - error: 'VERSION_NOTFOUND', - message: - "The version you specified ({version}) doesn't match any of the existing versions ({versions_list}) in ReadMe.", - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }) + .reply(404, errorResponse) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); - return deleteVersion.run({ key, version }).catch(err => { - expect(err.message).toContain('The version you specified'); - mockRequest.done(); - }); + await expect(deleteVersion.run({ key, version })).rejects.toStrictEqual(new APIError(errorResponse)); + mockRequest.done(); }); }); describe('rdme versions:update', () => { it('should error if no api key provided', () => { - expect.assertions(1); - return updateVersion.run({}).catch(err => { - expect(err.message).toBe('No project API key provided. Please use `--key`.'); - }); + return expect(updateVersion.run({})).rejects.toStrictEqual( + new Error('No project API key provided. Please use `--key`.') + ); }); - it('should update a specific version object', () => { + it('should update a specific version object', async () => { promptHandler.createVersionPrompt.mockResolvedValue({ is_stable: false, is_beta: false, is_deprecated: true, }); - const mockRequest = nock(config.host) + const mockRequest = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }) @@ -213,38 +209,36 @@ describe('rdme versions*', () => { .basicAuth({ user: key }) .reply(200, { version }); - return updateVersion.run({ key, version }).then(() => { - mockRequest.done(); - }); + await expect(updateVersion.run({ key, version })).resolves.toBe('Version 1.0.0 updated successfully.'); + mockRequest.done(); }); - it('should catch any put request errors', () => { - expect.assertions(1); + it('should catch any put request errors', async () => { promptHandler.createVersionPrompt.mockResolvedValue({ is_stable: false, is_beta: false, }); - const mockRequest = nock(config.host) + const errorResponse = { + error: 'VERSION_CANT_DEMOTE_STABLE', + message: "You can't make a stable version non-stable", + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }; + + const mockRequest = nock(config.get('host')) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }) .put(`/api/v1/version/${version}`) .basicAuth({ user: key }) - .reply(400, { - error: 'VERSION_CANT_DEMOTE_STABLE', - message: "You can't make a stable version non-stable", - suggestion: '...a suggestion to resolve the issue...', - help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', - }) + .reply(400, errorResponse) .get(`/api/v1/version/${version}`) .basicAuth({ user: key }) .reply(200, { version }); - return updateVersion.run({ key, version }).catch(err => { - expect(err.message).toContain("You can't make a stable version non-stable"); - mockRequest.done(); - }); + await expect(updateVersion.run({ key, version })).rejects.toStrictEqual(new APIError(errorResponse)); + mockRequest.done(); }); }); }); diff --git a/__tests__/cmds/whoami.test.js b/__tests__/cmds/whoami.test.js index 336a64a67..b6190a8da 100644 --- a/__tests__/cmds/whoami.test.js +++ b/__tests__/cmds/whoami.test.js @@ -8,27 +8,15 @@ describe('rdme whoami', () => { configStore.delete('email'); configStore.delete('project'); - return cmd - .run({}) - .then(() => { - throw new Error('unauthenticated error message not displayed'); - }) - .catch(err => { - expect(err.message).toBe(`Please login using \`${config.cli} ${loginCmd.command}\`.`); - }); + return expect(cmd.run({})).rejects.toStrictEqual( + new Error(`Please login using \`${config.get('cli')} ${loginCmd.command}\`.`) + ); }); it('should return the authenticated user', () => { configStore.set('email', 'email@example.com'); configStore.set('project', 'subdomain'); - return cmd - .run({}) - .then(() => { - expect(true).toBe(true); - }) - .catch(err => { - throw new Error(err); - }); + return expect(cmd.run({})).resolves.toMatchSnapshot(); }); }); diff --git a/__tests__/set-node-env.js b/__tests__/set-node-env.js index 4bee43cae..aa0d807e1 100644 --- a/__tests__/set-node-env.js +++ b/__tests__/set-node-env.js @@ -1 +1,4 @@ +// Chalk has trouble with Jest sometimes in test snapshots so we're disabling colorization here for all tests. +process.env.FORCE_COLOR = 0; + process.env.NODE_ENV = 'testing'; diff --git a/src/cmds/docs/edit.js b/src/cmds/docs/edit.js index d10ce55bb..959835437 100644 --- a/src/cmds/docs/edit.js +++ b/src/cmds/docs/edit.js @@ -45,7 +45,7 @@ exports.run = async function (opts) { } if (!slug) { - return Promise.reject(new Error(`No slug provided. Usage \`${config.cli} ${exports.usage}\`.`)); + return Promise.reject(new Error(`No slug provided. Usage \`${config.get('cli')} ${exports.usage}\`.`)); } const selectedVersion = await getProjectVersion(version, key, true).catch(e => { @@ -54,7 +54,7 @@ exports.run = async function (opts) { const filename = `${slug}.md`; - const existingDoc = await fetch(`${config.host}/api/v1/docs/${slug}`, { + const existingDoc = await fetch(`${config.get('host')}/api/v1/docs/${slug}`, { method: 'get', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, @@ -69,7 +69,7 @@ exports.run = async function (opts) { if (code !== 0) return reject(new Error('Non zero exit code from $EDITOR')); const updatedDoc = await readFile(filename, 'utf8'); - return fetch(`${config.host}/api/v1/docs/${slug}`, { + return fetch(`${config.get('host')}/api/v1/docs/${slug}`, { method: 'put', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, diff --git a/src/cmds/docs/index.js b/src/cmds/docs/index.js index 765b8a9f2..9a20bd5b4 100644 --- a/src/cmds/docs/index.js +++ b/src/cmds/docs/index.js @@ -45,7 +45,7 @@ exports.run = async function (opts) { } if (!folder) { - return Promise.reject(new Error(`No folder provided. Usage \`${config.cli} ${exports.usage}\`.`)); + return Promise.reject(new Error(`No folder provided. Usage \`${config.get('cli')} ${exports.usage}\`.`)); } // TODO: should we allow version selection at all here? @@ -75,7 +75,7 @@ exports.run = async function (opts) { function createDoc(slug, file, hash, err) { if (err.error !== 'DOC_NOTFOUND') return Promise.reject(err); - return fetch(`${config.host}/api/v1/docs`, { + return fetch(`${config.get('host')}/api/v1/docs`, { method: 'post', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, @@ -95,7 +95,7 @@ exports.run = async function (opts) { return `\`${slug}\` was not updated because there were no changes.`; } - return fetch(`${config.host}/api/v1/docs/${slug}`, { + return fetch(`${config.get('host')}/api/v1/docs/${slug}`, { method: 'put', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, @@ -120,7 +120,7 @@ exports.run = async function (opts) { const slug = matter.data.slug || path.basename(filename).replace(path.extname(filename), '').toLowerCase(); const hash = crypto.createHash('sha1').update(file).digest('hex'); - return fetch(`${config.host}/api/v1/docs/${slug}`, { + return fetch(`${config.get('host')}/api/v1/docs/${slug}`, { method: 'get', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, diff --git a/src/cmds/login.js b/src/cmds/login.js index 2d583fa10..9a69fd8bb 100644 --- a/src/cmds/login.js +++ b/src/cmds/login.js @@ -4,7 +4,7 @@ const { validate: isEmail } = require('isemail'); const { promisify } = require('util'); const read = promisify(require('read')); const configStore = require('../lib/configstore'); -const APIError = require('../lib/apiError'); +const { handleRes } = require('../lib/handleRes'); const fetch = require('node-fetch'); const testing = process.env.NODE_ENV === 'testing'; @@ -55,7 +55,7 @@ exports.run = async function (opts) { return Promise.reject(new Error('You must provide a valid email address.')); } - return fetch(`${config.host}/api/v1/login`, { + return fetch(`${config.get('host')}/api/v1/login`, { method: 'post', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ @@ -65,12 +65,8 @@ exports.run = async function (opts) { token, }), }) - .then(res => res.json()) + .then(handleRes) .then(res => { - if (res.error) { - return Promise.reject(new APIError(res)); - } - configStore.set('apiKey', res.apiKey); configStore.set('email', email); configStore.set('project', project); diff --git a/src/cmds/logout.js b/src/cmds/logout.js index 49d472be0..c64335cea 100644 --- a/src/cmds/logout.js +++ b/src/cmds/logout.js @@ -16,6 +16,6 @@ exports.run = async () => { } return Promise.resolve( - `You have logged out of ReadMe. Please use \`${config.cli} ${loginCmd.command}\` to login again.` + `You have logged out of ReadMe. Please use \`${config.get('cli')} ${loginCmd.command}\` to login again.` ); }; diff --git a/src/cmds/open.js b/src/cmds/open.js index 7b3e378ce..13a6aecde 100644 --- a/src/cmds/open.js +++ b/src/cmds/open.js @@ -15,11 +15,13 @@ exports.args = []; exports.run = function (opts) { const project = configStore.get('project'); if (!project) { - return Promise.reject(new Error(`Please login using \`${config.cli} ${loginCmd.command}\`.`)); + return Promise.reject(new Error(`Please login using \`${config.get('cli')} ${loginCmd.command}\`.`)); } - return (opts.mockOpen || open)(config.hub.replace('{project}', project), { + const url = config.get('hub').replace('{project}', project); + + return (opts.mockOpen || open)(url, { wait: false, url: true, - }).then(() => Promise.resolve(`Opening ${chalk.green(config.hub.replace('{project}', project))} in your browser...`)); + }).then(() => Promise.resolve(`Opening ${chalk.green(url)} in your browser...`)); }; diff --git a/src/cmds/openapi.js b/src/cmds/openapi.js index 459c60682..3fc9aa9c4 100644 --- a/src/cmds/openapi.js +++ b/src/cmds/openapi.js @@ -143,7 +143,7 @@ exports.run = async function (opts) { function createSpec() { options.method = 'post'; - return fetch(`${config.host}/api/v1/api-specification`, options).then(res => { + return fetch(`${config.get('host')}/api/v1/api-specification`, options).then(res => { if (res.ok) return success(res); return error(res); }); @@ -152,7 +152,7 @@ exports.run = async function (opts) { function updateSpec(specId) { isUpdate = true; options.method = 'put'; - return fetch(`${config.host}/api/v1/api-specification/${specId}`, options).then(res => { + return fetch(`${config.get('host')}/api/v1/api-specification/${specId}`, options).then(res => { if (res.ok) return success(res); return error(res); }); @@ -167,7 +167,7 @@ exports.run = async function (opts) { */ function getSpecs(url) { - return fetch(`${config.host}${url}`, { + return fetch(`${config.get('host')}${url}`, { method: 'get', headers: cleanHeaders(key, { 'x-readme-version': versionCleaned, diff --git a/src/cmds/versions/create.js b/src/cmds/versions/create.js index d5518b097..1c83c1290 100644 --- a/src/cmds/versions/create.js +++ b/src/cmds/versions/create.js @@ -62,12 +62,12 @@ exports.run = async function (opts) { if (!version || !semver.valid(semver.coerce(version))) { return Promise.reject( - new Error(`Please specify a semantic version. See \`${config.cli} help ${exports.command}\` for help.`) + new Error(`Please specify a semantic version. See \`${config.get('cli')} help ${exports.command}\` for help.`) ); } if (!fork) { - versionList = await fetch(`${config.host}/api/v1/version`, { + versionList = await fetch(`${config.get('host')}/api/v1/version`, { method: 'get', headers: cleanHeaders(key), }).then(res => handleRes(res)); @@ -80,7 +80,7 @@ exports.run = async function (opts) { const promptResponse = await prompt(versionPrompt); - return fetch(`${config.host}/api/v1/version`, { + return fetch(`${config.get('host')}/api/v1/version`, { method: 'post', headers: cleanHeaders(key, { Accept: 'application/json', diff --git a/src/cmds/versions/delete.js b/src/cmds/versions/delete.js index 466a362ca..231b8d242 100644 --- a/src/cmds/versions/delete.js +++ b/src/cmds/versions/delete.js @@ -1,7 +1,7 @@ const config = require('config'); -const APIError = require('../../lib/apiError'); const { getProjectVersion } = require('../../lib/versionSelect'); const { cleanHeaders } = require('../../lib/cleanHeaders'); +const { handleRes } = require('../../lib/handleRes'); const fetch = require('node-fetch'); exports.command = 'versions:delete'; @@ -35,13 +35,12 @@ exports.run = async function (opts) { return Promise.reject(e); }); - return fetch(`${config.host}/api/v1/version/${selectedVersion}`, { + return fetch(`${config.get('host')}/api/v1/version/${selectedVersion}`, { method: 'delete', headers: cleanHeaders(key), - }).then(res => { - if (res.error) { - return Promise.reject(new APIError(res)); - } - return Promise.resolve(`Version ${selectedVersion} deleted successfully.`); - }); + }) + .then(handleRes) + .then(() => { + return Promise.resolve(`Version ${selectedVersion} deleted successfully.`); + }); }; diff --git a/src/cmds/versions/index.js b/src/cmds/versions/index.js index f43fc0a9c..a4dcaaa59 100644 --- a/src/cmds/versions/index.js +++ b/src/cmds/versions/index.js @@ -2,9 +2,9 @@ const chalk = require('chalk'); const Table = require('cli-table'); const config = require('config'); const versionsCreate = require('./create'); -const APIError = require('../../lib/apiError'); const { cleanHeaders } = require('../../lib/cleanHeaders'); const fetch = require('node-fetch'); +const { handleRes } = require('../../lib/handleRes'); exports.command = 'versions'; exports.usage = 'versions [options]'; @@ -89,18 +89,14 @@ exports.run = function (opts) { return Promise.reject(new Error('No project API key provided. Please use `--key`.')); } - const uri = version ? `${config.host}/api/v1/version/${version}` : `${config.host}/api/v1/version`; + const uri = version ? `${config.get('host')}/api/v1/version/${version}` : `${config.get('host')}/api/v1/version`; return fetch(uri, { method: 'get', headers: cleanHeaders(key), }) - .then(res => res.json()) + .then(handleRes) .then(data => { - if (data.error) { - return Promise.reject(new APIError(data)); - } - if (raw) { return Promise.resolve(JSON.stringify(data, null, 2)); } @@ -113,7 +109,9 @@ exports.run = function (opts) { if (!versions.length) { return Promise.reject( new Error( - `Sorry, you haven't created any versions yet! See \`${config.cli} help ${versionsCreate.command}\` for commands on how to do that.` + `Sorry, you haven't created any versions yet! See \`${config.get('cli')} help ${ + versionsCreate.command + }\` for commands on how to do that.` ) ); } diff --git a/src/cmds/versions/update.js b/src/cmds/versions/update.js index 424576a5a..e151bfa01 100644 --- a/src/cmds/versions/update.js +++ b/src/cmds/versions/update.js @@ -1,7 +1,6 @@ const config = require('config'); const { prompt } = require('enquirer'); const promptOpts = require('../../lib/prompts'); -const APIError = require('../../lib/apiError'); const { cleanHeaders } = require('../../lib/cleanHeaders'); const { getProjectVersion } = require('../../lib/versionSelect'); const fetch = require('node-fetch'); @@ -57,14 +56,14 @@ exports.run = async function (opts) { return Promise.reject(e); }); - const foundVersion = await fetch(`${config.host}/api/v1/version/${selectedVersion}`, { + const foundVersion = await fetch(`${config.get('host')}/api/v1/version/${selectedVersion}`, { method: 'get', headers: cleanHeaders(key), }).then(res => handleRes(res)); const promptResponse = await prompt(promptOpts.createVersionPrompt([{}], opts, foundVersion)); - return fetch(`${config.host}/api/v1/version/${selectedVersion}`, { + return fetch(`${config.get('host')}/api/v1/version/${selectedVersion}`, { method: 'put', headers: cleanHeaders(key, { Accept: 'application/json', @@ -79,11 +78,8 @@ exports.run = async function (opts) { is_hidden: promptResponse.is_stable ? false : !(isPublic === 'true' || promptResponse.is_hidden), }), }) - .then(res => res.json()) - .then(res => { - if (res.error) { - return Promise.reject(new APIError(res)); - } + .then(handleRes) + .then(() => { return Promise.resolve(`Version ${selectedVersion} updated successfully.`); }); }; diff --git a/src/cmds/whoami.js b/src/cmds/whoami.js index 134d722e3..3659c32ee 100644 --- a/src/cmds/whoami.js +++ b/src/cmds/whoami.js @@ -13,7 +13,7 @@ exports.args = []; exports.run = () => { if (!configStore.has('email') || !configStore.has('project')) { - return Promise.reject(new Error(`Please login using \`${config.cli} ${loginCmd.command}\`.`)); + return Promise.reject(new Error(`Please login using \`${config.get('cli')} ${loginCmd.command}\`.`)); } return Promise.resolve( diff --git a/src/index.js b/src/index.js index 1dd650037..d75331ec7 100644 --- a/src/index.js +++ b/src/index.js @@ -33,7 +33,7 @@ module.exports = processArgv => { name: 'version', alias: 'v', type: Boolean, - description: `Show the current ${config.cli} version`, + description: `Show the current ${config.get('cli')} version`, }, { name: 'command', type: String, defaultOption: true }, ]; @@ -113,7 +113,9 @@ module.exports = processArgv => { return bin.run(cmdArgv); } catch (e) { if (e.message === 'Command not found.') { - e.message = `${e.message}\n\nType \`${chalk.yellow(`${config.cli} help`)}\` ${chalk.red('to see all commands')}`; + e.message = `${e.message}\n\nType \`${chalk.yellow(`${config.get('cli')} help`)}\` ${chalk.red( + 'to see all commands' + )}`; } return Promise.reject(e); diff --git a/src/lib/help.js b/src/lib/help.js index c9b10f5bd..f691e4583 100644 --- a/src/lib/help.js +++ b/src/lib/help.js @@ -8,7 +8,7 @@ function formatCommands(cmds) { .sort((a, b) => (a.position > b.position ? 1 : -1)) .map(command => { return { - name: `${chalk.grey('$')} ${config.cli} ${command.name}`, + name: `${chalk.grey('$')} ${config.get('cli')} ${command.name}`, summary: command.description, }; }); @@ -16,7 +16,7 @@ function formatCommands(cmds) { const owlbert = () => { // http://asciiart.club - return ` πŸ“– ${chalk.blue.bold(config.cli)} + return ` πŸ“– ${chalk.blue.bold(config.get('cli'))} ${chalk.bold(`a utlity for interacting with ReadMe`)} . @@ -51,7 +51,7 @@ exports.commandUsage = cmd => { }, { header: 'Usage', - content: `${config.cli} ${cmd.usage}`, + content: `${config.get('cli')} ${cmd.usage}`, }, { header: 'Options', @@ -86,7 +86,7 @@ exports.globalUsage = async args => { }, { header: 'Usage', - content: `${config.cli} [arguments]`, + content: `${config.get('cli')} [arguments]`, }, { header: 'Options', @@ -108,7 +108,7 @@ exports.globalUsage = async args => { helpContent.push( { - content: `Run ${chalk.dim(`${config.cli} help `)} for help with a specific command.`, + content: `Run ${chalk.dim(`${config.get('cli')} help `)} for help with a specific command.`, raw: true, }, { diff --git a/src/lib/versionSelect.js b/src/lib/versionSelect.js index 2c7bef091..b1c5c2d31 100644 --- a/src/lib/versionSelect.js +++ b/src/lib/versionSelect.js @@ -9,7 +9,7 @@ const { handleRes } = require('./handleRes'); async function getProjectVersion(versionFlag, key, allowNewVersion) { try { if (versionFlag) { - return await fetch(`${config.host}/api/v1/version/${versionFlag}`, { + return await fetch(`${config.get('host')}/api/v1/version/${versionFlag}`, { method: 'get', headers: cleanHeaders(key), }) @@ -17,7 +17,7 @@ async function getProjectVersion(versionFlag, key, allowNewVersion) { .then(res => res.version); } - const versionList = await fetch(`${config.host}/api/v1/version`, { + const versionList = await fetch(`${config.get('host')}/api/v1/version`, { method: 'get', headers: cleanHeaders(key), }).then(res => handleRes(res)); @@ -27,7 +27,7 @@ async function getProjectVersion(versionFlag, key, allowNewVersion) { if (option === 'update') return versionSelection; - await fetch(`${config.host}/api/v1/version`, { + await fetch(`${config.get('host')}/api/v1/version`, { method: 'post', headers: cleanHeaders(key, { 'Content-Type': 'application/json' }), body: JSON.stringify({