From 7ae2536cd17007327c7b6b9273aea3863419efae Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Fri, 2 Sep 2022 13:39:49 -0700 Subject: [PATCH 1/5] Add back type checking and fix errors. --- src/react-app-env.d.ts => .eslintrc.js | 26 ++- package-lock.json | 210 ++++++++++++++++++++++++- package.json | 18 +-- src/analytics.ts | 36 +---- src/components/Auth/api.ts | 10 +- src/setupProxy.js | 29 ---- tsconfig.json | 8 +- vite.config.ts | 5 + 8 files changed, 254 insertions(+), 88 deletions(-) rename src/react-app-env.d.ts => .eslintrc.js (50%) delete mode 100644 src/setupProxy.js diff --git a/src/react-app-env.d.ts b/.eslintrc.js similarity index 50% rename from src/react-app-env.d.ts rename to .eslintrc.js index 042d18487..ed429ac15 100644 --- a/src/react-app-env.d.ts +++ b/.eslintrc.js @@ -1,5 +1,5 @@ /** - * Copyright 2020 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,24 @@ * limitations under the License. */ -/// -/// -/// +const restrictedGlobals = require('confusing-browser-globals'); + +module.exports = { + extends: [ + 'react-app', + 'plugin:jest/recommended', + 'plugin:react-hooks/recommended', + ], + plugins: ['jest'], + rules: { + 'jest/no-jasmine-globals': 'error', + 'no-restricted-globals': [ + 'error', + { + // Our setup does not use global var gtag (declared by @types/gtag.js). + name: 'gtag', + message: "Use `import { gtag } from './analytics';` instead.", + }, + ].concat(restrictedGlobals), + }, +}; diff --git a/package-lock.json b/package-lock.json index 9c3531dc9..d255e7e01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,8 +72,9 @@ "swr": "^1.3.0", "tslib": "^2.4.0", "typesafe-actions": "^5.1.0", - "typescript": "^4.1.5", + "typescript": "^4.8.2", "vite": "^2.9.13", + "vite-plugin-checker": "^0.5.0", "vite-plugin-node": "^1.0.0", "vite-plugin-rewrite-all": "^1.0.0", "vite-plugin-svgr": "^2.1.0" @@ -83,6 +84,7 @@ "@firebase/component": "^0.5.0", "@originjs/vite-plugin-commonjs": "^1.0.3", "@testing-library/react": "^13.2.0", + "@types/gtag.js": "^0.0.11", "@types/lodash.get": "^4.4.6", "@types/lodash.groupby": "^4.6.7", "@types/lodash.isequal": "^4.5.5", @@ -91,6 +93,7 @@ "@types/react-router": "^5.1.18", "@types/react-router-dom": "^5.3.0", "@types/redux-mock-store": "^1.0.2", + "confusing-browser-globals": "^1.0.11", "eslint-plugin-jest": "^23.11.0", "eslint-plugin-react-hooks": "^4.0.0", "globalthis": "^1.0.1", @@ -5172,6 +5175,12 @@ "@types/node": "*" } }, + "node_modules/@types/gtag.js": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.11.tgz", + "integrity": "sha512-rUuSDedDjcuUpoc2zf6eX6zRrxqALNgwrmMBfVFopkLH7YGM52C7tt6j9GsYIvaxn+ioVRpOKoHnN1DXzHEqIg==", + "dev": true + }, "node_modules/@types/hast": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", @@ -13276,6 +13285,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -18952,9 +18966,9 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19391,6 +19405,52 @@ } } }, + "node_modules/vite-plugin-checker": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.5.0.tgz", + "integrity": "sha512-IuCHIpnJwRxDupd+jVNwza07+ajt4jFItwWMFEF2cDDp5E92ePx1eVn91JMsa02XkquR1s6L4VZX8/RTFamD9w==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "ansi-escapes": "^4.3.0", + "chalk": "^4.1.1", + "chokidar": "^3.5.1", + "commander": "^8.0.0", + "fast-glob": "^3.2.7", + "lodash.debounce": "^4.0.8", + "lodash.pick": "^4.4.0", + "npm-run-path": "^4.0.1", + "strip-ansi": "^6.0.0", + "tiny-invariant": "^1.1.0", + "vscode-languageclient": "^7.0.0", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-uri": "^3.0.2" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "eslint": ">=7", + "typescript": "*", + "vite": "^2.0.0 || ^3.0.0-0", + "vls": "*", + "vti": "*" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vls": { + "optional": true + }, + "vti": { + "optional": true + } + } + }, "node_modules/vite-plugin-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/vite-plugin-node/-/vite-plugin-node-1.0.0.tgz", @@ -19662,6 +19722,62 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + }, + "engines": { + "vscode": "^1.52.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz", + "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==", + "dependencies": { + "vscode-languageserver-protocol": "3.16.0" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "dependencies": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", + "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "node_modules/vscode-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", + "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -24417,6 +24533,12 @@ "@types/node": "*" } }, + "@types/gtag.js": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.11.tgz", + "integrity": "sha512-rUuSDedDjcuUpoc2zf6eX6zRrxqALNgwrmMBfVFopkLH7YGM52C7tt6j9GsYIvaxn+ioVRpOKoHnN1DXzHEqIg==", + "dev": true + }, "@types/hast": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", @@ -30411,6 +30533,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -34308,9 +34435,9 @@ "integrity": "sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==" }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==" + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" }, "typescript-compare": { "version": "0.0.2", @@ -34600,6 +34727,28 @@ "rollup": "^2.59.0" } }, + "vite-plugin-checker": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.5.0.tgz", + "integrity": "sha512-IuCHIpnJwRxDupd+jVNwza07+ajt4jFItwWMFEF2cDDp5E92ePx1eVn91JMsa02XkquR1s6L4VZX8/RTFamD9w==", + "requires": { + "@babel/code-frame": "^7.12.13", + "ansi-escapes": "^4.3.0", + "chalk": "^4.1.1", + "chokidar": "^3.5.1", + "commander": "^8.0.0", + "fast-glob": "^3.2.7", + "lodash.debounce": "^4.0.8", + "lodash.pick": "^4.4.0", + "npm-run-path": "^4.0.1", + "strip-ansi": "^6.0.0", + "tiny-invariant": "^1.1.0", + "vscode-languageclient": "^7.0.0", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-uri": "^3.0.2" + } + }, "vite-plugin-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/vite-plugin-node/-/vite-plugin-node-1.0.0.tgz", @@ -34742,6 +34891,53 @@ } } }, + "vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" + }, + "vscode-languageclient": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "requires": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + } + }, + "vscode-languageserver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz", + "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==", + "requires": { + "vscode-languageserver-protocol": "3.16.0" + } + }, + "vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "requires": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz", + "integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==" + }, + "vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "vscode-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", + "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index 574b31519..a5b2377c8 100644 --- a/package.json +++ b/package.json @@ -70,8 +70,9 @@ "swr": "^1.3.0", "tslib": "^2.4.0", "typesafe-actions": "^5.1.0", - "typescript": "^4.1.5", + "typescript": "^4.8.2", "vite": "^2.9.13", + "vite-plugin-checker": "^0.5.0", "vite-plugin-node": "^1.0.0", "vite-plugin-rewrite-all": "^1.0.0", "vite-plugin-svgr": "^2.1.0" @@ -92,19 +93,6 @@ "format": "prettier --write .", "prepare": "husky install" }, - "eslintConfig": { - "extends": [ - "react-app", - "plugin:jest/recommended", - "plugin:react-hooks/recommended" - ], - "plugins": [ - "jest" - ], - "rules": { - "jest/no-jasmine-globals": "error" - } - }, "browserslist": { "production": [ ">0.2%", @@ -122,6 +110,7 @@ "@firebase/component": "^0.5.0", "@originjs/vite-plugin-commonjs": "^1.0.3", "@testing-library/react": "^13.2.0", + "@types/gtag.js": "^0.0.11", "@types/lodash.get": "^4.4.6", "@types/lodash.groupby": "^4.6.7", "@types/lodash.isequal": "^4.5.5", @@ -130,6 +119,7 @@ "@types/react-router": "^5.1.18", "@types/react-router-dom": "^5.3.0", "@types/redux-mock-store": "^1.0.2", + "confusing-browser-globals": "^1.0.11", "eslint-plugin-jest": "^23.11.0", "eslint-plugin-react-hooks": "^4.0.0", "globalthis": "^1.0.1", diff --git a/src/analytics.ts b/src/analytics.ts index f017b2460..af8ccb311 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -23,34 +23,10 @@ const USAGE_CONFIG_API = '/api/config'; // Types for gtag // https://developers.google.com/analytics/devguides/collection/gtagjs#install_the_global_site_tag interface WindowWithGA extends Window { - _gtag: Gtag; + _gtag: Gtag.Gtag; [key: `ga-disable-${string}`]: boolean; } -// Roughed out types for gtag because the default TS types for -// gtag expect it to be global, but we don't want it to be callable from -// anywhere in the app (only here, where we can control whether events -// are sent or not based on user preferences) -interface Gtag { - ( - command: 'config', - targetId: string, - config?: { [key: string]: unknown } - ): void; - (command: 'set', config: { [key: string]: unknown }): void; - ( - command: 'event', - eventName: string, - eventParams?: { [key: string]: unknown } - ): void; - ( - command: 'consent', - consentArg: 'default' | 'update' | 'default', - consentParams: { [key: string]: unknown } - ): void; - (command: 'js', config: Date): void; -} - /** * Used to report usage data to Google Analytics. * @@ -65,16 +41,20 @@ interface Gtag { * the Analytics backend * */ -export const gtag: Gtag = function (eventName, eventParams, options?) { +export const gtag: Gtag.Gtag = function () { initGtag(); - (window as WindowWithGA)._gtag.apply(window, arguments); + (window as unknown as WindowWithGA)._gtag.apply( + window, + arguments as unknown as Parameters + ); }; let loadedMeasurementId: string | undefined = undefined; function disableGtag() { if (loadedMeasurementId) { - (window as WindowWithGA)[`ga-disable-${loadedMeasurementId}`] = true; + (window as unknown as WindowWithGA)[`ga-disable-${loadedMeasurementId}`] = + true; } } diff --git a/src/components/Auth/api.ts b/src/components/Auth/api.ts index 24b1fe948..6991bbff8 100644 --- a/src/components/Auth/api.ts +++ b/src/components/Auth/api.ts @@ -43,9 +43,9 @@ export default class AuthApi extends RestApi { // clients and infers projectId, which can lead to unexpected mismatches. // https://cloud.google.com/identity-platform/docs/reference/rest/v1/projects.accounts - private readonly baseUrl = `http://${this.hostAndPort}/identitytoolkit.googleapis.com/v1/projects/${this.projectId}`; - private readonly baseUrlV2 = `http://${this.hostAndPort}/identitytoolkit.googleapis.com/v2/projects/${this.projectId}`; - private readonly baseEmulatorUrl = `http://${this.hostAndPort}/emulator/v1/projects/${this.projectId}`; + private readonly baseUrl: string; + private readonly baseUrlV2: string; + private readonly baseEmulatorUrl: string; constructor( private readonly hostAndPort: string, @@ -53,6 +53,10 @@ export default class AuthApi extends RestApi { private readonly tenantId?: string ) { super(); + // https://cloud.google.com/identity-platform/docs/reference/rest/v1/projects.accounts + this.baseUrl = `http://${this.hostAndPort}/identitytoolkit.googleapis.com/v1/projects/${this.projectId}`; + this.baseUrlV2 = `http://${this.hostAndPort}/identitytoolkit.googleapis.com/v2/projects/${this.projectId}`; + this.baseEmulatorUrl = `http://${this.hostAndPort}/emulator/v1/projects/${this.projectId}`; } async nukeUsersForAllTenants(): Promise { diff --git a/src/setupProxy.js b/src/setupProxy.js deleted file mode 100644 index 69c04ff7a..000000000 --- a/src/setupProxy.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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. - */ - -/** - This file tells `npm start` (or more precisely, `react-scripts start`) to load - middlewares and APIs defined in ../server.js into the Webpack Dev Server, so - that those things work as expected during development. - - Note that setupProxy is probably the wrong name for this file -- it never - proxies anything. Instead, it just enhances the dev express app itself. - It's only named setupProxy.js so that react-scripts can pick it up. See also: - https://create-react-app.dev/docs/proxying-api-requests-in-development/#configuring-the-proxy-manually -*/ - -const registerApis = require('../server').registerApis; -module.exports = registerApis; diff --git a/tsconfig.json b/tsconfig.json index 1cb860a7a..3217f5d7d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { "target": "ESNext", - "types": ["vite/client"], + "types": ["vite/client", "@types/gtag.js"], "lib": ["dom", "dom.iterable", "es2020.string", "esnext"], "allowJs": false, - "skipLibCheck": false, + "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, @@ -17,5 +17,7 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": ["src", "server.ts"] + "include": ["src", "server.ts"], + // TODO: Somehow type check the tests too. + "exclude": ["src/**/*.test.*", "src/test_utils.tsx", "src/**/testing/*"] } diff --git a/vite.config.ts b/vite.config.ts index 888c88d4d..8248b69e3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -17,6 +17,7 @@ import { viteCommonjs } from '@originjs/vite-plugin-commonjs'; import react from '@vitejs/plugin-react'; import { UserConfig, defineConfig } from 'vite'; +import checker from 'vite-plugin-checker'; import { VitePluginNode } from 'vite-plugin-node'; import pluginRewriteAll from 'vite-plugin-rewrite-all'; import svgrPlugin from 'vite-plugin-svgr'; @@ -39,6 +40,10 @@ export default defineConfig(({ command, mode }) => { }, }), viteCommonjs(), + checker({ + typescript: true, + eslint: { lintCommand: 'eslint "src/**/*.{js,ts,tsx}"' }, + }), // Fix Vite routing paths with dots (used in Storage/Firestore/etc.): // https://github.com/vitejs/vite/issues/2415 From 790ee4ee73487905f6eea7bea479e4275b43c033 Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Fri, 2 Sep 2022 14:26:12 -0700 Subject: [PATCH 2/5] Fix most linter warnings. --- .eslintrc.js | 3 +- package-lock.json | 46 +++++++++++++++++++ package.json | 1 + src/__mocks__/react-markdown.js | 3 +- src/components/App/index.test.tsx | 2 - .../OneAccountPerEmailCard.test.tsx | 2 - .../Auth/UserFormDialog/UserForm.tsx | 4 +- .../controls/CustomAttributes.test.tsx | 4 +- .../controls/ImageUrlInput.test.tsx | 7 ++- .../controls/PhoneControl.test.tsx | 1 - .../Auth/UsersCard/UsersCard.test.tsx | 1 - .../Database/DataViewer/NodeLeaf.test.tsx | 5 +- .../Database/DataViewer/NodeLink.test.tsx | 3 +- .../Database/DatabaseEmulatedApiProvider.tsx | 2 +- src/components/Database/index.tsx | 1 - .../testing/DatabaseTestProviders.tsx | 7 ++- .../Tabs/EventsCodeSnippet.test.tsx | 3 +- .../api/internal/useExtensionsData.test.tsx | 2 + src/components/Firestore/Collection.test.tsx | 2 +- .../CollectionFilter.test.tsx | 1 - .../DocumentEditor/JsonEditor.test.tsx | 2 - .../Firestore/DocumentEditor/index.test.tsx | 6 +-- .../Firestore/DocumentListItem.test.tsx | 2 +- .../Firestore/DocumentPreview/index.test.tsx | 22 ++++----- .../FirestoreEmulatedApiProvider.tsx | 7 +-- .../dialogs/AddCollectionDialog.test.tsx | 3 +- .../dialogs/AddDocumentDialog.test.tsx | 32 +++++-------- .../testing/FirestoreTestProviders.tsx | 2 +- src/components/Home/index.test.tsx | 1 - .../Storage/Card/StorageCard.test.tsx | 2 +- .../providers/StorageFirebaseAppProvider.tsx | 2 +- .../Storage/testing/renderWithStorage.tsx | 11 ++--- .../useMainDetail/useMainDetail.test.tsx | 2 +- 33 files changed, 104 insertions(+), 90 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index ed429ac15..667e39b9b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,9 +22,10 @@ module.exports = { 'plugin:jest/recommended', 'plugin:react-hooks/recommended', ], - plugins: ['jest'], + plugins: ['jest', 'unused-imports'], rules: { 'jest/no-jasmine-globals': 'error', + 'unused-imports/no-unused-imports-ts': 'error', 'no-restricted-globals': [ 'error', { diff --git a/package-lock.json b/package-lock.json index d255e7e01..7e5361507 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,7 @@ "confusing-browser-globals": "^1.0.11", "eslint-plugin-jest": "^23.11.0", "eslint-plugin-react-hooks": "^4.0.0", + "eslint-plugin-unused-imports": "^2.0.0", "globalthis": "^1.0.1", "husky": "^8.0.0", "import-sort-config": "^6.0.0", @@ -9408,6 +9409,36 @@ "eslint": "^7.5.0 || ^8.0.0" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz", + "integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -27673,6 +27704,21 @@ "@typescript-eslint/utils": "^5.13.0" } }, + "eslint-plugin-unused-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz", + "integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==", + "dev": true, + "requires": { + "eslint-rule-composer": "^0.3.0" + } + }, + "eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true + }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", diff --git a/package.json b/package.json index a5b2377c8..8cb6e06bd 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "confusing-browser-globals": "^1.0.11", "eslint-plugin-jest": "^23.11.0", "eslint-plugin-react-hooks": "^4.0.0", + "eslint-plugin-unused-imports": "^2.0.0", "globalthis": "^1.0.1", "husky": "^8.0.0", "import-sort-config": "^6.0.0", diff --git a/src/__mocks__/react-markdown.js b/src/__mocks__/react-markdown.js index a727c5916..cab14d046 100644 --- a/src/__mocks__/react-markdown.js +++ b/src/__mocks__/react-markdown.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const React = require('react'); function ReactMarkdown({ children }) { - return <>Markdown: {children}; + return React.createElement(React.Fragment, {}, 'Markdown: ', children); } export default ReactMarkdown; diff --git a/src/components/App/index.test.tsx b/src/components/App/index.test.tsx index f088daa34..948969f1e 100644 --- a/src/components/App/index.test.tsx +++ b/src/components/App/index.test.tsx @@ -15,9 +15,7 @@ */ import { fireEvent, render } from '@testing-library/react'; -import { createMemoryHistory } from 'history'; import React from 'react'; -import ReactDOM from 'react-dom'; import { act } from 'react-dom/test-utils'; import { BrowserRouter } from 'react-router-dom'; diff --git a/src/components/Auth/OneAccountPerEmailCard/OneAccountPerEmailCard.test.tsx b/src/components/Auth/OneAccountPerEmailCard/OneAccountPerEmailCard.test.tsx index 739fbd6d3..bb6be82fc 100644 --- a/src/components/Auth/OneAccountPerEmailCard/OneAccountPerEmailCard.test.tsx +++ b/src/components/Auth/OneAccountPerEmailCard/OneAccountPerEmailCard.test.tsx @@ -18,9 +18,7 @@ import { Portal } from '@rmwc/base'; import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import { AppState } from '../../../store'; import { waitForDialogsToOpen } from '../../../test_utils'; import { getMockAuthStore } from '../test_utils'; import { diff --git a/src/components/Auth/UserFormDialog/UserForm.tsx b/src/components/Auth/UserFormDialog/UserForm.tsx index 159782529..26cefe30a 100644 --- a/src/components/Auth/UserFormDialog/UserForm.tsx +++ b/src/components/Auth/UserFormDialog/UserForm.tsx @@ -147,11 +147,11 @@ export const UserForm: React.FC> = ({ const { register, handleSubmit, - formState: { errors, isValid }, + formState: { errors }, reset, } = form; - // FIXME: Should be able to just check isValid, instead of checking + // FIXME: Should be able to just check form.isValid, instead of checking // that there are no errors. This only happens when `atLeastOneMethodRequired` // is the only error present and is causing the "Save" and "Save and create // another" buttons to remain enabled even when neither email/password or diff --git a/src/components/Auth/UserFormDialog/controls/CustomAttributes.test.tsx b/src/components/Auth/UserFormDialog/controls/CustomAttributes.test.tsx index 6fd850ed6..4710490ef 100644 --- a/src/components/Auth/UserFormDialog/controls/CustomAttributes.test.tsx +++ b/src/components/Auth/UserFormDialog/controls/CustomAttributes.test.tsx @@ -14,8 +14,6 @@ * limitations under the License. */ -import React from 'react'; - import { wrapWithForm } from '../../../../test_utils'; import { CustomAttributes } from './CustomAttributes'; @@ -60,7 +58,7 @@ describe('CustomAttributes', () => { }); it('displays an error if a forbidden key was used', async () => { - const { triggerValidation, getByRole, getByText } = setup( + const { triggerValidation, getByRole } = setup( '{"firebase": "is awesome"}' ); await triggerValidation(); diff --git a/src/components/Auth/UserFormDialog/controls/ImageUrlInput.test.tsx b/src/components/Auth/UserFormDialog/controls/ImageUrlInput.test.tsx index 8ac47e782..0c829ffda 100644 --- a/src/components/Auth/UserFormDialog/controls/ImageUrlInput.test.tsx +++ b/src/components/Auth/UserFormDialog/controls/ImageUrlInput.test.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import React from 'react'; import { act } from 'react-dom/test-utils'; import { wrapWithForm } from '../../../../test_utils'; @@ -51,7 +50,7 @@ describe('ImageUrlInput', () => { }); await triggerValidation(); - getByText('Error loading image'); + expect(getByText('Error loading image')).not.toBeNull(); }); it('displays an image if image loaded', async () => { @@ -64,13 +63,13 @@ describe('ImageUrlInput', () => { }); await triggerValidation(); - getByAltText('Profile preview'); + expect(getByAltText('Profile preview')).not.toBeNull(); }); it('displays loading spinner', async () => { const { getByTestId, triggerValidation } = setup({ photoUrl: 'lol.png' }); await triggerValidation(); - getByTestId('spinner'); + expect(getByTestId('spinner')).not.toBeNull(); }); it('displays nothing if there is no image', async () => { diff --git a/src/components/Auth/UserFormDialog/controls/PhoneControl.test.tsx b/src/components/Auth/UserFormDialog/controls/PhoneControl.test.tsx index 67383356d..0b520fb3f 100644 --- a/src/components/Auth/UserFormDialog/controls/PhoneControl.test.tsx +++ b/src/components/Auth/UserFormDialog/controls/PhoneControl.test.tsx @@ -15,7 +15,6 @@ */ import { fireEvent } from '@testing-library/react'; -import React from 'react'; import { wrapWithForm } from '../../../../test_utils'; import { PhoneControl, PhoneControlProps } from './PhoneControl'; diff --git a/src/components/Auth/UsersCard/UsersCard.test.tsx b/src/components/Auth/UsersCard/UsersCard.test.tsx index 9c5842361..85b7f5cd2 100644 --- a/src/components/Auth/UsersCard/UsersCard.test.tsx +++ b/src/components/Auth/UsersCard/UsersCard.test.tsx @@ -21,7 +21,6 @@ import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; import { AppState } from '../../../store'; -import { openAuthUserDialog } from '../../../store/auth/actions'; import { createRemoteDataLoaded } from '../../../store/utils'; import { waitForDialogsToOpen } from '../../../test_utils'; import { createFakeAuthStateWithUsers } from '../test_utils'; diff --git a/src/components/Database/DataViewer/NodeLeaf.test.tsx b/src/components/Database/DataViewer/NodeLeaf.test.tsx index 1c14366f6..ffb96a46d 100644 --- a/src/components/Database/DataViewer/NodeLeaf.test.tsx +++ b/src/components/Database/DataViewer/NodeLeaf.test.tsx @@ -14,10 +14,9 @@ * limitations under the License. */ -import { act, render } from '@testing-library/react'; -import { DatabaseReference, ref } from 'firebase/database'; +import { act } from '@testing-library/react'; +import { ref } from 'firebase/database'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; import { renderWithDatabase } from '../testing/DatabaseTestProviders'; import { NodeLeaf } from './NodeLeaf'; diff --git a/src/components/Database/DataViewer/NodeLink.test.tsx b/src/components/Database/DataViewer/NodeLink.test.tsx index ad4f49d5f..3ba9206df 100644 --- a/src/components/Database/DataViewer/NodeLink.test.tsx +++ b/src/components/Database/DataViewer/NodeLink.test.tsx @@ -14,10 +14,9 @@ * limitations under the License. */ -import { render } from '@testing-library/react'; import { ref } from 'firebase/database'; import React from 'react'; -import { MemoryRouter, Route } from 'react-router-dom'; +import { Route } from 'react-router-dom'; import { renderWithDatabase } from '../testing/DatabaseTestProviders'; import { NodeLink } from './NodeLink'; diff --git a/src/components/Database/DatabaseEmulatedApiProvider.tsx b/src/components/Database/DatabaseEmulatedApiProvider.tsx index 6fde23fcb..87c299917 100644 --- a/src/components/Database/DatabaseEmulatedApiProvider.tsx +++ b/src/components/Database/DatabaseEmulatedApiProvider.tsx @@ -25,7 +25,7 @@ import { DatabaseProvider, FirebaseAppProvider } from 'reactfire'; import { useEmulatedFirebaseApp } from '../../firebase'; import { useEmulatorConfig } from '../common/EmulatorConfigProvider'; -import { NamespaceProvider, useNamespace } from './useNamespace'; +import { NamespaceProvider } from './useNamespace'; const DATABASE_OPTIONS = {}; diff --git a/src/components/Database/index.tsx b/src/components/Database/index.tsx index 8bf17ec0f..9f457ac05 100644 --- a/src/components/Database/index.tsx +++ b/src/components/Database/index.tsx @@ -29,7 +29,6 @@ import { Spinner } from '../common/Spinner'; import Database from './Database'; import DatabaseContainer from './DatabaseContainer'; import { DatabaseEmulatedApiProvider } from './DatabaseEmulatedApiProvider'; -import { NamespaceProvider } from './useNamespace'; export const DatabaseRoute: React.FC> = () => { return ( diff --git a/src/components/Database/testing/DatabaseTestProviders.tsx b/src/components/Database/testing/DatabaseTestProviders.tsx index 982b6c86b..262a1d87e 100644 --- a/src/components/Database/testing/DatabaseTestProviders.tsx +++ b/src/components/Database/testing/DatabaseTestProviders.tsx @@ -16,9 +16,8 @@ import { randomUUID } from 'crypto'; -import { render, waitForElementToBeRemoved } from '@testing-library/react'; -import { Database, ref, set } from 'firebase/database'; -import { uniqueId } from 'lodash'; +import { render } from '@testing-library/react'; +import { Database } from 'firebase/database'; import React, { Suspense, useEffect, useState } from 'react'; import { MemoryRouter } from 'react-router-dom'; import { useDatabase } from 'reactfire'; @@ -108,7 +107,7 @@ const AsyncDatabase: React.FC< r(database) .then((c) => setDatabaseChildren(c)) .catch(onError); - }, [r, database, setDatabaseChildren]); + }, [r, database, setDatabaseChildren, onError]); return databaseChildren ? (
{databaseChildren}
diff --git a/src/components/Extensions/Details/DetailCard/Tabs/EventsCodeSnippet.test.tsx b/src/components/Extensions/Details/DetailCard/Tabs/EventsCodeSnippet.test.tsx index 438b36507..84ff236e4 100644 --- a/src/components/Extensions/Details/DetailCard/Tabs/EventsCodeSnippet.test.tsx +++ b/src/components/Extensions/Details/DetailCard/Tabs/EventsCodeSnippet.test.tsx @@ -15,6 +15,7 @@ */ import { render } from '@testing-library/react'; +import React from 'react'; import { Extension } from '../../../models'; import { TestExtensionsProvider } from '../../../testing/TestExtensionsProvider'; @@ -34,7 +35,7 @@ describe('EventsCodeSnippet', () => { allowedEventTypes: ['google.firebase.v1.custom-event-occurred'], } as Extension; - const { queryByTestId, getByText } = render( + const { getByText } = render( diff --git a/src/components/Extensions/api/internal/useExtensionsData.test.tsx b/src/components/Extensions/api/internal/useExtensionsData.test.tsx index fb415d765..86e15dab2 100644 --- a/src/components/Extensions/api/internal/useExtensionsData.test.tsx +++ b/src/components/Extensions/api/internal/useExtensionsData.test.tsx @@ -77,6 +77,7 @@ describe('useExtensionsData', () => { 'Listens for new images uploaded to your specified Cloud Storage bucket, resizes the images, then stores the resized images in the same bucket. Optionally keeps or deletes the original images.', name: 'generateResizedImage', propertiesYaml: + // eslint-disable-next-line no-template-curly-in-string 'availableMemoryMb: 1024\neventTrigger:\n eventType: google.storage.object.finalize\n resource: projects/_/buckets/${param:IMG_BUCKET}\nlocation: ${param:LOCATION}\nruntime: nodejs14\n', }, ], @@ -133,6 +134,7 @@ describe('useExtensionsData', () => { 'Listens for new images uploaded to your specified Cloud Storage bucket, resizes the images, then stores the resized images in the same bucket. Optionally keeps or deletes the original images.', name: 'generateResizedImage', propertiesYaml: + // eslint-disable-next-line no-template-curly-in-string 'availableMemoryMb: 1024\neventTrigger:\n eventType: google.storage.object.finalize\n resource: projects/_/buckets/${param:IMG_BUCKET}\nlocation: ${param:LOCATION}\nruntime: nodejs14\n', }, ], diff --git a/src/components/Firestore/Collection.test.tsx b/src/components/Firestore/Collection.test.tsx index a80500b91..1055375f2 100644 --- a/src/components/Firestore/Collection.test.tsx +++ b/src/components/Firestore/Collection.test.tsx @@ -20,7 +20,7 @@ import { waitFor, waitForElementToBeRemoved, } from '@testing-library/react'; -import { collection, doc, getDoc, getDocs, setDoc } from 'firebase/firestore'; +import { collection, doc, getDocs, setDoc } from 'firebase/firestore'; import React from 'react'; import { Route } from 'react-router-dom'; diff --git a/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx b/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx index dd4b225a4..8c1bb8340 100644 --- a/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx +++ b/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx @@ -15,7 +15,6 @@ */ import { - RenderResult, act, fireEvent, render, diff --git a/src/components/Firestore/DocumentEditor/JsonEditor.test.tsx b/src/components/Firestore/DocumentEditor/JsonEditor.test.tsx index 1e6222e53..68b34ef5a 100644 --- a/src/components/Firestore/DocumentEditor/JsonEditor.test.tsx +++ b/src/components/Firestore/DocumentEditor/JsonEditor.test.tsx @@ -20,8 +20,6 @@ import { FormProvider, useForm } from 'react-hook-form'; import JsonEditor from './JsonEditor'; -const GOOD_PATH = '/wow/cool'; - const TestForm: React.FC> = ({ children }) => { const methods = useForm({ mode: 'all' }); return {children}; diff --git a/src/components/Firestore/DocumentEditor/index.test.tsx b/src/components/Firestore/DocumentEditor/index.test.tsx index 4edd7a3ba..37da52b5f 100644 --- a/src/components/Firestore/DocumentEditor/index.test.tsx +++ b/src/components/Firestore/DocumentEditor/index.test.tsx @@ -320,9 +320,9 @@ describe('changing types', () => { act(() => { setType(FieldType.TIMESTAMP); }); - const [date, time] = ( - getByLabelText(/Value/) as HTMLInputElement - ).value.split('T'); + const [date] = (getByLabelText(/Value/) as HTMLInputElement).value.split( + 'T' + ); expect(date).toEqual(expect.stringMatching(/\d{4}-\d{2}-\d{2}/)); }); }); diff --git a/src/components/Firestore/DocumentListItem.test.tsx b/src/components/Firestore/DocumentListItem.test.tsx index 1412bb9f8..5924af71c 100644 --- a/src/components/Firestore/DocumentListItem.test.tsx +++ b/src/components/Firestore/DocumentListItem.test.tsx @@ -110,7 +110,7 @@ describe('DocumentListItem', () => { const history = createMemoryHistory({ initialEntries: ['/firestore/data'], }); - const { debug, queryByText, queryByTestId } = await render( + const { queryByText, queryByTestId } = await render( { fireEvent.submit(getByText('Save')); }); - expect(await findByText(/\"new\":\"42\"/)).not.toBeNull(); + expect(await findByText(/"new":"42"/)).not.toBeNull(); }); it('renders a field', () => { @@ -97,7 +97,7 @@ describe('loaded document', () => { fireEvent.submit(getByText('Save')); - expect(await findByText(/\"foo\":\"new\"/)).not.toBeNull(); + expect(await findByText(/"foo":"new"/)).not.toBeNull(); }); // TODO: these are definitely immutable, but cannot figure out how to trigger @@ -164,7 +164,7 @@ describe('missing document', () => { await new Promise((resolve) => setTimeout(resolve, 500)); - expect(await findByText(/\"meaningOfLife\":\"42\"/)).not.toBeNull(); + expect(await findByText(/"meaningOfLife":"42"/)).not.toBeNull(); }); }); // missing document @@ -212,7 +212,7 @@ describe('empty document', () => { await waitFor(() => !queryByText('Save')); - expect(await findByText(/\"meaningOfLife\":\"42\"/)).not.toBeNull(); + expect(await findByText(/"meaningOfLife":"42"/)).not.toBeNull(); }); }); // empty document @@ -265,11 +265,11 @@ describe('loaded array', () => { queryAllByText('delete')[1].click(); }); - expect(await findByText(/\"foo\":\[\"bravo\",\"bravo\"\]/)).not.toBeNull(); + expect(await findByText(/"foo":\["bravo","bravo"\]/)).not.toBeNull(); }); it('array elements keys are immutable', async () => { - const { getByLabelText, getByText, queryAllByText } = result; + const { getByLabelText, queryAllByText } = result; // update the bravo-element await act(async () => queryAllByText('edit')[1].click()); expect((getByLabelText('Index') as HTMLInputElement).disabled).toBe(true); @@ -285,9 +285,7 @@ describe('loaded array', () => { target: { value: 'new' }, }); fireEvent.submit(getByText('Save')); - expect( - await findByText(/\"foo\":\[\"alpha\",\"new\",\"bravo\"\]/) - ).not.toBeNull(); + expect(await findByText(/"foo":\["alpha","new","bravo"\]/)).not.toBeNull(); }); it('adds a top-level array element', async () => { @@ -304,7 +302,7 @@ describe('loaded array', () => { }); fireEvent.submit(getByText('Save')); expect( - await findByText(/\"foo\":\[\"alpha\",\"bravo\",\"bravo\",\"new\"\]/) + await findByText(/"foo":\["alpha","bravo","bravo","new"\]/) ).not.toBeNull(); }); }); @@ -375,7 +373,7 @@ describe('loaded map', () => { target: { value: 'new' }, }); fireEvent.submit(getByText('Save')); - expect(await findByText(/\"first_name\":\"new\"/)).not.toBeNull(); + expect(await findByText(/"first_name":"new"/)).not.toBeNull(); }); it('adds a map element', async () => { @@ -392,6 +390,6 @@ describe('loaded map', () => { target: { value: 'new' }, }); fireEvent.submit(getByText('Save')); - expect(await findByText(/\"wow\":\"new\"/)).not.toBeNull(); + expect(await findByText(/"wow":"new"/)).not.toBeNull(); }); }); diff --git a/src/components/Firestore/FirestoreEmulatedApiProvider.tsx b/src/components/Firestore/FirestoreEmulatedApiProvider.tsx index eb6ceb835..36b6e0ceb 100644 --- a/src/components/Firestore/FirestoreEmulatedApiProvider.tsx +++ b/src/components/Firestore/FirestoreEmulatedApiProvider.tsx @@ -18,12 +18,11 @@ import { FirebaseApp } from 'firebase/app'; import { CollectionReference, DocumentReference, - Firestore, collection, connectFirestoreEmulator, getFirestore, } from 'firebase/firestore'; -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback } from 'react'; import { FirebaseAppProvider, FirestoreProvider, @@ -37,10 +36,6 @@ import { useConfig, useEmulatorConfig } from '../common/EmulatorConfigProvider'; import { useFetcher, useRequest } from '../common/useRequest'; import { MissingDocument } from './models'; -interface WindowWithFirestore extends Window { - firestore?: Firestore; -} - const FIRESTORE_OPTIONS = {}; /** diff --git a/src/components/Firestore/dialogs/AddCollectionDialog.test.tsx b/src/components/Firestore/dialogs/AddCollectionDialog.test.tsx index a9aeca1ec..04fa8b792 100644 --- a/src/components/Firestore/dialogs/AddCollectionDialog.test.tsx +++ b/src/components/Firestore/dialogs/AddCollectionDialog.test.tsx @@ -33,6 +33,7 @@ it('shows correct title', async () => { )); await waitFor(() => getByText(/Start a collection/)); + expect(true).toBe(true); }); describe('step 1', () => { @@ -119,7 +120,7 @@ describe('step 2', () => { // but triggering a click event still triggers the underlying event. This is no // reproducible in the actual GUI. it.skip('[Save] is disabled if invalid doc-data', async () => { - const { getByLabelText, getByText } = result; + const { getByText } = result; await act(async () => { getByText('Save').click(); diff --git a/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx b/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx index c9522ed20..794cbbad4 100644 --- a/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx +++ b/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx @@ -96,29 +96,19 @@ it('provides a document-editor', async () => { expect(getByLabelText('Field')).not.toBe(null); }); -// TODO testing suggests that the button is infact disabed by inspecting the DOM -// but triggering a click event still triggers the underlying event. This is no -// reproducible in the actual GUI. -it.skip('[Save] is disabled with invalid doc-data', async () => { +it('[Save] is disabled with invalid doc-data', async () => { const onValue = jest.fn(); - const { getByText, getByLabelText } = await renderDialogWithFirestore( - async (firestore) => { - const collectionRef = collection(firestore, 'things'); - return ( - - ); - } - ); - - await act(async () => { - getByText('Save').click(); + const { getByText } = await renderDialogWithFirestore(async (firestore) => { + const collectionRef = collection(firestore, 'things'); + return ( + + ); }); - - expect(onValue).not.toHaveBeenCalled(); + expect((getByText('Save') as HTMLButtonElement).disabled).toBe(true); }); it('emits id and parsed data when [Save] is clicked', async () => { diff --git a/src/components/Firestore/testing/FirestoreTestProviders.tsx b/src/components/Firestore/testing/FirestoreTestProviders.tsx index 9f3b4daad..ed80c3f17 100644 --- a/src/components/Firestore/testing/FirestoreTestProviders.tsx +++ b/src/components/Firestore/testing/FirestoreTestProviders.tsx @@ -105,7 +105,7 @@ const AsyncFirestore: React.FC< r(firestore) .then((c) => setFirestoreChildren(c)) .catch(onError); - }, [r, firestore, setFirestoreChildren]); + }, [r, firestore, setFirestoreChildren, onError]); return firestoreChildren ? (
{firestoreChildren}
diff --git a/src/components/Home/index.test.tsx b/src/components/Home/index.test.tsx index b9a456caf..6d67c1513 100644 --- a/src/components/Home/index.test.tsx +++ b/src/components/Home/index.test.tsx @@ -19,7 +19,6 @@ import React from 'react'; import { MemoryRouter } from 'react-router'; import { - EmulatorConfigProvider, TestEmulatorConfigProvider, } from '../common/EmulatorConfigProvider'; import { Home } from './index'; diff --git a/src/components/Storage/Card/StorageCard.test.tsx b/src/components/Storage/Card/StorageCard.test.tsx index 927651002..beab250e6 100644 --- a/src/components/Storage/Card/StorageCard.test.tsx +++ b/src/components/Storage/Card/StorageCard.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { act, fireEvent } from '@testing-library/react'; +import { fireEvent } from '@testing-library/react'; import React from 'react'; import { mockTokens } from '../testing/mockTokens'; diff --git a/src/components/Storage/providers/StorageFirebaseAppProvider.tsx b/src/components/Storage/providers/StorageFirebaseAppProvider.tsx index be505f8b0..ba0603693 100644 --- a/src/components/Storage/providers/StorageFirebaseAppProvider.tsx +++ b/src/components/Storage/providers/StorageFirebaseAppProvider.tsx @@ -48,7 +48,7 @@ export const StorageFirebaseAppProvider: React.FC< }); setStorage(storage); }, - [host, port] + [host, port, bucket] ) ); diff --git a/src/components/Storage/testing/renderWithStorage.tsx b/src/components/Storage/testing/renderWithStorage.tsx index 9f1b4fce4..dc9fb9e03 100644 --- a/src/components/Storage/testing/renderWithStorage.tsx +++ b/src/components/Storage/testing/renderWithStorage.tsx @@ -14,20 +14,15 @@ * limitations under the License. */ -import { randomUUID } from 'crypto'; - import { randomId } from '@rmwc/base'; import { RenderResult, act, render, waitFor } from '@testing-library/react'; -import { waitForElementToBeRemoved } from '@testing-library/react'; -import { Database, enableLogging, ref, set } from 'firebase/database'; import { FirebaseStorage } from 'firebase/storage'; import { History } from 'history'; -import { uniqueId } from 'lodash'; import React, { ReactElement, useEffect, useState } from 'react'; import { Suspense } from 'react'; import { useHistory } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom'; -import { useDatabase, useStorage } from 'reactfire'; +import { useStorage } from 'reactfire'; import { makeDeferred } from '../../../test_utils'; import { TestEmulatorConfigProvider } from '../../common/EmulatorConfigProvider'; @@ -117,7 +112,7 @@ const AsyncStorage: React.FC< r(storage) .then((c) => setStorageChildren(c)) .catch(onError); - }, [r, storage, setStorageChildren]); + }, [r, storage, setStorageChildren, onError]); return storageChildren ? (
{storageChildren}
@@ -170,7 +165,7 @@ export async function renderWithStorage(children: ReactElement) { .catch((e) => { console.error('Unable to clear all storage files', { e }); }); - }, []); + }, [children]); return storageChildren ? (
{storageChildren}
diff --git a/src/components/common/useMainDetail/useMainDetail.test.tsx b/src/components/common/useMainDetail/useMainDetail.test.tsx index 771d0e186..4ba34afc4 100644 --- a/src/components/common/useMainDetail/useMainDetail.test.tsx +++ b/src/components/common/useMainDetail/useMainDetail.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { act, getByLabelText, render } from '@testing-library/react'; +import { act, render } from '@testing-library/react'; import React from 'react'; import { useMainDetail } from './useMainDetail'; From f30c8ec718e5f1b66f051f18f730e74fb33bab46 Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Fri, 2 Sep 2022 14:34:58 -0700 Subject: [PATCH 3/5] Fix glob pattern and more things. --- package.json | 4 ++-- src/components/Database/DataViewer/common/view_model.ts | 2 +- src/components/Extensions/testing/utils.ts | 1 + src/firebase.ts | 9 ++------- src/setupTests.js | 1 - vite.config.ts | 2 +- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 8cb6e06bd..3ecd680a1 100644 --- a/package.json +++ b/package.json @@ -87,8 +87,8 @@ "test:debug": "react-scripts --inspect-brk test --env=./custom-jest-environment.js --runInBand", "test:coverage": "react-scripts test --coverage --watchAll=false --env=./custom-jest-environment.js", "eject": "react-scripts eject", - "lint": "prettier --check . && eslint 'src/**/*.{ts,tsx}'", - "lint:fix": "eslint --fix 'src/**/*.{ts,tsx}' && npm run format", + "lint": "prettier --check . && eslint --ext .js,.jsx,.ts,.tsx src/", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx src/ && npm run format", "fmt": "npm run format", "format": "prettier --write .", "prepare": "husky install" diff --git a/src/components/Database/DataViewer/common/view_model.ts b/src/components/Database/DataViewer/common/view_model.ts index d868f8bcd..6642275de 100644 --- a/src/components/Database/DataViewer/common/view_model.ts +++ b/src/components/Database/DataViewer/common/view_model.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Database, DatabaseReference, Query } from 'firebase/database'; +import { DatabaseReference, Query } from 'firebase/database'; /** * Max nodes shown underneath each diff --git a/src/components/Extensions/testing/utils.ts b/src/components/Extensions/testing/utils.ts index 31ad2c6c8..16ed240b8 100644 --- a/src/components/Extensions/testing/utils.ts +++ b/src/components/Extensions/testing/utils.ts @@ -49,6 +49,7 @@ export const EXTENSION_SPEC: ExtensionSpec = { 'Listens for new images uploaded to your specified Cloud Storage bucket, resizes the images, then stores the resized images in the same bucket. Optionally keeps or deletes the original images.', name: 'generateResizedImage', propertiesYaml: + // eslint-disable-next-line no-template-curly-in-string 'availableMemoryMb: 1024\neventTrigger:\n eventType: google.storage.object.finalize\n resource: projects/_/buckets/${param:IMG_BUCKET}\nlocation: ${param:LOCATION}\nruntime: nodejs14\n', }, ], diff --git a/src/firebase.ts b/src/firebase.ts index 6fe9305b3..936ba7289 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -15,16 +15,11 @@ */ import { FirebaseApp, deleteApp, initializeApp } from 'firebase/app'; -import { - Database, - connectDatabaseEmulator, - getDatabase, -} from 'firebase/database'; -import { Firestore } from 'firebase/firestore'; + + import { useEffect, useState } from 'react'; import { useConfig } from './components/common/EmulatorConfigProvider'; -import { DatabaseConfig } from './store/config'; /** * Get a JS SDK App instance with emulator Admin auth enabled. diff --git a/src/setupTests.js b/src/setupTests.js index cd1213bb2..022320851 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -14,7 +14,6 @@ * limitations under the License. */ -import * as base from '@rmwc/base'; require('mutationobserver-shim'); diff --git a/vite.config.ts b/vite.config.ts index 8248b69e3..50d786414 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -42,7 +42,7 @@ export default defineConfig(({ command, mode }) => { viteCommonjs(), checker({ typescript: true, - eslint: { lintCommand: 'eslint "src/**/*.{js,ts,tsx}"' }, + eslint: { lintCommand: 'eslint --ext .js,.jsx,.ts,.tsx src/' }, }), // Fix Vite routing paths with dots (used in Storage/Firestore/etc.): From b5e83ea478d99eba75525c3bff2322e8009e5c2f Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Fri, 2 Sep 2022 14:38:19 -0700 Subject: [PATCH 4/5] Fix prettier formatting too. --- .../Firestore/CollectionFilter/CollectionFilter.test.tsx | 7 +------ src/components/Home/index.test.tsx | 4 +--- src/firebase.ts | 2 -- src/setupTests.js | 1 - 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx b/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx index 8c1bb8340..19af0dd05 100644 --- a/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx +++ b/src/components/Firestore/CollectionFilter/CollectionFilter.test.tsx @@ -14,12 +14,7 @@ * limitations under the License. */ -import { - act, - fireEvent, - render, - waitFor, -} from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; import { delay } from '../../../test_utils'; diff --git a/src/components/Home/index.test.tsx b/src/components/Home/index.test.tsx index 6d67c1513..3cd692356 100644 --- a/src/components/Home/index.test.tsx +++ b/src/components/Home/index.test.tsx @@ -18,9 +18,7 @@ import { getByRole, getByText, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router'; -import { - TestEmulatorConfigProvider, -} from '../common/EmulatorConfigProvider'; +import { TestEmulatorConfigProvider } from '../common/EmulatorConfigProvider'; import { Home } from './index'; it('renders fetching placeholder when fetching config', () => { diff --git a/src/firebase.ts b/src/firebase.ts index 936ba7289..b099df021 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -15,8 +15,6 @@ */ import { FirebaseApp, deleteApp, initializeApp } from 'firebase/app'; - - import { useEffect, useState } from 'react'; import { useConfig } from './components/common/EmulatorConfigProvider'; diff --git a/src/setupTests.js b/src/setupTests.js index 022320851..556ad2a8f 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -14,7 +14,6 @@ * limitations under the License. */ - require('mutationobserver-shim'); // reactFire has an implicit dependency on globalThis From 522759cbbee451e0a84c0627b0bf873c4a9b3c0c Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Fri, 2 Sep 2022 14:41:35 -0700 Subject: [PATCH 5/5] Fix disabled button test. --- src/components/Firestore/dialogs/AddDocumentDialog.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx b/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx index 794cbbad4..dce7950d9 100644 --- a/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx +++ b/src/components/Firestore/dialogs/AddDocumentDialog.test.tsx @@ -108,7 +108,10 @@ it('[Save] is disabled with invalid doc-data', async () => { /> ); }); - expect((getByText('Save') as HTMLButtonElement).disabled).toBe(true); + + const saveButton = getByText('Save').closest('button'); + expect(saveButton).not.toBeNull(); + expect(saveButton!.disabled).toBe(true); }); it('emits id and parsed data when [Save] is clicked', async () => {