mirror of
https://github.com/element-hq/element-web
synced 2024-11-26 03:05:51 +03:00
Apply strictNullChecks
to src/utils/*!exportUtils
(#10455
* Apply `strictNullChecks` to `src/utils/exportUtils` * strict fix * fix strictNullChecks issues in some utils * fix error message * test coverage * lint * more strictNullChecks * small optimisation for getUniqueRoomsWithIndividuals * tidy * test coverage
This commit is contained in:
parent
4ed6e39067
commit
81a4498a8f
18 changed files with 143 additions and 81 deletions
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef } from "react";
|
||||
import {
|
||||
AuthType,
|
||||
IAuthData,
|
||||
|
@ -23,8 +24,8 @@ import {
|
|||
IStageStatus,
|
||||
} from "matrix-js-sdk/src/interactive-auth";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import React, { createRef } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
|
||||
|
||||
import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
|
||||
import Spinner from "../views/elements/Spinner";
|
||||
|
@ -39,7 +40,7 @@ type InteractiveAuthCallbackSuccess = (
|
|||
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
|
||||
export type InteractiveAuthCallback = InteractiveAuthCallbackSuccess & InteractiveAuthCallbackFailure;
|
||||
|
||||
interface IProps {
|
||||
export interface InteractiveAuthProps<T> {
|
||||
// matrix client to use for UI auth requests
|
||||
matrixClient: MatrixClient;
|
||||
// response from initial request. If not supplied, will do a request on mount.
|
||||
|
@ -61,7 +62,7 @@ interface IProps {
|
|||
continueText?: string;
|
||||
continueKind?: string;
|
||||
// callback
|
||||
makeRequest(auth: IAuthData | null): Promise<IAuthData>;
|
||||
makeRequest(auth?: IAuthData): Promise<UIAResponse<T>>;
|
||||
// callback called when the auth process has finished,
|
||||
// successfully or unsuccessfully.
|
||||
// @param {boolean} status True if the operation requiring
|
||||
|
@ -92,14 +93,14 @@ interface IState {
|
|||
submitButtonEnabled: boolean;
|
||||
}
|
||||
|
||||
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
|
||||
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
|
||||
private readonly authLogic: InteractiveAuth;
|
||||
private readonly intervalId: number | null = null;
|
||||
private readonly stageComponent = createRef<IStageComponent>();
|
||||
|
||||
private unmounted = false;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
public constructor(props: InteractiveAuthProps<T>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
|
|
@ -22,7 +22,11 @@ import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
|
|||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback } from "../../structures/InteractiveAuth";
|
||||
import InteractiveAuth, {
|
||||
ERROR_USER_CANCELLED,
|
||||
InteractiveAuthCallback,
|
||||
InteractiveAuthProps,
|
||||
} from "../../structures/InteractiveAuth";
|
||||
import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
|
||||
|
@ -37,17 +41,11 @@ type DialogAesthetics = Partial<{
|
|||
};
|
||||
}>;
|
||||
|
||||
export interface InteractiveAuthDialogProps {
|
||||
export interface InteractiveAuthDialogProps<T = unknown>
|
||||
extends Pick<InteractiveAuthProps<T>, "makeRequest" | "authData"> {
|
||||
// matrix client to use for UI auth requests
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
// response from initial request. If not supplied, will do a request on
|
||||
// mount.
|
||||
authData?: IAuthData;
|
||||
|
||||
// callback
|
||||
makeRequest: (auth: IAuthData) => Promise<IAuthData>;
|
||||
|
||||
// Optional title and body to show when not showing a particular stage
|
||||
title?: string;
|
||||
body?: string;
|
||||
|
@ -83,8 +81,8 @@ interface IState {
|
|||
uiaStagePhase: number | null;
|
||||
}
|
||||
|
||||
export default class InteractiveAuthDialog extends React.Component<InteractiveAuthDialogProps, IState> {
|
||||
public constructor(props: InteractiveAuthDialogProps) {
|
||||
export default class InteractiveAuthDialog<T> extends React.Component<InteractiveAuthDialogProps<T>, IState> {
|
||||
public constructor(props: InteractiveAuthDialogProps<T>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
|
|
@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { _t, UserFriendlyError } from "../languageHandler";
|
||||
import { makeType } from "./TypeUtils";
|
||||
import SdkConfig from "../SdkConfig";
|
||||
import { ValidatedServerConfig } from "./ValidatedServerConfig";
|
||||
|
||||
|
@ -43,7 +42,7 @@ export default class AutoDiscoveryUtils {
|
|||
* @param {string | Error} error The error to check
|
||||
* @returns {boolean} True if the error is a liveliness error.
|
||||
*/
|
||||
public static isLivelinessError(error: string | Error): boolean {
|
||||
public static isLivelinessError(error?: string | Error | null): boolean {
|
||||
if (!error) return false;
|
||||
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) =>
|
||||
typeof error === "string" ? e === error : e === error.message,
|
||||
|
@ -197,7 +196,7 @@ export default class AutoDiscoveryUtils {
|
|||
): ValidatedServerConfig {
|
||||
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
|
||||
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
|
||||
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
|
||||
// in the log so we can find this bit of code but otherwise tell the user "it broke".
|
||||
logger.error("Ended up in a state of not knowing which homeserver to connect to.");
|
||||
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||
}
|
||||
|
@ -216,7 +215,7 @@ export default class AutoDiscoveryUtils {
|
|||
// of Element.
|
||||
let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"];
|
||||
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
|
||||
preferredIdentityUrl = isResult["base_url"];
|
||||
preferredIdentityUrl = isResult["base_url"] ?? undefined;
|
||||
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
|
||||
logger.error("Error determining preferred identity server URL:", isResult);
|
||||
if (isResult.state === AutoDiscovery.FAIL_ERROR) {
|
||||
|
@ -244,6 +243,12 @@ export default class AutoDiscoveryUtils {
|
|||
}
|
||||
|
||||
const preferredHomeserverUrl = hsResult["base_url"];
|
||||
|
||||
if (!preferredHomeserverUrl) {
|
||||
logger.error("No homeserver URL configured");
|
||||
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||
}
|
||||
|
||||
let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
|
||||
|
||||
const url = new URL(preferredHomeserverUrl);
|
||||
|
@ -255,7 +260,7 @@ export default class AutoDiscoveryUtils {
|
|||
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||
}
|
||||
|
||||
return makeType(ValidatedServerConfig, {
|
||||
return {
|
||||
hsUrl: preferredHomeserverUrl,
|
||||
hsName: preferredHomeserverName,
|
||||
hsNameIsDifferent: url.hostname !== preferredHomeserverName,
|
||||
|
@ -263,6 +268,6 @@ export default class AutoDiscoveryUtils {
|
|||
isDefault: false,
|
||||
warning: hsResult.error,
|
||||
isNameResolvable: !isSynthetic,
|
||||
});
|
||||
} as ValidatedServerConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,8 +103,6 @@ export default class DMRoomMap {
|
|||
}
|
||||
|
||||
private onAccountData = (ev: MatrixEvent): void => {
|
||||
console.log("onAccountData");
|
||||
|
||||
if (ev.getType() == EventType.Direct) {
|
||||
this.setMDirectFromContent(ev.getContent());
|
||||
this.userToRooms = null;
|
||||
|
@ -207,13 +205,16 @@ export default class DMRoomMap {
|
|||
|
||||
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
|
||||
if (!this.roomToUser) return {}; // No rooms means no map.
|
||||
return Object.keys(this.roomToUser)
|
||||
.map((r) => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) }))
|
||||
.filter((r) => r.userId && r.room?.getInvitedAndJoinedMemberCount() === 2)
|
||||
.reduce((obj, r) => {
|
||||
obj[r.userId] = r.room;
|
||||
return obj;
|
||||
}, {} as Record<string, Room>);
|
||||
// map roomToUser to valid rooms with two participants
|
||||
return Object.keys(this.roomToUser).reduce((acc, roomId: string) => {
|
||||
const userId = this.getUserIdForRoomId(roomId);
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
const hasTwoMembers = room?.getInvitedAndJoinedMemberCount() === 2;
|
||||
if (userId && room && hasTwoMembers) {
|
||||
acc[userId] = room;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, Room>);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,9 +237,7 @@ export default class DMRoomMap {
|
|||
// to avoid multiple devices fighting to correct
|
||||
// the account data, only try to send the corrected
|
||||
// version once.
|
||||
logger.warn(
|
||||
`Invalid m.direct account data detected ` + `(self-chats that shouldn't be), patching it up.`,
|
||||
);
|
||||
logger.warn(`Invalid m.direct account data detected (self-chats that shouldn't be), patching it up.`);
|
||||
if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
|
||||
this.hasSentOutPatchDirectAccountDataPatch = true;
|
||||
this.matrixClient.setAccountData(EventType.Direct, userToRooms);
|
||||
|
|
|
@ -21,8 +21,8 @@ import SdkConfig from "../SdkConfig";
|
|||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { Policies } from "../Terms";
|
||||
|
||||
export function getDefaultIdentityServerUrl(): string {
|
||||
return SdkConfig.get("validated_server_config").isUrl;
|
||||
export function getDefaultIdentityServerUrl(): string | undefined {
|
||||
return SdkConfig.get("validated_server_config")?.isUrl;
|
||||
}
|
||||
|
||||
export function setToDefaultIdentityServer(): void {
|
||||
|
|
|
@ -102,7 +102,10 @@ export class MediaEventHelper implements IDestroyable {
|
|||
}
|
||||
}
|
||||
|
||||
return fetch(this.media.thumbnailHttp).then((r) => r.blob());
|
||||
const thumbnailHttp = this.media.thumbnailHttp;
|
||||
if (!thumbnailHttp) return Promise.resolve(null);
|
||||
|
||||
return fetch(thumbnailHttp).then((r) => r.blob());
|
||||
};
|
||||
|
||||
public static isEligible(event: MatrixEvent): boolean {
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a class of a given type using the objects defined. This
|
||||
* is a stopgap function while we don't have TypeScript interfaces.
|
||||
* In future, we'd define the `type` as an interface and just cast
|
||||
* it instead of cheating like we are here.
|
||||
* @param {Type} Type The type of class to construct.
|
||||
* @param {*} opts The options (properties) to set on the object.
|
||||
* @returns {*} The created object.
|
||||
*/
|
||||
export function makeType<T>(Type: { new (): T }, opts: Partial<T>): T {
|
||||
const c = new Type();
|
||||
Object.assign(c, opts);
|
||||
return c;
|
||||
}
|
|
@ -24,7 +24,7 @@ type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResp
|
|||
|
||||
export function wrapRequestWithDialog<R, A = any>(
|
||||
requestFunction: FunctionWithUIA<R, A>,
|
||||
opts: Omit<InteractiveAuthDialogProps, "makeRequest" | "onFinished">,
|
||||
opts: Omit<InteractiveAuthDialogProps<R>, "makeRequest" | "onFinished">,
|
||||
): (...args: A[]) => Promise<R> {
|
||||
return async function (...args): Promise<R> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
@ -119,6 +119,7 @@ export default class WidgetUtils {
|
|||
if (
|
||||
testUrl.protocol === scalarUrl.protocol &&
|
||||
testUrl.host === scalarUrl.host &&
|
||||
scalarUrl.pathname &&
|
||||
testUrl.pathname?.startsWith(scalarUrl.pathname)
|
||||
) {
|
||||
return true;
|
||||
|
|
|
@ -61,6 +61,12 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
|
|||
}
|
||||
|
||||
const room = cli.getRoom(roomId);
|
||||
|
||||
// should not encounter this
|
||||
if (!room) {
|
||||
throw new Error(`Expected to find room for id ${roomId}`);
|
||||
}
|
||||
|
||||
// await any queued messages being sent so that they do not fail
|
||||
await Promise.all(
|
||||
room
|
||||
|
|
|
@ -85,12 +85,14 @@ export const createMapSiteLinkFromEvent = (event: MatrixEvent): string | null =>
|
|||
if (mLocation !== undefined) {
|
||||
const uri = mLocation["uri"];
|
||||
if (uri !== undefined) {
|
||||
return makeMapSiteLink(parseGeoUri(uri));
|
||||
const geoCoords = parseGeoUri(uri);
|
||||
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||
}
|
||||
} else {
|
||||
const geoUri = content["geo_uri"];
|
||||
if (geoUri) {
|
||||
return makeMapSiteLink(parseGeoUri(geoUri));
|
||||
const geoCoords = parseGeoUri(geoUri);
|
||||
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -28,16 +28,23 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined =>
|
|||
if (!m) return;
|
||||
const parts = m[1].split(";");
|
||||
const coords = parts[0].split(",");
|
||||
let uncertainty: number | null;
|
||||
let uncertainty: number | null | undefined = undefined;
|
||||
for (const param of parts.slice(1)) {
|
||||
const m = param.match(/u=(.*)/);
|
||||
if (m) uncertainty = parse(m[1]);
|
||||
}
|
||||
const latitude = parse(coords[0]);
|
||||
const longitude = parse(coords[1]);
|
||||
|
||||
if (latitude === null || longitude === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
latitude: parse(coords[0]),
|
||||
longitude: parse(coords[1]),
|
||||
latitude: latitude!,
|
||||
longitude: longitude!,
|
||||
altitude: parse(coords[2]),
|
||||
accuracy: uncertainty,
|
||||
accuracy: uncertainty!,
|
||||
altitudeAccuracy: null,
|
||||
heading: null,
|
||||
speed: null,
|
||||
|
|
|
@ -120,7 +120,7 @@ export const reorderLexicographically = (
|
|||
// verify the right move would be sufficient
|
||||
if (
|
||||
rightBoundIdx === newOrder.length - 1 &&
|
||||
(newOrder[rightBoundIdx] ? stringToBase(newOrder[rightBoundIdx].order) : BigInt(Number.MAX_VALUE)) -
|
||||
(newOrder[rightBoundIdx]?.order ? stringToBase(newOrder[rightBoundIdx].order!) : BigInt(Number.MAX_VALUE)) -
|
||||
prevBase <=
|
||||
rightBoundIdx - toIndex
|
||||
) {
|
||||
|
|
|
@ -65,6 +65,9 @@ export const lookupThreePids = async (
|
|||
if (threePids.length === 0) return [];
|
||||
|
||||
const token = await client.identityServer.getAccessToken();
|
||||
|
||||
if (!token) return [];
|
||||
|
||||
const lookedUp = await client.bulkLookupThreePids(
|
||||
threePids.map((t) => [t.isEmail ? "email" : "msisdn", t.userId]),
|
||||
token,
|
||||
|
|
|
@ -47,7 +47,6 @@ import { MapperOpts } from "matrix-js-sdk/src/event-mapper";
|
|||
|
||||
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||
import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
|
||||
import { makeType } from "../../src/utils/TypeUtils";
|
||||
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
|
||||
import { EnhancedMap } from "../../src/utils/maps";
|
||||
import { AsyncStoreWithClient } from "../../src/stores/AsyncStoreWithClient";
|
||||
|
@ -591,13 +590,13 @@ export function mkStubRoom(
|
|||
} as unknown as Room;
|
||||
}
|
||||
|
||||
export function mkServerConfig(hsUrl: string, isUrl: string) {
|
||||
return makeType(ValidatedServerConfig, {
|
||||
export function mkServerConfig(hsUrl: string, isUrl: string): ValidatedServerConfig {
|
||||
return {
|
||||
hsUrl,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl,
|
||||
});
|
||||
} as ValidatedServerConfig;
|
||||
}
|
||||
|
||||
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ClientEvent, EventType, IContent, MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { ClientEvent, EventType, IContent, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import DMRoomMap from "../../src/utils/DMRoomMap";
|
||||
import { mkEvent, stubClient } from "../test-utils";
|
||||
|
@ -137,4 +137,56 @@ describe("DMRoomMap", () => {
|
|||
expect(dmRoomMap.getRoomIds()).toEqual(new Set([roomId1, roomId2, roomId4]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("getUniqueRoomsWithIndividuals()", () => {
|
||||
const bigRoom = {
|
||||
roomId: "!bigRoom:server.org",
|
||||
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(5000),
|
||||
} as unknown as Room;
|
||||
const dmWithBob = {
|
||||
roomId: "!dmWithBob:server.org",
|
||||
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(2),
|
||||
} as unknown as Room;
|
||||
const dmWithCharlie = {
|
||||
roomId: "!dmWithCharlie:server.org",
|
||||
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(2),
|
||||
} as unknown as Room;
|
||||
const smallRoom = {
|
||||
roomId: "!smallRoom:server.org",
|
||||
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(3),
|
||||
} as unknown as Room;
|
||||
|
||||
const mDirectContent = {
|
||||
"@bob:server.org": [bigRoom.roomId, dmWithBob.roomId, smallRoom.roomId],
|
||||
"@charlie:server.org": [dmWithCharlie.roomId, smallRoom.roomId],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
client.getAccountData.mockReturnValue(mkMDirectEvent(mDirectContent));
|
||||
client.getRoom.mockImplementation((roomId: string) =>
|
||||
[bigRoom, smallRoom, dmWithCharlie, dmWithBob].find((room) => room.roomId === roomId),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns an empty object when room map has not been populated", () => {
|
||||
const instance = new DMRoomMap(client);
|
||||
expect(instance.getUniqueRoomsWithIndividuals()).toEqual({});
|
||||
});
|
||||
|
||||
it("returns map of users to rooms with 2 members", () => {
|
||||
const dmRoomMap = new DMRoomMap(client);
|
||||
dmRoomMap.start();
|
||||
expect(dmRoomMap.getUniqueRoomsWithIndividuals()).toEqual({
|
||||
"@bob:server.org": dmWithBob,
|
||||
"@charlie:server.org": dmWithCharlie,
|
||||
});
|
||||
});
|
||||
|
||||
it("excludes rooms that are not found by matrixClient", () => {
|
||||
client.getRoom.mockReset().mockReturnValue(undefined);
|
||||
const dmRoomMap = new DMRoomMap(client);
|
||||
dmRoomMap.start();
|
||||
expect(dmRoomMap.getUniqueRoomsWithIndividuals()).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,15 +31,23 @@ describe("createMapSiteLinkFromEvent", () => {
|
|||
).toBeNull();
|
||||
});
|
||||
|
||||
it("returns OpenStreetMap link if event contains m.location", () => {
|
||||
it("returns OpenStreetMap link if event contains m.location with valid uri", () => {
|
||||
expect(createMapSiteLinkFromEvent(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
||||
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null if event contains m.location with invalid uri", () => {
|
||||
expect(createMapSiteLinkFromEvent(makeLocationEvent("123 Sesame St"))).toBeNull();
|
||||
});
|
||||
|
||||
it("returns OpenStreetMap link if event contains geo_uri", () => {
|
||||
expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
||||
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null if event contains an invalid geo_uri", () => {
|
||||
expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("123 Sesame St"))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,6 +21,14 @@ describe("parseGeoUri", () => {
|
|||
expect(parseGeoUri("")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("returns undefined if latitude is not a number", () => {
|
||||
expect(parseGeoUri("geo:ABCD,16.3695,183")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined if longitude is not a number", () => {
|
||||
expect(parseGeoUri("geo:48.2010,EFGH,183")).toBeUndefined();
|
||||
});
|
||||
|
||||
// We use some examples from the spec, but don't check semantics
|
||||
// like two textually-different URIs being equal, since we are
|
||||
// just a humble parser.
|
||||
|
|
Loading…
Reference in a new issue