Conform more of the codebase with noImplicitAny and strictNullChecks (#25174

* Conform more of the codebase with `noImplicitAny` and `strictNullChecks`

* Fix tests

* Update src/vector/app.tsx
This commit is contained in:
Michael Telatynski 2023-04-25 09:36:17 +01:00 committed by GitHub
parent a2da1eb79d
commit f5b8bccb65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 265 additions and 211 deletions

View file

@ -28,6 +28,8 @@ import MockHttpBackend from "matrix-mock-request";
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
import { ActionPayload } from "matrix-react-sdk/src/dispatcher/payloads";
import "../jest-mocks";
import WebPlatform from "../../src/vector/platform/WebPlatform";
@ -38,26 +40,21 @@ const DEFAULT_HS_URL = "http://my_server";
const DEFAULT_IS_URL = "http://my_is";
describe("loading:", function () {
let parentDiv;
let httpBackend;
let httpBackend: MockHttpBackend;
// an Object simulating the window.location
let windowLocation: Location | undefined;
// the mounted MatrixChat
let matrixChat: RenderResult<any, any, any> | undefined;
let matrixChat: RenderResult | undefined;
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
let tokenLoginCompletePromise: Promise<void> | undefined;
beforeEach(function () {
httpBackend = new MockHttpBackend();
// @ts-ignore
window.fetch = httpBackend.fetchFn;
parentDiv = document.createElement("div");
// uncomment this to actually add the div to the UI, to help with
// debugging (but slow things down)
// document.body.appendChild(parentDiv);
windowLocation = undefined;
matrixChat = undefined;
@ -80,8 +77,13 @@ 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?): void {
opts = opts || {};
function loadApp(
opts: {
queryString?: string;
uriFragment?: string;
config?: IConfigOptions;
} = {},
): void {
const queryString = opts.queryString || "";
const uriFragment = opts.uriFragment || "";
@ -93,7 +95,7 @@ describe("loading:", function () {
},
} as Location;
function onNewScreen(screen): void {
function onNewScreen(screen: string): void {
console.log(Date.now() + " newscreen " + screen);
const hash = "#/" + screen;
windowLocation!.hash = hash;
@ -102,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): { screen: string; params: QueryDict } {
function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
const fragparts = parseQsFromFragment(location);
return {
screen: fragparts.location.substring(1),
@ -112,22 +114,20 @@ describe("loading:", function () {
const fragParts = parseQsFromFragment(windowLocation);
const config = Object.assign(
{
default_hs_url: DEFAULT_HS_URL,
default_is_url: DEFAULT_IS_URL,
validated_server_config: {
hsUrl: DEFAULT_HS_URL,
hsName: "TEST_ENVIRONMENT",
hsNameIsDifferent: false, // yes, we lie
isUrl: DEFAULT_IS_URL,
} as ValidatedServerConfig,
embeddedPages: {
homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
},
const config = {
default_hs_url: DEFAULT_HS_URL,
default_is_url: DEFAULT_IS_URL,
validated_server_config: {
hsUrl: DEFAULT_HS_URL,
hsName: "TEST_ENVIRONMENT",
hsNameIsDifferent: false, // yes, we lie
isUrl: DEFAULT_IS_URL,
} as ValidatedServerConfig,
embedded_pages: {
home_url: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
},
opts.config || {},
);
...(opts.config ?? {}),
} as IConfigOptions;
PlatformPeg.set(new WebPlatform());
@ -137,18 +137,17 @@ describe("loading:", function () {
matrixChat = render(
<MatrixChat
onNewScreen={onNewScreen}
config={config}
config={config!}
serverConfig={config.validated_server_config}
realQueryParams={params}
startingFragmentQueryParams={fragParts.params}
enableGuest={true}
onTokenLoginCompleted={resolve}
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
initialScreenAfterLogin={getScreenFromLocation(windowLocation!)}
makeRegistrationUrl={(): string => {
throw new Error("Not implemented");
}}
/>,
parentDiv,
);
});
}
@ -157,15 +156,15 @@ describe("loading:", function () {
// http requests until we do.
//
// returns a promise resolving to the received request
async function expectAndAwaitSync(opts?): Promise<any> {
let syncRequest = null;
async function expectAndAwaitSync(opts?: { isGuest?: boolean }): Promise<any> {
let syncRequest: (typeof MockHttpBackend.prototype.requests)[number] | null = null;
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.3.0"],
unstable_features: {
"m.lazy_load_members": true,
},
});
const isGuest = opts && opts.isGuest;
const isGuest = opts?.isGuest;
if (!isGuest) {
// the call to create the LL filter
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
@ -183,7 +182,7 @@ describe("loading:", function () {
if (syncRequest) {
return syncRequest;
}
await httpBackend.flush();
await httpBackend.flush(undefined);
}
throw new Error("Gave up waiting for /sync");
}
@ -201,11 +200,11 @@ describe("loading:", function () {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(403, "Guest access is disabled");
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
// Wait for another trip around the event loop for the UI to update
@ -234,11 +233,11 @@ describe("loading:", function () {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(403, "Guest access is disabled");
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
// Wait for another trip around the event loop for the UI to update
@ -403,14 +402,14 @@ describe("loading:", function () {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
return awaitLoggedIn(matrixChat!);
@ -440,14 +439,14 @@ describe("loading:", function () {
.when("POST", "/register")
.check(function (req) {
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
return awaitLoggedIn(matrixChat!);
@ -480,14 +479,14 @@ describe("loading:", function () {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
return awaitLoggedIn(matrixChat!);
@ -513,7 +512,7 @@ describe("loading:", function () {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
expect(req.queryParams?.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
@ -521,7 +520,7 @@ describe("loading:", function () {
});
return httpBackend
.flush()
.flush(undefined)
.then(() => {
return awaitLoggedIn(matrixChat!);
})
@ -584,7 +583,7 @@ describe("loading:", function () {
access_token: "access_token",
});
return httpBackend.flush();
return httpBackend.flush(undefined);
})
.then(() => {
// at this point, MatrixChat should fire onTokenLoginCompleted, which
@ -607,11 +606,11 @@ 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<any, any, any>): Promise<void> {
async function completeLogin(matrixChat: RenderResult): Promise<void> {
// 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").respond(200, { flows: [{ type: "m.login.password" }] });
httpBackend.flush(); // We already would have tried the GET /login request
httpBackend.flush(undefined); // We already would have tried the GET /login request
// Give the component some time to finish processing the login flows before
// continuing.
@ -635,7 +634,7 @@ describe("loading:", function () {
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
return httpBackend
.flush()
.flush(undefined)
.then(() => {
// Wait for another trip around the event loop for the UI to update
return sleep(1);
@ -656,11 +655,11 @@ async function assertAtLoadingSpinner(): Promise<void> {
await screen.findByRole("progressbar");
}
async function awaitLoggedIn(matrixChat: RenderResult<any, any, any>): Promise<void> {
async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
return new Promise((resolve) => {
const onAction = ({ action }): void => {
const onAction = ({ action }: ActionPayload): void => {
if (action !== "on_logged_in") {
return;
}
@ -673,19 +672,19 @@ async function awaitLoggedIn(matrixChat: RenderResult<any, any, any>): Promise<v
});
}
async function awaitRoomView(matrixChat?: RenderResult<any, any, any>): Promise<void> {
async function awaitRoomView(matrixChat?: RenderResult): Promise<void> {
await waitFor(() => matrixChat?.container.querySelector(".mx_RoomView"));
}
async function awaitLoginComponent(matrixChat?: RenderResult<any, any, any>): Promise<void> {
async function awaitLoginComponent(matrixChat?: RenderResult): Promise<void> {
await waitFor(() => matrixChat?.container.querySelector(".mx_AuthPage"));
}
async function awaitWelcomeComponent(matrixChat?: RenderResult<any, any, any>): Promise<void> {
async function awaitWelcomeComponent(matrixChat?: RenderResult): Promise<void> {
await waitFor(() => matrixChat?.container.querySelector(".mx_Welcome"));
}
function moveFromWelcomeToLogin(matrixChat?: RenderResult<any, any, any>): Promise<void> {
function moveFromWelcomeToLogin(matrixChat?: RenderResult): Promise<void> {
dis.dispatch({ action: "start_login" });
return awaitLoginComponent(matrixChat);
}

View file

@ -29,7 +29,7 @@ describe("Favicon", () => {
it("should create a link element if one doesn't yet exist", () => {
const favicon = new Favicon();
expect(favicon).toBeTruthy();
const link = window.document.querySelector("link");
const link = window.document.querySelector("link")!;
expect(link.rel).toContain("icon");
});

View file

@ -21,7 +21,6 @@ import { getVectorConfig } from "../../../src/vector/getconfig";
fetchMock.config.overwriteRoutes = true;
describe("getVectorConfig()", () => {
const prevDocumentDomain = document.domain;
const elementDomain = "app.element.io";
const now = 1234567890;
const specificConfig = {
@ -32,7 +31,10 @@ describe("getVectorConfig()", () => {
};
beforeEach(() => {
document.domain = elementDomain;
Object.defineProperty(window, "location", {
value: { href: `https://${elementDomain}`, hostname: elementDomain },
writable: true,
});
// stable value for cachebuster
jest.spyOn(Date, "now").mockReturnValue(now);
@ -41,7 +43,6 @@ describe("getVectorConfig()", () => {
});
afterAll(() => {
document.domain = prevDocumentDomain;
jest.spyOn(Date, "now").mockRestore();
});
@ -107,6 +108,6 @@ describe("getVectorConfig()", () => {
// We can't assert it'll be a SyntaxError as node-fetch behaves differently
// https://github.com/wheresrhys/fetch-mock/issues/270
await expect(getVectorConfig()).rejects.toThrow("Unexpected token } in JSON at position 19");
await expect(getVectorConfig()).rejects.toThrow("in JSON at position 19");
});
});

View file

@ -47,8 +47,7 @@ describe("ElectronPlatform", () => {
beforeEach(() => {
window.electron = mockElectron;
jest.clearAllMocks();
delete window.navigator;
window.navigator = { userAgent: defaultUserAgent } as unknown as Navigator;
Object.defineProperty(window, "navigator", { value: { userAgent: defaultUserAgent }, writable: true });
});
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
@ -56,7 +55,7 @@ describe("ElectronPlatform", () => {
it("flushes rageshake before quitting", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall("before-quit");
const [event, handler] = getElectronEventHandlerCall("before-quit")!;
// correct event bound
expect(event).toBeTruthy();
@ -68,7 +67,7 @@ describe("ElectronPlatform", () => {
it("dispatches view settings action on preferences event", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall("preferences");
const [event, handler] = getElectronEventHandlerCall("preferences")!;
// correct event bound
expect(event).toBeTruthy();
@ -80,7 +79,7 @@ describe("ElectronPlatform", () => {
describe("updates", () => {
it("dispatches on check updates action", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall("check_updates");
const [event, handler] = getElectronEventHandlerCall("check_updates")!;
// correct event bound
expect(event).toBeTruthy();
@ -93,7 +92,7 @@ describe("ElectronPlatform", () => {
it("dispatches on check updates action when update not available", () => {
new ElectronPlatform();
const [, handler] = getElectronEventHandlerCall("check_updates");
const [, handler] = getElectronEventHandlerCall("check_updates")!;
handler({}, false);
expect(dispatchSpy).toHaveBeenCalledWith({
@ -138,8 +137,7 @@ describe("ElectronPlatform", () => {
["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"],
["custom user agent", "Element Desktop: Unknown"],
])("%s = %s", (userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
const platform = new ElectronPlatform();
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
});

View file

@ -33,7 +33,6 @@ describe("WebPlatform", () => {
});
it("registers service worker", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - mocking readonly object
navigator.serviceWorker = { register: jest.fn() };
new WebPlatform();
@ -41,10 +40,7 @@ describe("WebPlatform", () => {
});
it("should call reload on window location object", () => {
delete window.location;
window.location = {
reload: jest.fn(),
} as unknown as Location;
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
const platform = new WebPlatform();
expect(window.location.reload).not.toHaveBeenCalled();
@ -53,10 +49,7 @@ describe("WebPlatform", () => {
});
it("should call reload to install update", () => {
delete window.location;
window.location = {
reload: jest.fn(),
} as unknown as Location;
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
const platform = new WebPlatform();
expect(window.location.reload).not.toHaveBeenCalled();
@ -73,10 +66,8 @@ describe("WebPlatform", () => {
"develop.element.io: Chrome on macOS",
],
])("%s & %s = %s", (url, userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
delete window.location;
window.location = { href: url } as unknown as Location;
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
Object.defineProperty(window, "location", { value: { href: url }, writable: true });
const platform = new WebPlatform();
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
});
@ -88,14 +79,12 @@ describe("WebPlatform", () => {
permission: "notGranted",
};
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Notification = mockNotification;
mockNotification.permission = "notGranted";
});
it("supportsNotifications returns false when platform does not support notifications", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Notification = undefined;
expect(new WebPlatform().supportsNotifications()).toBe(false);
@ -132,7 +121,8 @@ describe("WebPlatform", () => {
});
afterAll(() => {
process.env.VERSION = envVersion;
// @ts-ignore
WebPlatform.VERSION = envVersion;
});
it("should return true from canSelfUpdate()", async () => {
@ -142,18 +132,21 @@ describe("WebPlatform", () => {
});
it("getAppVersion returns normalized app version", async () => {
process.env.VERSION = prodVersion;
// @ts-ignore
WebPlatform.VERSION = prodVersion;
const platform = new WebPlatform();
const version = await platform.getAppVersion();
expect(version).toEqual(prodVersion);
process.env.VERSION = `v${prodVersion}`;
// @ts-ignore
WebPlatform.VERSION = `v${prodVersion}`;
const version2 = await platform.getAppVersion();
// v prefix removed
expect(version2).toEqual(prodVersion);
process.env.VERSION = `version not like semver`;
// @ts-ignore
WebPlatform.VERSION = `version not like semver`;
const notSemverVersion = await platform.getAppVersion();
expect(notSemverVersion).toEqual(`version not like semver`);
});
@ -163,7 +156,8 @@ describe("WebPlatform", () => {
"should return not available and call showNoUpdate when current version " +
"matches most recent version",
async () => {
process.env.VERSION = prodVersion;
// @ts-ignore
WebPlatform.VERSION = prodVersion;
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
@ -178,7 +172,8 @@ describe("WebPlatform", () => {
);
it("should strip v prefix from versions before comparing", async () => {
process.env.VERSION = prodVersion;
// @ts-ignore
WebPlatform.VERSION = prodVersion;
fetchMock.getOnce("/version", `v${prodVersion}`);
const platform = new WebPlatform();
@ -195,7 +190,8 @@ describe("WebPlatform", () => {
it(
"should return ready and call showUpdate when current version " + "differs from most recent version",
async () => {
process.env.VERSION = "0.0.0"; // old version
// @ts-ignore
WebPlatform.VERSION = "0.0.0"; // old version
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
@ -210,7 +206,8 @@ describe("WebPlatform", () => {
);
it("should return ready without showing update when user registered in last 24", async () => {
process.env.VERSION = "0.0.0"; // old version
// @ts-ignore
WebPlatform.VERSION = "0.0.0"; // old version
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();

View file

@ -18,24 +18,28 @@ import { onNewScreen } from "../../../src/vector/routing";
describe("onNewScreen", () => {
it("should replace history if stripping via fields", () => {
delete window.location;
window.location = {
hash: "#/room/!room:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
} as unknown as Location;
Object.defineProperty(window, "location", {
value: {
hash: "#/room/!room:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
},
writable: true,
});
onNewScreen("room/!room:server");
expect(window.location.assign).not.toHaveBeenCalled();
expect(window.location.replace).toHaveBeenCalled();
});
it("should not replace history if changing rooms", () => {
delete window.location;
window.location = {
hash: "#/room/!room1:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
} as unknown as Location;
Object.defineProperty(window, "location", {
value: {
hash: "#/room/!room1:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
},
writable: true,
});
onNewScreen("room/!room2:server");
expect(window.location.assign).toHaveBeenCalled();
expect(window.location.replace).not.toHaveBeenCalled();

View file

@ -17,7 +17,6 @@ limitations under the License.
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
describe("url_utils.ts", function () {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const location: Location = {
hash: "",