From e11e18cb33eb60a666980d061c54bb8891cdd242 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Thu, 26 Mar 2020 12:00:02 -0700 Subject: [PATCH] build!: update to latest gts and TypeScript (#927) BREAKING CHANGE: typescript@3.7.x introduced some breaking changes in generated code. --- .eslintrc.json | 3 + .eslintrc.yml | 15 ---- .github/workflows/ci.yaml | 2 +- .prettierignore | 8 ++- .prettierrc | 8 --- .prettierrc.js | 17 +++++ browser-test/test.crypto.ts | 1 + browser-test/test.oauth2.ts | 8 ++- karma.conf.js | 2 +- package.json | 13 +--- src/auth/googleauth.ts | 4 ++ src/auth/oauth2client.ts | 2 +- src/crypto/browser/crypto.ts | 7 ++ src/crypto/crypto.ts | 1 + src/crypto/node/crypto.ts | 5 +- src/transporters.ts | 3 +- system-test/test.kitchen.ts | 5 +- test/test.compute.ts | 13 ++-- test/test.googleauth.ts | 76 ++++++++------------ test/test.index.ts | 2 + test/test.jwt.ts | 17 ++--- test/test.jwtaccess.ts | 1 + test/test.oauth2.ts | 133 ++++++++++++----------------------- test/test.transporters.ts | 19 ++--- tsconfig.json | 2 +- 25 files changed, 152 insertions(+), 215 deletions(-) create mode 100644 .eslintrc.json delete mode 100644 .eslintrc.yml delete mode 100644 .prettierrc create mode 100644 .prettierrc.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..78215349 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/gts" +} diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 73eeec27..00000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -extends: - - 'eslint:recommended' - - 'plugin:node/recommended' - - prettier -plugins: - - node - - prettier -rules: - prettier/prettier: error - block-scoped-var: error - eqeqeq: error - no-warning-comments: warn - no-var: error - prefer-const: error diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 92394b1e..7138a79a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [8, 10, 12, 13] + node: [10, 12, 13] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/.prettierignore b/.prettierignore index f6fac98b..a4ac7b37 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ -node_modules/* -samples/node_modules/* -src/**/doc/* +**/node_modules +**/.coverage +build/ +docs/ +protos/ diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index df6eac07..00000000 --- a/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ ---- -bracketSpacing: false -printWidth: 80 -semi: true -singleQuote: true -tabWidth: 2 -trailingComma: es5 -useTabs: false diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..08cba377 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,17 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module.exports = { + ...require('gts/.prettierrc.json') +} diff --git a/browser-test/test.crypto.ts b/browser-test/test.crypto.ts index c152c5eb..aaa13b3f 100644 --- a/browser-test/test.crypto.ts +++ b/browser-test/test.crypto.ts @@ -17,6 +17,7 @@ import {assert} from 'chai'; import {createCrypto} from '../src/crypto/crypto'; import {BrowserCrypto} from '../src/crypto/browser/crypto'; import {privateKey, publicKey} from './fixtures/keys'; +import {describe, it} from 'mocha'; // Not all browsers support `TextEncoder`. The following `require` will // provide a fast UTF8-only replacement for those browsers that don't support diff --git a/browser-test/test.oauth2.ts b/browser-test/test.oauth2.ts index a87429a1..d94bbabd 100644 --- a/browser-test/test.oauth2.ts +++ b/browser-test/test.oauth2.ts @@ -16,6 +16,7 @@ import * as base64js from 'base64-js'; import {assert} from 'chai'; import * as sinon from 'sinon'; import {privateKey, publicKey} from './fixtures/keys'; +import {it, describe, beforeEach} from 'mocha'; // Not all browsers support `TextEncoder`. The following `require` will // provide a fast UTF8-only replacement for those browsers that don't support @@ -127,7 +128,7 @@ describe('Browser OAuth2 tests', () => { it('should generate a valid code verifier and resulting challenge', async () => { const codes = await client.generateCodeVerifierAsync(); - assert.match(codes.codeVerifier, /^[a-zA-Z0-9\-\.~_]{128}$/); + assert.match(codes.codeVerifier, /^[a-zA-Z0-9-.~_]{128}$/); }); it('should include code challenge and method in the url', async () => { @@ -166,13 +167,16 @@ describe('Browser OAuth2 tests', () => { '}'; const envelope = JSON.stringify({kid: 'keyid', alg: 'RS256'}); let data = + // eslint-disable-next-line node/no-unsupported-features/node-builtins base64js.fromByteArray(new TextEncoder().encode(envelope)) + '.' + + // eslint-disable-next-line node/no-unsupported-features/node-builtins base64js.fromByteArray(new TextEncoder().encode(idToken)); const algo = { name: 'RSASSA-PKCS1-v1_5', hash: {name: 'SHA-256'}, }; + // eslint-disable-next-line no-undef const cryptoKey = await window.crypto.subtle.importKey( 'jwk', privateKey, @@ -180,9 +184,11 @@ describe('Browser OAuth2 tests', () => { true, ['sign'] ); + // eslint-disable-next-line no-undef const signature = await window.crypto.subtle.sign( algo, cryptoKey, + // eslint-disable-next-line node/no-unsupported-features/node-builtins new TextEncoder().encode(data) ); data += '.' + base64js.fromByteArray(new Uint8Array(signature)); diff --git a/karma.conf.js b/karma.conf.js index 5b812100..54433e4e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -19,7 +19,7 @@ const isDocker = require('is-docker')(); const webpackConfig = require('./webpack-tests.config.js'); process.env.CHROME_BIN = require('puppeteer').executablePath(); -module.exports = function(config) { +module.exports = function (config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', diff --git a/package.json b/package.json index 2cc0585b..cfe20ee5 100644 --- a/package.json +++ b/package.json @@ -43,14 +43,9 @@ "c8": "^7.0.0", "chai": "^4.2.0", "codecov": "^3.0.2", - "eslint": "^6.0.0", - "eslint-config-prettier": "^6.0.0", - "eslint-plugin-node": "^11.0.0", - "eslint-plugin-prettier": "^3.0.0", "execa": "^4.0.0", - "gts": "^1.1.2", + "gts": "^2.0.0-alpha.8", "is-docker": "^2.0.0", - "js-green-licenses": "^1.0.0", "karma": "^4.0.0", "karma-chrome-launcher": "^3.0.0", "karma-coverage": "^2.0.0", @@ -66,12 +61,11 @@ "ncp": "^2.0.0", "nock": "^12.0.0", "null-loader": "^3.0.0", - "prettier": "^1.13.4", "puppeteer": "^2.0.0", "sinon": "^9.0.0", "tmp": "^0.1.0", "ts-loader": "^6.0.0", - "typescript": "3.6.4", + "typescript": "^3.8.3", "webpack": "^4.20.2", "webpack-cli": "^3.1.1" }, @@ -83,11 +77,10 @@ "test": "c8 mocha build/test", "clean": "gts clean", "prepare": "npm run compile", - "lint": "gts check && eslint '**/*.js' && jsgl --local .", + "lint": "gts check", "compile": "tsc -p .", "fix": "gts fix && eslint --fix '**/*.js'", "pretest": "npm run compile", - "license-check": "jsgl --local .", "docs": "compodoc src/", "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", "system-test": "mocha build/system-test --timeout 60000", diff --git a/src/auth/googleauth.ts b/src/auth/googleauth.ts index 11fce60c..62d7b8e6 100644 --- a/src/auth/googleauth.ts +++ b/src/auth/googleauth.ts @@ -45,6 +45,7 @@ export interface CredentialCallback { (err: Error | null, result?: UserRefreshClient | JWT): void; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface interface DeprecatedGetClientOptions {} export interface ADCCallback { @@ -164,7 +165,10 @@ export class GoogleAuth { // - Cloud SDK: `gcloud config config-helper --format json` // - GCE project ID from metadata server) if (!this._getDefaultProjectIdPromise) { + // TODO: refactor the below code so that it doesn't mix and match + // promises and async/await. this._getDefaultProjectIdPromise = new Promise( + // eslint-disable-next-line no-async-promise-executor async (resolve, reject) => { try { const projectId = diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 163d15a7..588ac804 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -1216,7 +1216,7 @@ export class OAuth2Client extends AuthClient { throw new Error("Can't parse token payload: " + segments[1]); } - if (!certs.hasOwnProperty(envelope.kid)) { + if (!Object.prototype.hasOwnProperty.call(certs, envelope.kid)) { // If this is not present, then there's no reason to attempt verification throw new Error('No pem found for envelope: ' + JSON.stringify(envelope)); } diff --git a/src/crypto/browser/crypto.ts b/src/crypto/browser/crypto.ts index f84191b6..5e1665f0 100644 --- a/src/crypto/browser/crypto.ts +++ b/src/crypto/browser/crypto.ts @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +/* global window */ // This file implements crypto functions we need using in-browser // SubtleCrypto interface `window.crypto.subtle`. @@ -20,6 +21,7 @@ import * as base64js from 'base64-js'; // Not all browsers support `TextEncoder`. The following `require` will // provide a fast UTF8-only replacement for those browsers that don't support // text encoding natively. +// eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof process === 'undefined' && typeof TextEncoder === 'undefined') { require('fast-text-encoding'); } @@ -45,6 +47,7 @@ export class BrowserCrypto implements Crypto { // To calculate SHA256 digest using SubtleCrypto, we first // need to convert an input string to an ArrayBuffer: + // eslint-disable-next-line node/no-unsupported-features/node-builtins const inputBuffer = new TextEncoder().encode(str); // Result is ArrayBuffer as well. @@ -79,6 +82,7 @@ export class BrowserCrypto implements Crypto { name: 'RSASSA-PKCS1-v1_5', hash: {name: 'SHA-256'}, }; + // eslint-disable-next-line node/no-unsupported-features/node-builtins const dataArray = new TextEncoder().encode(data); const signatureArray = base64js.toByteArray( BrowserCrypto.padBase64(signature) @@ -107,6 +111,7 @@ export class BrowserCrypto implements Crypto { name: 'RSASSA-PKCS1-v1_5', hash: {name: 'SHA-256'}, }; + // eslint-disable-next-line node/no-unsupported-features/node-builtins const dataArray = new TextEncoder().encode(data); const cryptoKey = await window.crypto.subtle.importKey( 'jwk', @@ -124,11 +129,13 @@ export class BrowserCrypto implements Crypto { decodeBase64StringUtf8(base64: string): string { const uint8array = base64js.toByteArray(BrowserCrypto.padBase64(base64)); + // eslint-disable-next-line node/no-unsupported-features/node-builtins const result = new TextDecoder().decode(uint8array); return result; } encodeBase64StringUtf8(text: string): string { + // eslint-disable-next-line node/no-unsupported-features/node-builtins const uint8array = new TextEncoder().encode(text); const result = base64js.fromByteArray(uint8array); return result; diff --git a/src/crypto/crypto.ts b/src/crypto/crypto.ts index 45ca5c9b..27ce5a9d 100644 --- a/src/crypto/crypto.ts +++ b/src/crypto/crypto.ts @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +/* global window */ import {BrowserCrypto} from './browser/crypto'; import {NodeCrypto} from './node/crypto'; diff --git a/src/crypto/node/crypto.ts b/src/crypto/node/crypto.ts index 215ea7a4..58b60671 100644 --- a/src/crypto/node/crypto.ts +++ b/src/crypto/node/crypto.ts @@ -17,10 +17,7 @@ import {Crypto} from '../crypto'; export class NodeCrypto implements Crypto { async sha256DigestBase64(str: string): Promise { - return crypto - .createHash('sha256') - .update(str) - .digest('base64'); + return crypto.createHash('sha256').update(str).digest('base64'); } randomBytesBase64(count: number): string { diff --git a/src/transporters.ts b/src/transporters.ts index e9feb5be..3b3fded6 100644 --- a/src/transporters.ts +++ b/src/transporters.ts @@ -21,8 +21,9 @@ import { } from 'gaxios'; import {validate} from './options'; -// tslint:disable-next-line no-var-requires +// eslint-disable-next-line @typescript-eslint/no-var-requires const pkg = require('../../package.json'); + const PRODUCT_NAME = 'google-api-nodejs-client'; export interface Transporter { diff --git a/system-test/test.kitchen.ts b/system-test/test.kitchen.ts index 30933d24..630f9b82 100644 --- a/system-test/test.kitchen.ts +++ b/system-test/test.kitchen.ts @@ -13,7 +13,7 @@ // limitations under the License. import * as assert from 'assert'; -import {describe, it} from 'mocha'; +import {describe, it, before, after} from 'mocha'; import * as execa from 'execa'; import * as fs from 'fs'; import * as mv from 'mv'; @@ -27,6 +27,7 @@ const ncpp = promisify(ncp); const keep = !!process.env.GALN_KEEP_TEMPDIRS; const stagingDir = tmp.dirSync({keep, unsafeCleanup: true}); const stagingPath = stagingDir.name; +// eslint-disable-next-line @typescript-eslint/no-var-requires const pkg = require('../../package.json'); describe('pack and install', () => { @@ -34,7 +35,7 @@ describe('pack and install', () => { * Create a staging directory with temp fixtures used to test on a fresh * application. */ - before('should be able to use the d.ts', async function() { + before('should be able to use the d.ts', async function () { this.timeout(40000); console.log(`${__filename} staging area: ${stagingPath}`); await execa('npm', ['pack'], {stdio: 'inherit'}); diff --git a/test/test.compute.ts b/test/test.compute.ts index 365ee60e..05d476ec 100644 --- a/test/test.compute.ts +++ b/test/test.compute.ts @@ -14,7 +14,6 @@ import * as assert from 'assert'; import {describe, it, beforeEach, afterEach} from 'mocha'; -const assertRejects = require('assert-rejects'); import {BASE_PATH, HEADERS, HOST_ADDRESS} from 'gcp-metadata'; import * as nock from 'nock'; import * as sinon from 'sinon'; @@ -37,9 +36,7 @@ describe('compute', () => { } function mockExample() { - return nock(url) - .get('/') - .reply(200); + return nock(url).get('/').reply(200); } // set up compute client. @@ -158,7 +155,7 @@ describe('compute', () => { 'Compute Engine instance does not have the correct permission scopes specified. ' + 'Could not refresh access token.' ); - await assertRejects(compute.request({url}), expected); + await assert.rejects(compute.request({url}), expected); scope.done(); }); @@ -169,7 +166,7 @@ describe('compute', () => { 'token for the Compute Engine built-in service account. This may be because the ' + 'Compute Engine instance does not have any permission scopes specified.' ); - await assertRejects(compute.request({url}), expected); + await assert.rejects(compute.request({url}), expected); scope.done(); }); @@ -188,7 +185,7 @@ describe('compute', () => { 'Compute Engine instance does not have the correct permission scopes specified. ' + 'Could not refresh access token.' ); - await assertRejects(compute.request({}), expected); + await assert.rejects(compute.request({}), expected); scope.done(); }); @@ -210,7 +207,7 @@ describe('compute', () => { 'refresh access token.' ); - await assertRejects(compute.request({}), expected); + await assert.rejects(compute.request({}), expected); scope.done(); }); diff --git a/test/test.googleauth.ts b/test/test.googleauth.ts index 11bcd62f..a813cff3 100644 --- a/test/test.googleauth.ts +++ b/test/test.googleauth.ts @@ -14,7 +14,6 @@ import * as assert from 'assert'; import {describe, it, beforeEach, afterEach} from 'mocha'; -const assertRejects = require('assert-rejects'); import * as child_process from 'child_process'; import * as crypto from 'crypto'; import * as fs from 'fs'; @@ -54,8 +53,11 @@ describe('googleauth', () => { STUB_PROJECT, ].join('/'); + // eslint-disable-next-line @typescript-eslint/no-var-requires const privateJSON = require('../../test/fixtures/private.json'); + // eslint-disable-next-line @typescript-eslint/no-var-requires const private2JSON = require('../../test/fixtures/private2.json'); + // eslint-disable-next-line @typescript-eslint/no-var-requires const refreshJSON = require('../../test/fixtures/refresh.json'); const privateKey = fs.readFileSync('./test/fixtures/private.pem', 'utf-8'); const wellKnownPathWindows = path.join( @@ -165,9 +167,7 @@ describe('googleauth', () => { } function nockIsGCE() { - const primary = nock(host) - .get(instancePath) - .reply(200, {}, HEADERS); + const primary = nock(host).get(instancePath).reply(200, {}, HEADERS); const secondary = nock(SECONDARY_HOST_ADDRESS) .get(instancePath) .reply(200, {}, HEADERS); @@ -204,9 +204,7 @@ describe('googleauth', () => { } function nock500GCE() { - const primary = nock(host) - .get(instancePath) - .reply(500, {}, HEADERS); + const primary = nock(host).get(instancePath).reply(500, {}, HEADERS); const secondary = nock(SECONDARY_HOST_ADDRESS) .get(instancePath) .reply(500, {}, HEADERS); @@ -224,9 +222,7 @@ describe('googleauth', () => { } function nock404GCE() { - const primary = nock(host) - .get(instancePath) - .reply(404); + const primary = nock(host).get(instancePath).reply(404); const secondary = nock(SECONDARY_HOST_ADDRESS) .get(instancePath) .reply(404); @@ -315,7 +311,7 @@ describe('googleauth', () => { it('should make a request with the api key', async () => { const scope = nock(BASE_URL) .post(ENDPOINT) - .reply(function(uri) { + .reply(function (uri) { assert.strictEqual(this.req.headers['x-goog-api-key'][0], API_KEY); return [200, RESPONSE_BODY]; }); @@ -340,7 +336,7 @@ describe('googleauth', () => { const scope = nock(BASE_URL) .post(ENDPOINT) .query({test: OTHER_QS_PARAM.test}) - .reply(function(uri) { + .reply(function (uri) { assert.strictEqual(this.req.headers['x-goog-api-key'][0], API_KEY); assert(uri.indexOf('test=' + OTHER_QS_PARAM.test) > -1); return [200, RESPONSE_BODY]; @@ -501,7 +497,7 @@ describe('googleauth', () => { }); it('getApplicationCredentialsFromFilePath should error on invalid symlink', async () => { - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath('./test/fixtures/badlink') ); }); @@ -511,7 +507,7 @@ describe('googleauth', () => { // git does not create symlinks on Windows return; } - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath('./test/fixtures/emptylink') ); }); @@ -561,13 +557,13 @@ describe('googleauth', () => { it('getApplicationCredentialsFromFilePath should error on directory', async () => { // Make sure that the following path actually does point to a directory. const directory = './test/fixtures'; - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath(directory) ); }); it('getApplicationCredentialsFromFilePath should handle errors thrown from createReadStream', async () => { - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath('./does/not/exist.json'), /ENOENT: no such file or directory/ ); @@ -575,7 +571,7 @@ describe('googleauth', () => { it('getApplicationCredentialsFromFilePath should handle errors thrown from fromStream', async () => { sandbox.stub(auth, 'fromStream').throws('🤮'); - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath( './test/fixtures/private.json' ), @@ -586,7 +582,7 @@ describe('googleauth', () => { it('getApplicationCredentialsFromFilePath should handle errors passed from fromStream', async () => { // Set up a mock to return an error from the fromStream method. sandbox.stub(auth, 'fromStream').throws('🤮'); - await assertRejects( + await assert.rejects( auth._getApplicationCredentialsFromFilePath( './test/fixtures/private.json' ), @@ -726,7 +722,7 @@ describe('googleauth', () => { sandbox .stub(auth, '_getApplicationCredentialsFromFilePath') .rejects('🤮'); - await assertRejects( + await assert.rejects( auth._tryGetApplicationCredentialsFromWellKnownFile(), /🤮/ ); @@ -737,7 +733,7 @@ describe('googleauth', () => { sandbox .stub(auth, '_getApplicationCredentialsFromFilePath') .rejects('🤮'); - await assertRejects( + await assert.rejects( auth._tryGetApplicationCredentialsFromWellKnownFile(), /🤮/ ); @@ -994,7 +990,7 @@ describe('googleauth', () => { // * Running on GCE is set to true. mockWindows(); sandbox.stub(auth, '_checkIsGCE').rejects('🤮'); - await assertRejects( + await assert.rejects( auth.getApplicationDefault(), /Unexpected error determining execution environment/ ); @@ -1052,7 +1048,7 @@ describe('googleauth', () => { it('_checkIsGCE should throw on unexpected errors', async () => { assert.notStrictEqual(true, auth.isGCE); const scope = nock500GCE(); - await assertRejects(auth._checkIsGCE()); + await assert.rejects(auth._checkIsGCE()); assert.strictEqual(undefined, auth.isGCE); scope.done(); }); @@ -1096,9 +1092,7 @@ describe('googleauth', () => { const scopes = [ nockIsGCE(), createGetProjectIdNock(), - nock(host) - .get(svcAccountPath) - .reply(200, response, HEADERS), + nock(host).get(svcAccountPath).reply(200, response, HEADERS), ]; await auth._checkIsGCE(); assert.strictEqual(true, auth.isGCE); @@ -1116,13 +1110,11 @@ describe('googleauth', () => { const scopes = [ nockIsGCE(), createGetProjectIdNock(), - nock(HOST_ADDRESS) - .get(svcAccountPath) - .reply(404), + nock(HOST_ADDRESS).get(svcAccountPath).reply(404), ]; await auth._checkIsGCE(); assert.strictEqual(true, auth.isGCE); - await assertRejects( + await assert.rejects( auth.getCredentials(), /Unsuccessful response status code. Request failed with status code 404/ ); @@ -1133,13 +1125,11 @@ describe('googleauth', () => { const scopes = [ nockIsGCE(), createGetProjectIdNock(), - nock(HOST_ADDRESS) - .get(svcAccountPath) - .reply(200, {}), + nock(HOST_ADDRESS).get(svcAccountPath).reply(200, {}), ]; await auth._checkIsGCE(); assert.strictEqual(true, auth.isGCE); - await assertRejects( + await assert.rejects( auth.getCredentials(), /Invalid response from metadata service: incorrect Metadata-Flavor header./ ); @@ -1202,7 +1192,7 @@ describe('googleauth', () => { // Set up a mock to return a null path string const client = await auth._tryGetApplicationCredentialsFromEnvironmentVariable(); assert.strictEqual(null, client); - await assertRejects(auth.getCredentials()); + await assert.rejects(auth.getCredentials()); }); it('should use jsonContent if available', async () => { @@ -1224,7 +1214,7 @@ describe('googleauth', () => { it('should error when invalid keyFilename passed to getClient', async () => { const auth = new GoogleAuth({keyFilename: './funky/fresh.json'}); - await assertRejects( + await assert.rejects( auth.getClient(), /ENOENT: no such file or directory/ ); @@ -1361,11 +1351,7 @@ describe('googleauth', () => { const {auth, scopes} = mockGCE(); scopes.push(createGetProjectIdNock()); const data = {breakfast: 'coffee'}; - scopes.push( - nock(url) - .get('/') - .reply(200, data) - ); + scopes.push(nock(url).get('/').reply(200, data)); const res = await auth.request({url}); scopes.forEach(s => s.done()); assert.deepStrictEqual(res.data, data); @@ -1395,9 +1381,7 @@ describe('googleauth', () => { const signedBlob = 'erutangis'; const data = 'abc123'; scopes.push( - nock(iamUri) - .post(iamPath) - .reply(200, {signedBlob}), + nock(iamUri).post(iamPath).reply(200, {signedBlob}), nock(host) .get(svcAccountPath) .reply(200, {default: {email, private_key: privateKey}}, HEADERS) @@ -1420,7 +1404,7 @@ describe('googleauth', () => { it('should throw if getProjectId cannot find a projectId', async () => { // tslint:disable-next-line no-any sinon.stub(auth as any, 'getDefaultServiceProjectId').resolves(); - await assertRejects( + await assert.rejects( auth.getProjectId(), /Unable to detect a Project Id in the current environment/ ); @@ -1428,7 +1412,7 @@ describe('googleauth', () => { it('should throw if options are passed to getClient()', async () => { const auth = new GoogleAuth(); - await assertRejects( + await assert.rejects( auth.getClient({hello: 'world'}), /Passing options to getClient is forbidden in v5.0.0/ ); @@ -1475,7 +1459,7 @@ describe('googleauth', () => { assert(client instanceof UserRefreshClient); const apiReq = nock(BASE_URL) .post(ENDPOINT) - .reply(function(uri) { + .reply(function (uri) { assert.strictEqual( this.req.headers['x-goog-user-project'][0], 'my-quota-project' diff --git a/test/test.index.ts b/test/test.index.ts index 935f21c1..a68e00f3 100644 --- a/test/test.index.ts +++ b/test/test.index.ts @@ -17,11 +17,13 @@ import * as gal from '../src'; describe('index', () => { it('should publicly export GoogleAuth', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires const cjs = require('../src/'); assert.strictEqual(cjs.GoogleAuth, gal.GoogleAuth); }); it('should publicly export DefaultTransporter', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires const cjs = require('../src'); assert.strictEqual(cjs.DefaultTransporter, gal.DefaultTransporter); }); diff --git a/test/test.jwt.ts b/test/test.jwt.ts index 4b26a573..ac643dd4 100644 --- a/test/test.jwt.ts +++ b/test/test.jwt.ts @@ -23,6 +23,7 @@ import {GoogleAuth, JWT} from '../src'; import {CredentialRequest, JWTInput} from '../src/auth/credentials'; describe('jwt', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires const keypair = require('keypair'); const PEM_PATH = './test/fixtures/private.pem'; const PEM_CONTENTS = fs.readFileSync(PEM_PATH, 'utf8'); @@ -288,10 +289,7 @@ describe('jwt', () => { // endpoint. This makes sure that refreshToken is called only once. const scopes = [ createGTokenMock({access_token: 'abc123'}), - nock('http://example.com') - .get('/') - .thrice() - .reply(200), + nock('http://example.com').get('/').thrice().reply(200), ]; const jwt = new JWT({ email: 'foo@serviceaccount.com', @@ -316,10 +314,7 @@ describe('jwt', () => { const scopes = [ createGTokenMock({access_token: 'abc123'}), createGTokenMock({access_token: 'abc123'}), - nock('http://example.com') - .get('/') - .twice() - .reply(200), + nock('http://example.com').get('/').twice().reply(200), ]; const jwt = new JWT({ email: 'foo@serviceaccount.com', @@ -404,10 +399,7 @@ describe('jwt', () => { }); it('should refresh token if the server returns 403', done => { - nock('http://example.com') - .get('/access') - .twice() - .reply(403); + nock('http://example.com').get('/access').twice().reply(403); const jwt = new JWT({ email: 'foo@serviceaccount.com', keyFile: PEM_PATH, @@ -745,6 +737,7 @@ describe('jwt', () => { it('getRequestHeaders populates x-goog-user-project for JWT client', async () => { const auth = new GoogleAuth({ credentials: Object.assign( + // eslint-disable-next-line @typescript-eslint/no-var-requires require('../../test/fixtures/service-account-with-quota.json'), { private_key: keypair(512 /* bitsize of private key */).private, diff --git a/test/test.jwtaccess.ts b/test/test.jwtaccess.ts index ca615b4b..afe57b2a 100644 --- a/test/test.jwtaccess.ts +++ b/test/test.jwtaccess.ts @@ -21,6 +21,7 @@ import * as sinon from 'sinon'; import {JWTAccess} from '../src'; import * as messages from '../src/messages'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const keypair = require('keypair'); describe('jwtaccess', () => { diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index f0b54947..718f91c8 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -14,7 +14,6 @@ import * as assert from 'assert'; import {describe, it, beforeEach, afterEach} from 'mocha'; -const assertRejects = require('assert-rejects'); import * as crypto from 'crypto'; import * as formatEcdsa from 'ecdsa-sig-formatter'; import * as fs from 'fs'; @@ -86,16 +85,12 @@ describe('oauth2', () => { }); const generated = oauth2client.generateAuthUrl(opts); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.response_type, 'code token'); - assert.strictEqual(query.access_type, ACCESS_TYPE); - assert.strictEqual(query.scope, SCOPE); - assert.strictEqual(query.client_id, CLIENT_ID); - assert.strictEqual(query.redirect_uri, REDIRECT_URI); + const query = new URL(generated).searchParams; + assert.strictEqual(query.get('response_type'), 'code token'); + assert.strictEqual(query.get('access_type'), ACCESS_TYPE); + assert.strictEqual(query.get('scope'), SCOPE); + assert.strictEqual(query.get('client_id'), CLIENT_ID); + assert.strictEqual(query.get('redirect_uri'), REDIRECT_URI); done(); }); @@ -115,7 +110,7 @@ describe('oauth2', () => { const codes = await client.generateCodeVerifierAsync(); // ensure the code_verifier matches all requirements assert.strictEqual(codes.codeVerifier.length, 128); - const match = codes.codeVerifier.match(/[a-zA-Z0-9\-\.~_]*/); + const match = codes.codeVerifier.match(/[a-zA-Z0-9-.~_]*/); assert(match); if (!match) return; assert(match.length > 0 && match[0] === codes.codeVerifier); @@ -127,13 +122,12 @@ describe('oauth2', () => { code_challenge: codes.codeChallenge, code_challenge_method: CodeChallengeMethod.S256, }); - const parsed = url.parse(authUrl); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const props = qs.parse(parsed.query); - assert.strictEqual(props.code_challenge, codes.codeChallenge); - assert.strictEqual(props.code_challenge_method, CodeChallengeMethod.S256); + const props = new URL(authUrl).searchParams; + assert.strictEqual(props.get('code_challenge'), codes.codeChallenge); + assert.strictEqual( + props.get('code_challenge_method'), + CodeChallengeMethod.S256 + ); }); it('should verifyIdToken properly', async () => { @@ -208,22 +202,14 @@ describe('oauth2', () => { response_type: 'code token', }; const generated = client.generateAuthUrl(opts); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.scope, SCOPE_ARRAY.join(' ')); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('scope'), SCOPE_ARRAY.join(' ')); }); it('should set response_type param to code if none is given while generating the consent page url', () => { const generated = client.generateAuthUrl(); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.response_type, 'code'); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('response_type'), 'code'); }); it('should verify a valid certificate against a jwt', async () => { @@ -291,7 +277,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -331,7 +317,7 @@ describe('oauth2', () => { const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; const validAudiences = ['testaudience', 'extra-audience']; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -364,7 +350,7 @@ describe('oauth2', () => { const signature = signer.sign(privateKey, 'base64'); // Originally: data += '.'+signature; data += signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -402,13 +388,13 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, 'testaudience' ), - /Can\'t parse token envelope/ + /Can't parse token envelope/ ); }); @@ -440,13 +426,13 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, 'testaudience' ), - /Can\'t parse token payload/ + /Can't parse token payload/ ); }); @@ -476,7 +462,7 @@ describe('oauth2', () => { Buffer.from(idToken).toString('base64') + '.' + 'broken-signature'; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -509,7 +495,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -544,7 +530,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -582,7 +568,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -660,7 +646,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -701,7 +687,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -739,7 +725,7 @@ describe('oauth2', () => { signer.update(data); const signature = signer.sign(privateKey, 'base64'); data += '.' + signature; - return assertRejects( + return assert.rejects( client.verifySignedJwtWithCertsAsync( data, {keyid: publicKey}, @@ -883,42 +869,26 @@ describe('oauth2', () => { it('should set redirect_uri if not provided in options', () => { const generated = client.generateAuthUrl({}); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.redirect_uri, REDIRECT_URI); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('redirect_uri'), REDIRECT_URI); }); it('should set client_id if not provided in options', () => { const generated = client.generateAuthUrl({}); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.client_id, CLIENT_ID); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('client_id'), CLIENT_ID); }); it('should override redirect_uri if provided in options', () => { const generated = client.generateAuthUrl({redirect_uri: 'overridden'}); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.redirect_uri, 'overridden'); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('redirect_uri'), 'overridden'); }); it('should override client_id if provided in options', () => { const generated = client.generateAuthUrl({client_id: 'client_override'}); - const parsed = url.parse(generated); - if (typeof parsed.query !== 'string') { - throw new Error('Unable to parse querystring'); - } - const query = qs.parse(parsed.query); - assert.strictEqual(query.client_id, 'client_override'); + const parsed = new URL(generated).searchParams; + assert.strictEqual(parsed.get('client_id'), 'client_override'); }); it('should return error in callback on request', done => { @@ -955,9 +925,7 @@ describe('oauth2', () => { reqheaders: {'content-type': 'application/x-www-form-urlencoded'}, }) .reply(200, {access_token: 'abc123', expires_in: 1}), - nock('http://example.com') - .get('/') - .reply(200), + nock('http://example.com').get('/').reply(200), ]; } @@ -991,10 +959,7 @@ describe('oauth2', () => { reqheaders: {'content-type': 'application/x-www-form-urlencoded'}, }) .reply(200, {access_token: 'abc123', expires_in: 1}), - nock('http://example.com') - .get('/') - .thrice() - .reply(200), + nock('http://example.com').get('/').thrice().reply(200), ]; client.credentials = {refresh_token: 'refresh-token-placeholder'}; await Promise.all([ @@ -1017,10 +982,7 @@ describe('oauth2', () => { }) .twice() .reply(200, {access_token: 'abc123', expires_in: 100000}), - nock('http://example.com') - .get('/') - .twice() - .reply(200), + nock('http://example.com').get('/').twice().reply(200), ]; client.credentials = {refresh_token: 'refresh-token-placeholder'}; await client.request({url: 'http://example.com'}); @@ -1043,13 +1005,12 @@ describe('oauth2', () => { reqheaders: {'content-type': 'application/x-www-form-urlencoded'}, }) .reply(200, {access_token: 'abc123', expires_in: 100000}), - nock('http://example.com') - .get('/') - .reply(200), + nock('http://example.com').get('/').reply(200), ]; client.credentials = {refresh_token: 'refresh-token-placeholder'}; try { await client.request({url: 'http://example.com'}); + // eslint-disable-next-line no-empty } catch (e) {} await client.request({url: 'http://example.com'}); scopes.forEach(s => s.done()); @@ -1194,9 +1155,7 @@ describe('oauth2', () => { it('should not retry requests with streaming data', done => { const s = fs.createReadStream('./test/fixtures/public.pem'); - const scope = nock('http://example.com') - .post('/') - .reply(401); + const scope = nock('http://example.com').post('/').reply(401); client.credentials = { access_token: 'initial-access-token', refresh_token: 'refresh-token-placeholder', @@ -1381,7 +1340,7 @@ describe('oauth2', () => { access_token: 'initial-access-token', expiry_date: new Date().getTime() - 1000, }); - await assertRejects( + await assert.rejects( client.getRequestHeaders('http://example.com'), /No refresh token is set./ ); diff --git a/test/test.transporters.ts b/test/test.transporters.ts index 59a3c7e3..be1c93ed 100644 --- a/test/test.transporters.ts +++ b/test/test.transporters.ts @@ -15,7 +15,6 @@ import * as assert from 'assert'; import {describe, it, afterEach} from 'mocha'; import {GaxiosOptions} from 'gaxios'; -const assertRejects = require('assert-rejects'); import * as nock from 'nock'; import {DefaultTransporter, RequestError} from '../src/transporters'; @@ -111,9 +110,7 @@ describe('transporters', () => { it('should return an error for a 404 response', done => { const url = 'http://example.com'; - const scope = nock(url) - .get('/') - .reply(404, 'Not found'); + const scope = nock(url).get('/').reply(404, 'Not found'); transporter.request({url}, error => { scope.done(); assert.strictEqual(error!.message, 'Not found'); @@ -148,9 +145,7 @@ describe('transporters', () => { it('should support invocation with async/await', async () => { const url = 'http://example.com'; - const scope = nock(url) - .get('/') - .reply(200); + const scope = nock(url).get('/').reply(200); const res = await transporter.request({url}); scope.done(); assert.strictEqual(res.status, 200); @@ -158,18 +153,14 @@ describe('transporters', () => { it('should throw if using async/await', async () => { const url = 'http://example.com'; - const scope = nock(url) - .get('/') - .reply(500, '🦃'); - await assertRejects(transporter.request({url}), /🦃/); + const scope = nock(url).get('/').reply(500, '🦃'); + await assert.rejects(transporter.request({url}), /🦃/); scope.done(); }); it('should work with a callback', done => { const url = 'http://example.com'; - const scope = nock(url) - .get('/') - .reply(200); + const scope = nock(url).get('/').reply(200); transporter.request({url}, (err, res) => { scope.done(); assert.strictEqual(err, null); diff --git a/tsconfig.json b/tsconfig.json index f674b084..e4058efd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { - "lib": ["es2015", "dom"], + "lib": ["es2018", "dom"], "rootDir": ".", "outDir": "build" },