diff --git a/.eslintrc.js b/.eslintrc.js index a4bf0f7395..07e3e54e0e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -72,5 +72,14 @@ module.exports = { }], }], }, + }, { + files: [ + "test/**/*.{ts,tsx}", + ], + rules: { + // We don't need super strict typing in test utilities + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-member-accessibility": "off", + }, }], }; diff --git a/module_system/installer.ts b/module_system/installer.ts index 021550fdee..705a2f98ae 100644 --- a/module_system/installer.ts +++ b/module_system/installer.ts @@ -123,12 +123,12 @@ function readCurrentPackageDetails(): RawDependencies { }; } -function writePackageDetails(deps: RawDependencies) { +function writePackageDetails(deps: RawDependencies): void { fs.writeFileSync("./yarn.lock", deps.lockfile, "utf-8"); fs.writeFileSync("./package.json", deps.packageJson, "utf-8"); } -function callYarnAdd(dep: string) { +function callYarnAdd(dep: string): void { // Add the module to the optional dependencies section just in case something // goes wrong in restoring the original package details. childProcess.execSync(`yarn add -O ${dep}`, { @@ -186,6 +186,6 @@ function isModuleVersionCompatible(ourApiVersion: string, moduleApiVersion: stri return semver.satisfies(ourApiVersion, moduleApiVersion); } -function writeModulesTs(content: string) { +function writeModulesTs(content: string): void { fs.writeFileSync("./src/modules.ts", content, "utf-8"); } diff --git a/package.json b/package.json index 676fd3b281..ac5319b7f9 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-deprecate": "^0.7.0", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-matrix-org": "^0.7.0", + "eslint-plugin-matrix-org": "^0.8.0", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-unicorn": "^44.0.2", diff --git a/src/components/views/auth/VectorAuthFooter.tsx b/src/components/views/auth/VectorAuthFooter.tsx index c97718bda4..82ba659d4c 100644 --- a/src/components/views/auth/VectorAuthFooter.tsx +++ b/src/components/views/auth/VectorAuthFooter.tsx @@ -15,11 +15,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { ReactElement } from 'react'; import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; import { _t } from 'matrix-react-sdk/src/languageHandler'; -const VectorAuthFooter = () => { +const VectorAuthFooter = (): ReactElement => { const brandingConfig = SdkConfig.getObject("branding"); const links = brandingConfig?.get("auth_footer_links") ?? [ { "text": "Blog", "url": "https://element.io/blog" }, diff --git a/src/components/views/auth/VectorAuthHeaderLogo.tsx b/src/components/views/auth/VectorAuthHeaderLogo.tsx index eaa60cf7ee..2e7cce5756 100644 --- a/src/components/views/auth/VectorAuthHeaderLogo.tsx +++ b/src/components/views/auth/VectorAuthHeaderLogo.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; export default class VectorAuthHeaderLogo extends React.PureComponent { - public render() { + public render(): React.ReactElement { const brandingConfig = SdkConfig.getObject("branding"); const logoUrl = brandingConfig?.get("auth_header_logo_url") ?? "themes/element/img/logos/element-logo.svg"; diff --git a/src/components/views/auth/VectorAuthPage.tsx b/src/components/views/auth/VectorAuthPage.tsx index 6b0004cf1b..b06aade8fb 100644 --- a/src/components/views/auth/VectorAuthPage.tsx +++ b/src/components/views/auth/VectorAuthPage.tsx @@ -23,7 +23,7 @@ export default class VectorAuthPage extends React.PureComponent { private static welcomeBackgroundUrl; // cache the url as a static to prevent it changing without refreshing - private static getWelcomeBackgroundUrl() { + private static getWelcomeBackgroundUrl(): string { if (VectorAuthPage.welcomeBackgroundUrl) return VectorAuthPage.welcomeBackgroundUrl; const brandingConfig = SdkConfig.getObject("branding"); @@ -42,7 +42,7 @@ export default class VectorAuthPage extends React.PureComponent { return VectorAuthPage.welcomeBackgroundUrl; } - public render() { + public render(): React.ReactElement { const pageStyle = { background: `center/cover fixed url(${VectorAuthPage.getWelcomeBackgroundUrl()})`, }; diff --git a/src/favicon.ts b/src/favicon.ts index 7488cac299..6899bbebb4 100644 --- a/src/favicon.ts +++ b/src/favicon.ts @@ -56,7 +56,7 @@ export default class Favicon { // callback to run once isReady is asserted, allows for a badge to be queued for when it can be shown private readyCb?: () => void; - constructor(params: Partial = {}) { + public constructor(params: Partial = {}) { this.params = { ...defaults, ...params }; this.icons = Favicon.getIcons(); @@ -68,7 +68,7 @@ export default class Favicon { const lastIcon = this.icons[this.icons.length - 1]; if (lastIcon.hasAttribute("href")) { this.baseImage.setAttribute("crossOrigin", "anonymous"); - this.baseImage.onload = () => { + this.baseImage.onload = (): void => { // get height and width of the favicon this.canvas.height = (this.baseImage.height > 0) ? this.baseImage.height : 32; this.canvas.width = (this.baseImage.width > 0) ? this.baseImage.width : 32; @@ -217,7 +217,7 @@ export default class Favicon { public badge(content: number | string, opts?: Partial): void { if (!this.isReady) { - this.readyCb = () => { + this.readyCb = (): void => { this.badge(content, opts); }; return; diff --git a/src/vector/app.tsx b/src/vector/app.tsx index b63e22a891..0ae5bd3040 100644 --- a/src/vector/app.tsx +++ b/src/vector/app.tsx @@ -21,7 +21,7 @@ limitations under the License. // To ensure we load the browser-matrix version first import "matrix-js-sdk/src/browser-index"; -import React from 'react'; +import React, { ReactElement } from 'react'; import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; import { _td, newTranslatableError } from 'matrix-react-sdk/src/languageHandler'; import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils'; @@ -55,7 +55,7 @@ window.matrixLogger = logger; // If we're in electron, we should never pass through a file:// URL otherwise // the identity server will try to 302 the browser to it, which breaks horribly. // so in that instance, hardcode to use app.element.io for now instead. -function makeRegistrationUrl(params: object) { +function makeRegistrationUrl(params: object): string { let url; if (window.location.protocol === "vector:") { url = 'https://app.element.io/#/register'; @@ -81,7 +81,7 @@ function makeRegistrationUrl(params: object) { return url; } -function onTokenLoginCompleted() { +function onTokenLoginCompleted(): void { // if we did a token login, we're now left with the token, hs and is // url as query params in the url; a little nasty but let's redirect to // clear them. @@ -93,7 +93,7 @@ function onTokenLoginCompleted() { window.history.replaceState(null, "", url.href); } -export async function loadApp(fragParams: {}) { +export async function loadApp(fragParams: {}): Promise { initRouting(); const platform = PlatformPeg.get(); diff --git a/src/vector/index.ts b/src/vector/index.ts index dd182119d1..067121e939 100644 --- a/src/vector/index.ts +++ b/src/vector/index.ts @@ -40,7 +40,7 @@ require('katex/dist/katex.css'); require('./devcss'); require('./localstorage-fix'); -async function settled(...promises: Array>) { +async function settled(...promises: Array>): Promise { for (const prom of promises) { try { await prom; @@ -50,7 +50,7 @@ async function settled(...promises: Array>) { } } -function checkBrowserFeatures() { +function checkBrowserFeatures(): boolean { if (!window.Modernizr) { logger.error("Cannot check features - Modernizr global is missing."); return false; @@ -102,7 +102,7 @@ const supportedBrowser = checkBrowserFeatures(); // We start loading stuff but don't block on it until as late as possible to allow // the browser to use as much parallelism as it can. // Load parallelism is based on research in https://github.com/vector-im/element-web/issues/12253 -async function start() { +async function start(): Promise { // load init.ts async so that its code is not executed immediately and we can catch any exceptions const { rageshakePromise, diff --git a/src/vector/init.tsx b/src/vector/init.tsx index d8e79dde20..133ebefdf6 100644 --- a/src/vector/init.tsx +++ b/src/vector/init.tsx @@ -41,7 +41,7 @@ import { INSTALLED_MODULES } from "../modules"; export const rageshakePromise = initRageshake(); -export function preparePlatform() { +export function preparePlatform(): void { if (window.electron) { logger.log("Using Electron platform"); PlatformPeg.set(new ElectronPlatform()); @@ -54,7 +54,7 @@ export function preparePlatform() { } } -export function setupLogStorage() { +export function setupLogStorage(): Promise { if (SdkConfig.get().bug_report_endpoint_url) { return initRageshakeStore(); } @@ -62,7 +62,7 @@ export function setupLogStorage() { return Promise.resolve(); } -export async function loadConfig() { +export async function loadConfig(): Promise { // XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure // granular settings are loaded correctly and to avoid duplicating the override logic for the theme. // @@ -112,7 +112,7 @@ export function loadOlm(): Promise { }); } -export async function loadLanguage() { +export async function loadLanguage(): Promise { const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true); let langs = []; @@ -131,11 +131,11 @@ export async function loadLanguage() { } } -export async function loadTheme() { +export async function loadTheme(): Promise { setTheme(); } -export async function loadApp(fragParams: {}) { +export async function loadApp(fragParams: {}): Promise { // load app.js async so that its code is not executed immediately and we can catch any exceptions const module = await import( /* webpackChunkName: "element-web-app" */ @@ -145,7 +145,7 @@ export async function loadApp(fragParams: {}) { document.getElementById('matrixchat')); } -export async function showError(title: string, messages?: string[]) { +export async function showError(title: string, messages?: string[]): Promise { const ErrorView = (await import( /* webpackChunkName: "error-view" */ "../async-components/structures/ErrorView")).default; @@ -153,7 +153,7 @@ export async function showError(title: string, messages?: string[]) { document.getElementById('matrixchat')); } -export async function showIncompatibleBrowser(onAccept) { +export async function showIncompatibleBrowser(onAccept): Promise { const CompatibilityView = (await import( /* webpackChunkName: "compatibility-view" */ "../async-components/structures/CompatibilityView")).default; @@ -161,7 +161,7 @@ export async function showIncompatibleBrowser(onAccept) { document.getElementById('matrixchat')); } -export async function loadModules() { +export async function loadModules(): Promise { for (const InstalledModule of INSTALLED_MODULES) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - we know the constructor exists even if TypeScript can't be convinced of that diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 4eda316eec..81a9735187 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -61,7 +61,7 @@ let widgetApi: WidgetApi; let meetApi: any; // JitsiMeetExternalAPI let skipOurWelcomeScreen = false; -const setupCompleted = (async () => { +const setupCompleted = (async (): Promise => { try { // Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with // other setup work and therefore do not block. @@ -223,11 +223,11 @@ const setupCompleted = (async () => { } })(); -function enableJoinButton() { - document.getElementById("joinButton").onclick = () => joinConference(); +function enableJoinButton(): void { + document.getElementById("joinButton").onclick = (): void => joinConference(); } -function switchVisibleContainers() { +function switchVisibleContainers(): void { inConference = !inConference; // Our welcome screen is managed by other code, so just don't switch to it ever @@ -237,14 +237,14 @@ function switchVisibleContainers() { } } -function toggleConferenceVisibility(inConference: boolean) { +function toggleConferenceVisibility(inConference: boolean): void { document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden'; // Video rooms have a separate UI for joining, so they should never show our join button document.getElementById("joinButtonContainer").style.visibility = (inConference || isVideoChannel) ? 'hidden' : 'unset'; } -function skipToJitsiSplashScreen() { +function skipToJitsiSplashScreen(): void { // really just a function alias for self-documenting code joinConference(); } @@ -254,7 +254,7 @@ function skipToJitsiSplashScreen() { * * See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification */ -function createJWTToken() { +function createJWTToken(): string { // Header const header = { alg: 'HS256', typ: 'JWT' }; // Payload @@ -289,7 +289,7 @@ function createJWTToken() { ); } -async function notifyHangup(errorMessage?: string) { +async function notifyHangup(errorMessage?: string): Promise { if (widgetApi) { // We send the hangup event before setAlwaysOnScreen, because the latter // can cause the receiving side to instantly stop listening. @@ -301,7 +301,7 @@ async function notifyHangup(errorMessage?: string) { } } -function closeConference() { +function closeConference(): void { switchVisibleContainers(); document.getElementById("jitsiContainer").innerHTML = ""; @@ -315,7 +315,7 @@ function closeConference() { // audio input it can find, while an input of null instructs it to start muted, // and a non-nullish input specifies the label of a specific device to use. // Same for video inputs. -function joinConference(audioInput?: string | null, videoInput?: string | null) { +function joinConference(audioInput?: string | null, videoInput?: string | null): void { let jwt; if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { if (!openIdToken?.access_token) { // eslint-disable-line camelcase @@ -408,7 +408,7 @@ function joinConference(audioInput?: string | null, videoInput?: string | null) meetApi.on("log", onLog); } -const onVideoConferenceJoined = () => { +const onVideoConferenceJoined = (): void => { // Although we set our displayName with the userInfo option above, that // option has a bug where it causes the name to be the HTML encoding of // what was actually intended. So, we use the displayName command to at @@ -432,12 +432,12 @@ const onVideoConferenceJoined = () => { if (isVideoChannel) meetApi.executeCommand("setTileView", true); }; -const onVideoConferenceLeft = () => { +const onVideoConferenceLeft = (): void => { notifyHangup(); meetApi = null; }; -const onErrorOccurred = ({ error }) => { +const onErrorOccurred = ({ error }): void => { if (error.isFatal) { // We got disconnected. Since Jitsi Meet might send us back to the // prejoin screen, we're forced to act as if we hung up entirely. @@ -447,12 +447,12 @@ const onErrorOccurred = ({ error }) => { } }; -const onAudioMuteStatusChanged = ({ muted }) => { +const onAudioMuteStatusChanged = ({ muted }): void => { const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio; widgetApi?.transport.send(action, {}); }; -const onVideoMuteStatusChanged = ({ muted }) => { +const onVideoMuteStatusChanged = ({ muted }): void => { if (muted) { // Jitsi Meet always sends a "video muted" event directly before // hanging up, which we need to ignore by padding the timeout here, @@ -466,11 +466,11 @@ const onVideoMuteStatusChanged = ({ muted }) => { } }; -const updateParticipants = () => { +const updateParticipants = (): void => { widgetApi?.transport.send(ElementWidgetActions.CallParticipants, { participants: meetApi.getParticipantsInfo(), }); }; -const onLog = ({ logLevel, args }) => +const onLog = ({ logLevel, args }): void => (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args); diff --git a/src/vector/platform/ElectronPlatform.tsx b/src/vector/platform/ElectronPlatform.tsx index 635660d43f..4b1a71e36e 100644 --- a/src/vector/platform/ElectronPlatform.tsx +++ b/src/vector/platform/ElectronPlatform.tsx @@ -92,7 +92,7 @@ export default class ElectronPlatform extends VectorBasePlatform { // this is the opaque token we pass to the HS which when we get it in our callback we can resolve to a profile private readonly ssoID: string = randomString(32); - constructor() { + public constructor() { super(); dis.register(onAction); @@ -124,12 +124,12 @@ export default class ElectronPlatform extends VectorBasePlatform { window.electron.on('userDownloadCompleted', (ev, { id, name }) => { const key = `DOWNLOAD_TOAST_${id}`; - const onAccept = () => { + const onAccept = (): void => { window.electron.send('userDownloadAction', { id, open: true }); ToastStore.sharedInstance().dismissToast(key); }; - const onDismiss = () => { + const onDismiss = (): void => { window.electron.send('userDownloadAction', { id }); }; @@ -156,7 +156,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return this.ipc.call('getConfig'); } - private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }) => { + private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }): Promise => { dis.dispatch({ action: Action.CheckUpdates, status: UpdateCheckStatus.Ready, @@ -223,7 +223,7 @@ export default class ElectronPlatform extends VectorBasePlatform { ); const handler = notification.onclick as Function; - notification.onclick = () => { + notification.onclick = (): void => { handler?.(); this.ipc.call('focusWindow'); }; @@ -231,7 +231,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return notification; } - public loudNotification(ev: MatrixEvent, room: Room) { + public loudNotification(ev: MatrixEvent, room: Room): void { window.electron.send('loudNotification'); } @@ -261,17 +261,17 @@ export default class ElectronPlatform extends VectorBasePlatform { return this.ipc.call("setSettingValue", settingName, value); } - async canSelfUpdate(): Promise { + public async canSelfUpdate(): Promise { const feedUrl = await this.ipc.call('getUpdateFeedUrl'); return Boolean(feedUrl); } - public startUpdateCheck() { + public startUpdateCheck(): void { super.startUpdateCheck(); window.electron.send('check_updates'); } - public installUpdate() { + public installUpdate(): void { // IPC to the main process to install the update, since quitAndInstall // doesn't fire the before-quit event so the main process needs to know // it should exit. @@ -290,7 +290,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return Promise.resolve('granted'); } - public reload() { + public reload(): void { window.location.reload(); } @@ -298,7 +298,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return this.eventIndexManager; } - public async setLanguage(preferredLangs: string[]) { + public async setLanguage(preferredLangs: string[]): Promise { return this.ipc.call('setLanguage', preferredLangs); } @@ -353,7 +353,7 @@ export default class ElectronPlatform extends VectorBasePlatform { loginType: "sso" | "cas", fragmentAfterLogin: string, idpId?: string, - ) { + ): void { // this will get intercepted by electron-main will-navigate super.startSingleSignOn(mxClient, loginType, fragmentAfterLogin, idpId); Modal.createDialog(InfoDialog, { diff --git a/src/vector/platform/VectorBasePlatform.ts b/src/vector/platform/VectorBasePlatform.ts index cca39ea45e..516c76e3ab 100644 --- a/src/vector/platform/VectorBasePlatform.ts +++ b/src/vector/platform/VectorBasePlatform.ts @@ -43,7 +43,7 @@ export default abstract class VectorBasePlatform extends BasePlatform { * it uses canvas, which can trigger a permission prompt in Firefox's resist fingerprinting mode. * See https://github.com/vector-im/element-web/issues/9605. */ - public get favicon() { + public get favicon(): Favicon { if (this._favicon) { return this._favicon; } @@ -51,7 +51,7 @@ export default abstract class VectorBasePlatform extends BasePlatform { return this._favicon; } - private updateFavicon() { + private updateFavicon(): void { let bgColor = "#d00"; let notif: string | number = this.notificationCount; @@ -63,13 +63,13 @@ export default abstract class VectorBasePlatform extends BasePlatform { this.favicon.badge(notif, { bgColor }); } - public setNotificationCount(count: number) { + public setNotificationCount(count: number): void { if (this.notificationCount === count) return; super.setNotificationCount(count); this.updateFavicon(); } - public setErrorStatus(errorDidOccur: boolean) { + public setErrorStatus(errorDidOccur: boolean): void { if (this.errorDidOccur === errorDidOccur) return; super.setErrorStatus(errorDidOccur); this.updateFavicon(); @@ -78,7 +78,7 @@ export default abstract class VectorBasePlatform extends BasePlatform { /** * Begin update polling, if applicable */ - public startUpdater() { + public startUpdater(): void { } /** diff --git a/src/vector/platform/WebPlatform.ts b/src/vector/platform/WebPlatform.ts index 4a1f8140c4..be5842ec46 100644 --- a/src/vector/platform/WebPlatform.ts +++ b/src/vector/platform/WebPlatform.ts @@ -40,7 +40,7 @@ function getNormalizedAppVersion(version: string): string { } export default class WebPlatform extends VectorBasePlatform { - constructor() { + public constructor() { super(); // Register service worker if available on this platform if ('serviceWorker' in navigator) { diff --git a/src/vector/rageshakesetup.ts b/src/vector/rageshakesetup.ts index cddd7adc98..ce98b423f6 100644 --- a/src/vector/rageshakesetup.ts +++ b/src/vector/rageshakesetup.ts @@ -31,7 +31,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import sendBugReport from "matrix-react-sdk/src/rageshake/submit-rageshake"; import { logger } from "matrix-js-sdk/src/logger"; -export function initRageshake() { +export function initRageshake(): Promise { // we manually check persistence for rageshakes ourselves const prom = rageshake.init(/*setUpPersistence=*/false); prom.then(() => { @@ -52,11 +52,11 @@ export function initRageshake() { return prom; } -export function initRageshakeStore() { +export function initRageshakeStore(): Promise { return rageshake.tryInitStorage(); } -window.mxSendRageshake = function(text: string, withLogs?: boolean) { +window.mxSendRageshake = function(text: string, withLogs?: boolean): void { const url = SdkConfig.get().bug_report_endpoint_url; if (!url) { logger.error("Cannot send a rageshake - no bug_report_endpoint_url configured"); diff --git a/src/vector/routing.ts b/src/vector/routing.ts index d2633cb8e3..3388e9d76a 100644 --- a/src/vector/routing.ts +++ b/src/vector/routing.ts @@ -17,13 +17,14 @@ limitations under the License. // Parse the given window.location and return parameters that can be used when calling // MatrixChat.showScreen(screen, params) import { logger } from "matrix-js-sdk/src/logger"; +import { QueryDict } from "matrix-js-sdk/src/utils"; import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat"; import { parseQsFromFragment } from "./url_utils"; let lastLocationHashSet: string = null; -export function getScreenFromLocation(location: Location) { +export function getScreenFromLocation(location: Location): { screen: string, params: QueryDict } { const fragparts = parseQsFromFragment(location); return { screen: fragparts.location.substring(1), @@ -33,7 +34,7 @@ export function getScreenFromLocation(location: Location) { // Here, we do some crude URL analysis to allow // deep-linking. -function routeUrl(location: Location) { +function routeUrl(location: Location): void { if (!window.matrixChat) return; logger.log("Routing URL ", location.href); @@ -41,7 +42,7 @@ function routeUrl(location: Location) { (window.matrixChat as MatrixChatType).showScreen(s.screen, s.params); } -function onHashChange() { +function onHashChange(): void { if (decodeURIComponent(window.location.hash) === lastLocationHashSet) { // we just set this: no need to route it! return; @@ -51,7 +52,7 @@ function onHashChange() { // This will be called whenever the SDK changes screens, // so a web page can update the URL bar appropriately. -export function onNewScreen(screen: string, replaceLast = false) { +export function onNewScreen(screen: string, replaceLast = false): void { logger.log("newscreen " + screen); const hash = '#/' + screen; lastLocationHashSet = hash; @@ -71,6 +72,6 @@ export function onNewScreen(screen: string, replaceLast = false) { } } -export function init() { +export function init(): void { window.addEventListener('hashchange', onHashChange); } diff --git a/src/vector/url_utils.ts b/src/vector/url_utils.ts index 98c6e39366..718fc15273 100644 --- a/src/vector/url_utils.ts +++ b/src/vector/url_utils.ts @@ -19,8 +19,7 @@ import { QueryDict, decodeParams } from "matrix-js-sdk/src/utils"; // We want to support some name / value pairs in the fragment // so we're re-using query string like format // -// returns {location, params} -export function parseQsFromFragment(location: Location) { +export function parseQsFromFragment(location: Location): { location: string, params: QueryDict } { // if we have a fragment, it will start with '#', which we need to drop. // (if we don't, this will return ''). const fragment = location.hash.substring(1); diff --git a/test/app-tests/loading-test.tsx b/test/app-tests/loading-test.tsx index d9b9008387..59a5a84cb4 100644 --- a/test/app-tests/loading-test.tsx +++ b/test/app-tests/loading-test.tsx @@ -28,7 +28,7 @@ import MockHttpBackend from 'matrix-mock-request'; import { makeType } from "matrix-react-sdk/src/utils/TypeUtils"; import { ValidatedServerConfig } from 'matrix-react-sdk/src/utils/ValidatedServerConfig'; import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; -import { sleep } from "matrix-js-sdk/src/utils"; +import { QueryDict, sleep } from "matrix-js-sdk/src/utils"; import "../jest-mocks"; import WebPlatform from '../../src/vector/platform/WebPlatform'; @@ -84,7 +84,7 @@ describe('loading:', function() { * TODO: it would be nice to factor some of this stuff out of index.js so * that we can test it rather than our own implementation of it. */ - function loadApp(opts?) { + function loadApp(opts?): void { opts = opts || {}; const queryString = opts.queryString || ""; const uriFragment = opts.uriFragment || ""; @@ -92,10 +92,10 @@ describe('loading:', function() { windowLocation = { search: queryString, hash: uriFragment, - toString: function() { return this.search + this.hash; }, + toString: function(): string { return this.search + this.hash; }, }; - function onNewScreen(screen) { + function onNewScreen(screen): void { console.log(Date.now() + " newscreen "+screen); const hash = '#/' + screen; windowLocation.hash = hash; @@ -104,7 +104,7 @@ describe('loading:', function() { // Parse the given window.location and return parameters that can be used when calling // MatrixChat.showScreen(screen, params) - function getScreenFromLocation(location) { + function getScreenFromLocation(location): { screen: string, params: QueryDict } { const fragparts = parseQsFromFragment(location); return { screen: fragparts.location.substring(1), @@ -143,7 +143,7 @@ describe('loading:', function() { enableGuest={true} onTokenLoginCompleted={resolve} initialScreenAfterLogin={getScreenFromLocation(windowLocation)} - makeRegistrationUrl={() => {throw new Error('Not implemented');}} + makeRegistrationUrl={(): string => {throw new Error('Not implemented');}} />, parentDiv, ); }); @@ -153,7 +153,7 @@ describe('loading:', function() { // http requests until we do. // // returns a promise resolving to the received request - async function expectAndAwaitSync(opts?) { + async function expectAndAwaitSync(opts?): Promise { let syncRequest = null; httpBackend.when('GET', '/_matrix/client/versions') .respond(200, { @@ -548,7 +548,7 @@ describe('loading:', function() { // check that we have a Login component, send a 'user:pass' login, // and await the HTTP requests. - async function completeLogin(matrixChat: RenderResult) { + async function completeLogin(matrixChat: RenderResult): Promise { // When we switch to the login component, it'll hit the login endpoint // for proof of life and to get flows. We'll only give it one option. httpBackend.when('GET', '/login') @@ -587,15 +587,15 @@ describe('loading:', function() { }); // assert that we are on the loading page -async function assertAtLoadingSpinner() { +async function assertAtLoadingSpinner(): Promise { await screen.findByRole("progressbar"); } -async function awaitLoggedIn(matrixChat: RenderResult) { +async function awaitLoggedIn(matrixChat: RenderResult): Promise { if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in return new Promise(resolve => { - const onAction = ({ action }) => { + const onAction = ({ action }): void => { if (action !== "on_logged_in") { return; } @@ -608,19 +608,19 @@ async function awaitLoggedIn(matrixChat: RenderResult) { }); } -async function awaitRoomView(matrixChat: RenderResult) { +async function awaitRoomView(matrixChat: RenderResult): Promise { await waitFor(() => matrixChat.container.querySelector(".mx_RoomView")); } -async function awaitLoginComponent(matrixChat: RenderResult) { +async function awaitLoginComponent(matrixChat: RenderResult): Promise { await waitFor(() => matrixChat.container.querySelector(".mx_AuthPage")); } -async function awaitWelcomeComponent(matrixChat: RenderResult) { +async function awaitWelcomeComponent(matrixChat: RenderResult): Promise { await waitFor(() => matrixChat.container.querySelector(".mx_Welcome")); } -function moveFromWelcomeToLogin(matrixChat: RenderResult) { +function moveFromWelcomeToLogin(matrixChat: RenderResult): Promise { dis.dispatch({ action: 'start_login' }); return awaitLoginComponent(matrixChat); } diff --git a/test/test-utils.ts b/test/test-utils.ts index 2960d4f83c..db10cd5a2b 100644 --- a/test/test-utils.ts +++ b/test/test-utils.ts @@ -29,17 +29,17 @@ export function deleteIndexedDB(dbName: string): Promise { console.log(`${startTime}: Removing indexeddb instance: ${dbName}`); const req = window.indexedDB.deleteDatabase(dbName); - req.onblocked = () => { + req.onblocked = (): void => { console.log(`${Date.now()}: can't yet delete indexeddb ${dbName} because it is open elsewhere`); }; - req.onerror = (ev) => { + req.onerror = (ev): void => { reject(new Error( `${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`, )); }; - req.onsuccess = () => { + req.onsuccess = (): void => { const now = Date.now(); console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now-startTime} ms`); resolve(); diff --git a/yarn.lock b/yarn.lock index b6c5375f0c..4c4c13ac06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5155,10 +5155,10 @@ eslint-plugin-import@^2.25.4: resolve "^1.22.0" tsconfig-paths "^3.14.1" -eslint-plugin-matrix-org@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.7.0.tgz#4b7456b31e30e7575b62c2aada91915478829f88" - integrity sha512-FLmwE4/cRalB7J+J1BBuTccaXvKtRgAoHlbqSCbdsRqhh27xpxEWXe08KlNiET7drEnnz+xMHXdmvW469gch7g== +eslint-plugin-matrix-org@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.8.0.tgz#daa1396900a8cb1c1d88f1a370e45fc32482cd9e" + integrity sha512-/Poz/F8lXYDsmQa29iPSt+kO+Jn7ArvRdq10g0CCk8wbRS0sb2zb6fvd9xL1BgR5UDQL771V0l8X32etvY5yKA== eslint-plugin-react-hooks@^4.3.0: version "4.6.0"