1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-08-07 21:23:00 +03:00

Merge branch 'develop' into johannes/find-myself

This commit is contained in:
Johannes Marbach
2023-02-13 20:16:04 +01:00
committed by GitHub
612 changed files with 3608 additions and 2769 deletions

View File

@@ -0,0 +1,54 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
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.
*/
import { mocked, Mocked } from "jest-mock";
import { EventType, IContent, MatrixClient } from "matrix-js-sdk/src/matrix";
import DMRoomMap from "../../src/utils/DMRoomMap";
import { mkEvent, stubClient } from "../test-utils";
describe("DMRoomMap", () => {
const roomId1 = "!room1:example.com";
const roomId2 = "!room2:example.com";
const roomId3 = "!room3:example.com";
const roomId4 = "!room4:example.com";
const mDirectContent = {
"user@example.com": [roomId1, roomId2],
"@user:example.com": [roomId1, roomId3, roomId4],
"@user2:example.com": [] as string[],
} satisfies IContent;
let client: Mocked<MatrixClient>;
let dmRoomMap: DMRoomMap;
beforeEach(() => {
client = mocked(stubClient());
const mDirectEvent = mkEvent({
event: true,
type: EventType.Direct,
user: client.getSafeUserId(),
content: mDirectContent,
});
client.getAccountData.mockReturnValue(mDirectEvent);
dmRoomMap = new DMRoomMap(client);
});
it("getRoomIds should return the room Ids", () => {
expect(dmRoomMap.getRoomIds()).toEqual(new Set([roomId1, roomId2, roomId3, roomId4]));
});
});

View File

@@ -42,7 +42,7 @@ describe("formatSeconds", () => {
});
describe("formatRelativeTime", () => {
let dateSpy;
let dateSpy: jest.SpyInstance<number, []>;
beforeAll(() => {
dateSpy = jest
.spyOn(global.Date, "now")

View File

@@ -432,7 +432,7 @@ describe("EventUtils", () => {
stubClient();
client = MatrixClientPeg.get();
room = new Room(ROOM_ID, client, client.getUserId(), {
room = new Room(ROOM_ID, client, client.getUserId()!, {
pendingEventOrdering: PendingEventOrdering.Detached,
});

View File

@@ -18,9 +18,11 @@ import { TextEncoder } from "util";
import nodeCrypto from "crypto";
import { Crypto } from "@peculiar/webcrypto";
import type * as MegolmExportEncryptionExport from "../../src/utils/MegolmExportEncryption";
const webCrypto = new Crypto();
function getRandomValues<T extends ArrayBufferView>(buf: T): T {
function getRandomValues<T extends ArrayBufferView | null>(buf: T): T {
// @ts-ignore fussy generics
return nodeCrypto.randomFillSync(buf);
}
@@ -70,7 +72,7 @@ function stringToArray(s: string): ArrayBufferLike {
}
describe("MegolmExportEncryption", function () {
let MegolmExportEncryption;
let MegolmExportEncryption: typeof MegolmExportEncryptionExport;
beforeEach(() => {
window.crypto = {
@@ -131,7 +133,7 @@ cissyYBxjsfsAn
// TODO find a subtlecrypto shim which doesn't break this test
it.skip("should decrypt a range of inputs", function () {
function next(i) {
function next(i: number): Promise<string | undefined> | undefined {
if (i >= TEST_VECTORS.length) {
return;
}
@@ -142,7 +144,7 @@ cissyYBxjsfsAn
return next(i + 1);
});
}
return next(0);
next(0);
});
});

View File

@@ -29,7 +29,7 @@ const MXID1 = "@user1:server";
const MXID2 = "@user2:server";
const MXID3 = "@user3:server";
const MXID_PROFILE_STATES = {
const MXID_PROFILE_STATES: Record<string, Promise<any>> = {
[MXID1]: Promise.resolve({}),
[MXID2]: Promise.reject({ errcode: "M_FORBIDDEN" }),
[MXID3]: Promise.reject({ errcode: "M_NOT_FOUND" }),
@@ -47,7 +47,7 @@ jest.mock("../../src/settings/SettingsStore", () => ({
const mockPromptBeforeInviteUnknownUsers = (value: boolean) => {
mocked(SettingsStore.getValue).mockImplementation(
(settingName: string, roomId: string = null, _excludeDefault = false): any => {
(settingName: string, roomId: string, _excludeDefault = false): any => {
if (settingName === "promptBeforeInviteUnknownUsers" && roomId === ROOMID) {
return value;
}
@@ -57,7 +57,7 @@ const mockPromptBeforeInviteUnknownUsers = (value: boolean) => {
const mockCreateTrackedDialog = (callbackName: "onInviteAnyways" | "onGiveUp") => {
mocked(Modal.createDialog).mockImplementation((...rest: Parameters<ModalManager["createDialog"]>): any => {
rest[1][callbackName]();
rest[1]![callbackName]();
});
};

View File

@@ -22,14 +22,14 @@ import DMRoomMap from "../../src/utils/DMRoomMap";
function mkClient(selfTrust = false) {
return {
getUserId: () => "@self:localhost",
checkUserTrust: (userId) => ({
checkUserTrust: (userId: string) => ({
isCrossSigningVerified: () => userId[1] == "T",
wasCrossSigningVerified: () => userId[1] == "T" || userId[1] == "W",
}),
checkDeviceTrust: (userId, deviceId) => ({
checkDeviceTrust: (userId: string, deviceId: string) => ({
isVerified: () => (userId === "@self:localhost" ? selfTrust : userId[2] == "T"),
}),
getStoredDevicesForUser: (userId) => ["DEVICE"],
getStoredDevicesForUser: (userId: string) => ["DEVICE"],
} as unknown as MatrixClient;
}
@@ -61,7 +61,7 @@ describe("mkClient self-test", function () {
describe("shieldStatusForMembership self-trust behaviour", function () {
beforeAll(() => {
const mockInstance = {
getUserIdForRoomId: (roomId) => (roomId === "DM" ? "@any:h" : null),
getUserIdForRoomId: (roomId: string) => (roomId === "DM" ? "@any:h" : null),
} as unknown as DMRoomMap;
jest.spyOn(DMRoomMap, "shared").mockReturnValue(mockInstance);
});
@@ -164,7 +164,7 @@ describe("shieldStatusForMembership self-trust behaviour", function () {
describe("shieldStatusForMembership other-trust behaviour", function () {
beforeAll(() => {
const mockInstance = {
getUserIdForRoomId: (roomId) => (roomId === "DM" ? "@any:h" : null),
getUserIdForRoomId: (roomId: string) => (roomId === "DM" ? "@any:h" : null),
} as unknown as DMRoomMap;
jest.spyOn(DMRoomMap, "shared").mockReturnValue(mockInstance);
});

View File

@@ -22,7 +22,7 @@ describe("Singleflight", () => {
});
it("should throw for bad context variables", () => {
const permutations: [Object, string][] = [
const permutations: [Object | null, string | null][] = [
[null, null],
[{}, null],
[null, "test"],

View File

@@ -15,8 +15,10 @@ limitations under the License.
*/
import { logger } from "matrix-js-sdk/src/logger";
import { Mocked } from "jest-mock";
import {
GenericPosition,
GeolocationError,
getGeoUri,
mapGeolocationError,
@@ -27,7 +29,7 @@ import { getCurrentPosition } from "../../../src/utils/beacon/geolocation";
import { makeGeolocationPosition, mockGeolocation, getMockGeolocationPositionError } from "../../test-utils";
describe("geolocation utilities", () => {
let geolocation;
let geolocation: Mocked<Geolocation>;
const defaultPosition = makeGeolocationPosition({});
// 14.03.2022 16:15
@@ -45,7 +47,7 @@ describe("geolocation utilities", () => {
describe("getGeoUri", () => {
it("Renders a URI with only lat and lon", () => {
const pos = {
const pos: GenericPosition = {
latitude: 43.2,
longitude: 12.4,
altitude: undefined,
@@ -57,7 +59,7 @@ describe("geolocation utilities", () => {
});
it("Nulls in location are not shown in URI", () => {
const pos = {
const pos: GenericPosition = {
latitude: 43.2,
longitude: 12.4,
altitude: null,
@@ -69,7 +71,7 @@ describe("geolocation utilities", () => {
});
it("Renders a URI with 3 coords", () => {
const pos = {
const pos: GenericPosition = {
latitude: 43.2,
longitude: 12.4,
altitude: 332.54,
@@ -80,7 +82,7 @@ describe("geolocation utilities", () => {
});
it("Renders a URI with accuracy", () => {
const pos = {
const pos: GenericPosition = {
latitude: 43.2,
longitude: 12.4,
altitude: undefined,
@@ -193,9 +195,10 @@ describe("geolocation utilities", () => {
it("maps geolocation position error and calls error handler", () => {
// suppress expected errors from test log
jest.spyOn(logger, "error").mockImplementation(() => {});
geolocation.watchPosition.mockImplementation((_callback, error) =>
error(getMockGeolocationPositionError(1, "message")),
);
geolocation.watchPosition.mockImplementation((_callback, error) => {
error(getMockGeolocationPositionError(1, "message"));
return -1;
});
const positionHandler = jest.fn();
const errorHandler = jest.fn();
watchPosition(positionHandler, errorHandler);

View File

@@ -44,9 +44,9 @@ function assertLocalRoom(room: LocalRoom, targets: Member[], encrypted: boolean)
expect(roomCreateEvent.getContent()["room_version"]).toBe(KNOWN_SAFE_ROOM_VERSION);
// check that the user and all targets are joined
expect(room.getMember("@userId:matrix.org").membership).toBe("join");
expect(room.getMember("@userId:matrix.org")?.membership).toBe("join");
targets.forEach((target: Member) => {
expect(room.getMember(target.userId).membership).toBe("join");
expect(room.getMember(target.userId)?.membership).toBe("join");
});
if (encrypted) {

View File

@@ -30,12 +30,15 @@ jest.mock("../../../src/utils/room/getFunctionalMembers", () => ({
describe("findDMForUser", () => {
const userId1 = "@user1:example.com";
const userId2 = "@user2:example.com";
const userId3 = "@user3:example.com";
const botId = "@bot:example.com";
let room1: Room;
let room2: LocalRoom;
let room3: Room;
let room4: Room;
let room5: Room;
let room6: Room;
const room7Id = "!room7:example.com";
let dmRoomMap: DMRoomMap;
let mockClient: MatrixClient;
@@ -78,33 +81,56 @@ describe("findDMForUser", () => {
room5 = new Room("!room5:example.com", mockClient, userId1);
room5.getLastActiveTimestamp = () => 100;
// room not correctly stored in userId → room map; should be found by the "all rooms" fallback
room6 = new Room("!room6:example.com", mockClient, userId1);
room6.getMyMembership = () => "join";
room6.currentState.setStateEvents([
makeMembershipEvent(room6.roomId, userId1, "join"),
makeMembershipEvent(room6.roomId, userId3, "join"),
]);
mocked(mockClient.getRoom).mockImplementation((roomId: string) => {
return {
[room1.roomId]: room1,
[room2.roomId]: room2,
[room3.roomId]: room3,
[room4.roomId]: room4,
[room5.roomId]: room5,
}[roomId];
return (
{
[room1.roomId]: room1,
[room2.roomId]: room2,
[room3.roomId]: room3,
[room4.roomId]: room4,
[room5.roomId]: room5,
[room6.roomId]: room6,
}[roomId] || null
);
});
dmRoomMap = {
getDMRoomForIdentifiers: jest.fn(),
getDMRoomsForUserId: jest.fn(),
getRoomIds: jest.fn().mockReturnValue(
new Set([
room1.roomId,
room2.roomId,
room3.roomId,
room4.roomId,
room5.roomId,
room6.roomId,
room7Id, // this room does not exist in client
]),
),
} as unknown as DMRoomMap;
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
mocked(dmRoomMap.getDMRoomsForUserId).mockReturnValue([
room1.roomId,
room2.roomId,
room3.roomId,
room4.roomId,
room5.roomId,
]);
mocked(dmRoomMap.getDMRoomsForUserId).mockImplementation((userId: string) => {
if (userId === userId1) {
return [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId, room7Id];
}
return [];
});
});
describe("for an empty DM room list", () => {
beforeEach(() => {
mocked(dmRoomMap.getDMRoomsForUserId).mockReturnValue([]);
mocked(dmRoomMap.getRoomIds).mockReturnValue(new Set());
});
it("should return undefined", () => {
@@ -125,4 +151,11 @@ describe("findDMForUser", () => {
expect(findDMForUser(mockClient, userId1)).toBe(room3);
});
it("should find a room by the 'all rooms' fallback", () => {
room1.getLastActiveTimestamp = () => 1;
room6.getLastActiveTimestamp = () => 2;
expect(findDMForUser(mockClient, userId3)).toBe(room6);
});
});

View File

@@ -53,8 +53,8 @@ describe("findDMRoom", () => {
expect(findDMRoom(mockClient, [member1])).toBe(room1);
});
it("should return null for a single target without a room", () => {
mocked(findDMForUser).mockReturnValue(null);
it("should return undefined for a single target without a room", () => {
mocked(findDMForUser).mockReturnValue(undefined);
expect(findDMRoom(mockClient, [member1])).toBeNull();
});

View File

@@ -291,7 +291,7 @@ describe("export", function () {
it("tests the file extension splitter", function () {
const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null);
const fileNameWithExtensions = {
const fileNameWithExtensions: Record<string, [string, string]> = {
"": ["", ""],
"name": ["name", ""],
"name.txt": ["name", ".txt"],

View File

@@ -104,7 +104,7 @@ describe("HTMLExport", () => {
/** set a mock fetch response for an MXC */
function mockMxc(mxc: string, body: string) {
const media = mediaFromMxc(mxc, client);
fetchMock.get(media.srcHttp, body);
fetchMock.get(media.srcHttp!, body);
}
it("should have an SDK-branded destination file name", () => {
@@ -286,7 +286,7 @@ describe("HTMLExport", () => {
// Ensure that the attachment is present
const files = getFiles(exporter);
const file = files[Object.keys(files).find((k) => k.endsWith(".txt"))];
const file = files[Object.keys(files).find((k) => k.endsWith(".txt"))!];
expect(file).not.toBeUndefined();
// Ensure that the attachment has the expected content

View File

@@ -67,7 +67,7 @@ describe("local-room", () => {
});
describe("for a local room", () => {
let prom;
let prom: Promise<unknown>;
beforeEach(() => {
jest.spyOn(defaultDispatcher, "dispatch");

View File

@@ -26,12 +26,8 @@ describe("isLocalRoom", () => {
beforeEach(() => {
const client = createTestClient();
room = new Room("!room:example.com", client, client.getUserId());
localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test", client, client.getUserId());
});
it("should return false for null", () => {
expect(isLocalRoom(null)).toBe(false);
room = new Room("!room:example.com", client, client.getUserId()!);
localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test", client, client.getUserId()!);
});
it("should return false for a Room", () => {

View File

@@ -59,6 +59,7 @@ describe("isRoomReady", () => {
beforeEach(() => {
mocked(client.getRoom).mockImplementation((roomId: string) => {
if (roomId === room1.roomId) return room1;
return null;
});
});

View File

@@ -41,8 +41,8 @@ describe("isSelfLocation", () => {
[M_TEXT.name]: "",
[M_TIMESTAMP.name]: 0,
// Note: no m.asset!
};
expect(isSelfLocation(content as ILocationContent)).toBe(true);
} as unknown as ILocationContent;
expect(isSelfLocation(content)).toBe(true);
});
it("Returns true for a missing m.asset type", () => {
@@ -56,8 +56,8 @@ describe("isSelfLocation", () => {
[M_ASSET.name]: {
// Note: no type!
},
};
expect(isSelfLocation(content as ILocationContent)).toBe(true);
} as unknown as ILocationContent;
expect(isSelfLocation(content)).toBe(true);
});
it("Returns false for an unknown asset type", () => {

View File

@@ -15,6 +15,7 @@ limitations under the License.
*/
import { EventEmitter } from "events";
import { MatrixClient, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { waitForMember } from "../../src/utils/membership";
@@ -22,14 +23,14 @@ import { waitForMember } from "../../src/utils/membership";
const timeout = 30;
describe("waitForMember", () => {
let client;
let client: EventEmitter;
beforeEach(() => {
client = new EventEmitter();
});
it("resolves with false if the timeout is reached", (done) => {
waitForMember(client, "", "", { timeout: 0 }).then((r) => {
waitForMember(<MatrixClient>client, "", "", { timeout: 0 }).then((r) => {
expect(r).toBe(false);
done();
});
@@ -38,7 +39,7 @@ describe("waitForMember", () => {
it("resolves with false if the timeout is reached, even if other RoomState.newMember events fire", (done) => {
const roomId = "!roomId:domain";
const userId = "@clientId:domain";
waitForMember(client, roomId, userId, { timeout }).then((r) => {
waitForMember(<MatrixClient>client, roomId, userId, { timeout }).then((r) => {
expect(r).toBe(false);
done();
});
@@ -48,9 +49,9 @@ describe("waitForMember", () => {
it("resolves with true if RoomState.newMember fires", (done) => {
const roomId = "!roomId:domain";
const userId = "@clientId:domain";
waitForMember(client, roomId, userId, { timeout }).then((r) => {
waitForMember(<MatrixClient>client, roomId, userId, { timeout }).then((r) => {
expect(r).toBe(true);
expect(client.listeners("RoomState.newMember").length).toBe(0);
expect((<MatrixClient>client).listeners(RoomStateEvent.NewMember).length).toBe(0);
done();
});
client.emit("RoomState.newMember", undefined, undefined, { roomId, userId });

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { mocked } from "jest-mock";
import { Mocked, mocked } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
@@ -36,9 +36,9 @@ import { MatrixClientPeg } from "../../src/MatrixClientPeg";
jest.mock("../../src/settings/SettingsStore");
describe("notifications", () => {
let accountDataStore = {};
let mockClient;
let accountDataEventKey;
let accountDataStore: Record<string, MatrixEvent> = {};
let mockClient: Mocked<MatrixClient>;
let accountDataEventKey: string;
beforeEach(() => {
jest.clearAllMocks();
@@ -53,7 +53,7 @@ describe("notifications", () => {
}),
});
accountDataStore = {};
accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId!);
mocked(SettingsStore).getValue.mockReturnValue(false);
});

View File

@@ -43,7 +43,7 @@ describe("Permalinks", function () {
serverACLContent?: { deny?: string[]; allow?: string[] },
): Room {
members.forEach((m) => (m.membership = "join"));
const powerLevelsUsers = members.reduce((pl, member) => {
const powerLevelsUsers = members.reduce<Record<string, number>>((pl, member) => {
if (Number.isFinite(member.powerLevel)) {
pl[member.userId] = member.powerLevel;
}
@@ -74,7 +74,7 @@ describe("Permalinks", function () {
jest.spyOn(room, "getCanonicalAlias").mockReturnValue(null);
jest.spyOn(room, "getJoinedMembers").mockReturnValue(members);
jest.spyOn(room, "getMember").mockImplementation((userId) => members.find((m) => m.userId === userId));
jest.spyOn(room, "getMember").mockImplementation((userId) => members.find((m) => m.userId === userId) || null);
return room;
}
@@ -369,22 +369,22 @@ describe("Permalinks", function () {
it("should correctly parse room permalinks with a via argument", () => {
const result = parsePermalink("https://matrix.to/#/!room_id:server?via=some.org");
expect(result.roomIdOrAlias).toBe("!room_id:server");
expect(result.viaServers).toEqual(["some.org"]);
expect(result?.roomIdOrAlias).toBe("!room_id:server");
expect(result?.viaServers).toEqual(["some.org"]);
});
it("should correctly parse room permalink via arguments", () => {
const result = parsePermalink("https://matrix.to/#/!room_id:server?via=foo.bar&via=bar.foo");
expect(result.roomIdOrAlias).toBe("!room_id:server");
expect(result.viaServers).toEqual(["foo.bar", "bar.foo"]);
expect(result?.roomIdOrAlias).toBe("!room_id:server");
expect(result?.viaServers).toEqual(["foo.bar", "bar.foo"]);
});
it("should correctly parse event permalink via arguments", () => {
const result = parsePermalink(
"https://matrix.to/#/!room_id:server/$event_id/some_thing_here/foobar" + "?via=m1.org&via=m2.org",
);
expect(result.eventId).toBe("$event_id/some_thing_here/foobar");
expect(result.roomIdOrAlias).toBe("!room_id:server");
expect(result.viaServers).toEqual(["m1.org", "m2.org"]);
expect(result?.eventId).toBe("$event_id/some_thing_here/foobar");
expect(result?.roomIdOrAlias).toBe("!room_id:server");
expect(result?.viaServers).toEqual(["m1.org", "m2.org"]);
});
});

View File

@@ -37,8 +37,8 @@ describe("pillify", () => {
beforeEach(() => {
stubClient();
const cli = MatrixClientPeg.get();
(cli.getRoom as jest.Mock).mockReturnValue(new Room(roomId, cli, cli.getUserId()));
cli.pushRules.global = {
(cli.getRoom as jest.Mock).mockReturnValue(new Room(roomId, cli, cli.getUserId()!));
cli.pushRules!.global = {
override: [
{
rule_id: ".m.rule.roomnotif",
@@ -79,7 +79,7 @@ describe("pillify", () => {
const containers: Element[] = [];
pillifyLinks([container], event, containers);
expect(containers).toHaveLength(1);
expect(container.querySelector(".mx_Pill.mx_AtRoomPill").textContent).toBe("!@room");
expect(container.querySelector(".mx_Pill.mx_AtRoomPill")?.textContent).toBe("!@room");
});
it("should not double up pillification on repeated calls", () => {
@@ -90,6 +90,6 @@ describe("pillify", () => {
pillifyLinks([container], event, containers);
pillifyLinks([container], event, containers);
expect(containers).toHaveLength(1);
expect(container.querySelector(".mx_Pill.mx_AtRoomPill").textContent).toBe("!@room");
expect(container.querySelector(".mx_Pill.mx_AtRoomPill")?.textContent).toBe("!@room");
});
});

View File

@@ -21,7 +21,7 @@ import { createTestClient, mkEvent } from "../../test-utils";
describe("getRoomFunctionalMembers", () => {
const client = createTestClient();
const room = new Room("!room:example.com", client, client.getUserId());
const room = new Room("!room:example.com", client, client.getUserId()!);
it("should return an empty array if no functional members state event exists", () => {
expect(getFunctionalMembers(room)).toHaveLength(0);

View File

@@ -44,7 +44,7 @@ describe("tooltipify", () => {
expect(containers).toHaveLength(1);
const anchor = root.querySelector("a");
expect(anchor?.getAttribute("href")).toEqual("/foo");
const tooltip = anchor.querySelector(".mx_TextWithTooltip_target");
const tooltip = anchor!.querySelector(".mx_TextWithTooltip_target");
expect(tooltip).toBeDefined();
});
@@ -75,7 +75,7 @@ describe("tooltipify", () => {
expect(containers).toHaveLength(1);
const anchor = root.querySelector("a");
expect(anchor?.getAttribute("href")).toEqual("/foo");
const tooltip = anchor.querySelector(".mx_TextWithTooltip_target");
const tooltip = anchor!.querySelector(".mx_TextWithTooltip_target");
expect(tooltip).toBeDefined();
});
});