Prepare for repo merge
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
0f670b8dc0
commit
b084ff2313
807 changed files with 0 additions and 0 deletions
183
test/unit-tests/components/views/beacon/BeaconListItem-test.tsx
Normal file
183
test/unit-tests/components/views/beacon/BeaconListItem-test.tsx
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, fireEvent, render } from "jest-matrix-react";
|
||||
import { Beacon, RoomMember, MatrixEvent, LocationAssetType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
} from "../../../test-utils";
|
||||
|
||||
describe("<BeaconListItem />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// go back in time to create beacons and locations in the past
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now - 600000);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
|
||||
const aliceBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
const alicePinBeaconEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true, assetType: LocationAssetType.Pin, description: "Alice's car" },
|
||||
"$alice-room1-1",
|
||||
);
|
||||
const pinBeaconWithoutDescription = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true, assetType: LocationAssetType.Pin },
|
||||
"$alice-room1-1",
|
||||
);
|
||||
|
||||
const aliceLocation1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now - 1,
|
||||
});
|
||||
const aliceLocation2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconEvent.getId(),
|
||||
geoUri: "geo:52,42",
|
||||
timestamp: now - 500000,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
beacon: new Beacon(aliceBeaconEvent),
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) =>
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<BeaconListItem {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
const setupRoomWithBeacons = (beaconInfoEvents: MatrixEvent[], locationEvents?: MatrixEvent[]): Beacon[] => {
|
||||
const beacons = makeRoomWithBeacons(roomId, mockClient, beaconInfoEvents, locationEvents);
|
||||
|
||||
const member = new RoomMember(roomId, aliceId);
|
||||
member.name = `Alice`;
|
||||
const room = mockClient.getRoom(roomId)!;
|
||||
jest.spyOn(room, "getMember").mockReturnValue(member);
|
||||
|
||||
return beacons;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
});
|
||||
|
||||
it("renders null when beacon is not live", () => {
|
||||
const notLiveBeacon = makeBeaconInfoEvent(aliceId, roomId, { isLive: false });
|
||||
const [beacon] = setupRoomWithBeacons([notLiveBeacon]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it("renders null when beacon has no location", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("when a beacon is live and has locations", () => {
|
||||
it("renders beacon info", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { asFragment } = getComponent({ beacon });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("non-self beacons", () => {
|
||||
it("uses beacon description as beacon name", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent("Alice's car");
|
||||
});
|
||||
|
||||
it("uses beacon owner mxid as beacon name for a beacon without description", () => {
|
||||
const [beacon] = setupRoomWithBeacons([pinBeaconWithoutDescription], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent(aliceId);
|
||||
});
|
||||
|
||||
it("renders location icon", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector(".mx_StyledLiveBeaconIcon")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("self locations", () => {
|
||||
it("renders beacon owner avatar", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector(".mx_BaseAvatar")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("uses beacon owner name as beacon name", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation1]);
|
||||
const { container } = getComponent({ beacon });
|
||||
expect(container.querySelector(".mx_BeaconStatus_label")).toHaveTextContent("Alice");
|
||||
});
|
||||
});
|
||||
|
||||
describe("on location updates", () => {
|
||||
it("updates last updated time on location updated", () => {
|
||||
const [beacon] = setupRoomWithBeacons([aliceBeaconEvent], [aliceLocation2]);
|
||||
const { container } = getComponent({ beacon });
|
||||
|
||||
expect(container.querySelector(".mx_BeaconListItem_lastUpdated")).toHaveTextContent(
|
||||
"Updated 9 minutes ago",
|
||||
);
|
||||
|
||||
// update to a newer location
|
||||
act(() => {
|
||||
beacon.addLocations([aliceLocation1]);
|
||||
});
|
||||
|
||||
expect(container.querySelector(".mx_BeaconListItem_lastUpdated")).toHaveTextContent(
|
||||
"Updated a few seconds ago",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("interactions", () => {
|
||||
it("does not call onClick handler when clicking share button", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const onClick = jest.fn();
|
||||
const { getByTestId } = getComponent({ beacon, onClick });
|
||||
|
||||
fireEvent.click(getByTestId("open-location-in-osm"));
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls onClick handler when clicking outside of share buttons", () => {
|
||||
const [beacon] = setupRoomWithBeacons([alicePinBeaconEvent], [aliceLocation1]);
|
||||
const onClick = jest.fn();
|
||||
const { container } = getComponent({ beacon, onClick });
|
||||
|
||||
// click the beacon name
|
||||
fireEvent.click(container.querySelector(".mx_BeaconStatus_description")!);
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
136
test/unit-tests/components/views/beacon/BeaconMarker-test.tsx
Normal file
136
test/unit-tests/components/views/beacon/BeaconMarker-test.tsx
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, render, screen, waitFor } from "jest-matrix-react";
|
||||
import * as maplibregl from "maplibre-gl";
|
||||
import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import BeaconMarker from "../../../../src/components/views/beacon/BeaconMarker";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithStateEvents,
|
||||
} from "../../../test-utils";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
|
||||
describe("<BeaconMarker />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// stable date for snapshots
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
|
||||
const aliceMember = new RoomMember(roomId, aliceId);
|
||||
|
||||
const mapOptions = { container: {} as unknown as HTMLElement, style: "" };
|
||||
const mockMap = new maplibregl.Map(mapOptions);
|
||||
const mockMarker = new maplibregl.Marker();
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
|
||||
// make fresh rooms every time
|
||||
// as we update room state
|
||||
const setupRoom = (stateEvents: MatrixEvent[] = []): Room => {
|
||||
const room1 = makeRoomWithStateEvents(stateEvents, { roomId, mockClient });
|
||||
jest.spyOn(room1, "getMember").mockReturnValue(aliceMember);
|
||||
return room1;
|
||||
};
|
||||
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
const notLiveEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-2");
|
||||
|
||||
const geoUri1 = "geo:51,41";
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: geoUri1,
|
||||
timestamp: now + 1,
|
||||
});
|
||||
const geoUri2 = "geo:52,42";
|
||||
const location2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: geoUri2,
|
||||
timestamp: now + 10000,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
map: mockMap,
|
||||
beacon: new Beacon(defaultEvent),
|
||||
};
|
||||
|
||||
const renderComponent = (props = {}) => {
|
||||
const Wrapper = (wrapperProps = {}) => {
|
||||
return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
|
||||
};
|
||||
|
||||
return render(<BeaconMarker {...defaultProps} {...props} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders nothing when beacon is not live", () => {
|
||||
const room = setupRoom([notLiveEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(notLiveEvent));
|
||||
const { asFragment } = renderComponent({ beacon });
|
||||
expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
|
||||
expect(screen.queryByTestId("avatar-img")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders nothing when beacon has no location", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
const { asFragment } = renderComponent({ beacon });
|
||||
expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
|
||||
expect(screen.queryByTestId("avatar-img")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders marker when beacon has location", async () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon?.addLocations([location1]);
|
||||
const { asFragment } = renderComponent({ beacon });
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("avatar-img")).toBeInTheDocument();
|
||||
});
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("updates with new locations", () => {
|
||||
const lonLat1 = { lon: 41, lat: 51 };
|
||||
const lonLat2 = { lon: 42, lat: 52 };
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon?.addLocations([location1]);
|
||||
|
||||
// render the component then add a new location, check mockMarker called as expected
|
||||
renderComponent({ beacon });
|
||||
expect(mockMarker.setLngLat).toHaveBeenLastCalledWith(lonLat1);
|
||||
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||
|
||||
// add a location, check mockMarker called with new location details
|
||||
act(() => {
|
||||
beacon?.addLocations([location2]);
|
||||
});
|
||||
expect(mockMarker.setLngLat).toHaveBeenLastCalledWith(lonLat2);
|
||||
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { Beacon } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import BeaconStatus from "../../../../src/components/views/beacon/BeaconStatus";
|
||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
||||
import { makeBeaconInfoEvent } from "../../../test-utils";
|
||||
|
||||
describe("<BeaconStatus />", () => {
|
||||
const defaultProps = {
|
||||
displayStatus: BeaconDisplayStatus.Loading,
|
||||
label: "test label",
|
||||
withIcon: true,
|
||||
};
|
||||
const renderComponent = (props = {}) => render(<BeaconStatus {...defaultProps} {...props} />);
|
||||
|
||||
it("renders loading state", () => {
|
||||
const { asFragment } = renderComponent({ displayStatus: BeaconDisplayStatus.Loading });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders stopped state", () => {
|
||||
const { asFragment } = renderComponent({ displayStatus: BeaconDisplayStatus.Stopped });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders without icon", () => {
|
||||
const iconClassName = "mx_StyledLiveBeaconIcon";
|
||||
const { container } = renderComponent({ withIcon: false, displayStatus: BeaconDisplayStatus.Stopped });
|
||||
expect(container.getElementsByClassName(iconClassName)).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("active state", () => {
|
||||
it("renders without children", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
const { asFragment } = renderComponent({ beacon, displayStatus: BeaconDisplayStatus.Active });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders with children", () => {
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:sever", { isLive: false }));
|
||||
renderComponent({
|
||||
beacon,
|
||||
children: <span data-testid="test-child">test</span>,
|
||||
displayStatus: BeaconDisplayStatus.Active,
|
||||
});
|
||||
expect(screen.getByTestId("test-child")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders static remaining time when displayLiveTimeRemaining is falsy", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
renderComponent({ beacon, displayStatus: BeaconDisplayStatus.Active });
|
||||
expect(screen.getByText("Live until 11:17")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders live time remaining when displayLiveTimeRemaining is truthy", () => {
|
||||
// mock for stable snapshot
|
||||
jest.spyOn(Date, "now").mockReturnValue(123456789);
|
||||
const beacon = new Beacon(makeBeaconInfoEvent("@user:server", "!room:server", { isLive: false }, "$1"));
|
||||
renderComponent({
|
||||
beacon,
|
||||
displayStatus: BeaconDisplayStatus.Active,
|
||||
displayLiveTimeRemaining: true,
|
||||
});
|
||||
expect(screen.getByText("1h left")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, fireEvent, render, RenderResult, waitFor } from "jest-matrix-react";
|
||||
import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
import * as maplibregl from "maplibre-gl";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
makeRoomWithStateEvents,
|
||||
} from "../../../test-utils";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore";
|
||||
|
||||
describe("<BeaconViewDialog />", () => {
|
||||
// 14.03.2022 16:15
|
||||
const now = 1647270879403;
|
||||
// stable date for snapshots
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
const bobId = "@bob:server";
|
||||
|
||||
const aliceMember = new RoomMember(roomId, aliceId);
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
getUserId: jest.fn().mockReturnValue(bobId),
|
||||
getRoom: jest.fn(),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
getVisibleRooms: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
|
||||
const mapOptions = { container: {} as unknown as HTMLElement, style: "" };
|
||||
const mockMap = new maplibregl.Map(mapOptions);
|
||||
const mockMarker = new maplibregl.Marker();
|
||||
|
||||
// make fresh rooms every time
|
||||
// as we update room state
|
||||
const setupRoom = (stateEvents: MatrixEvent[] = []): Room => {
|
||||
const room1 = makeRoomWithStateEvents(stateEvents, { roomId, mockClient });
|
||||
jest.spyOn(room1, "getMember").mockReturnValue(aliceMember);
|
||||
|
||||
return room1;
|
||||
};
|
||||
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
onFinished: jest.fn(),
|
||||
roomId,
|
||||
matrixClient: mockClient as MatrixClient,
|
||||
};
|
||||
|
||||
const getComponent = (props = {}): RenderResult => render(<BeaconViewDialog {...defaultProps} {...props} />);
|
||||
|
||||
const openSidebar = (getByTestId: RenderResult["getByTestId"]) => {
|
||||
fireEvent.click(getByTestId("beacon-view-dialog-open-sidebar"));
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore();
|
||||
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockRestore();
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders a map with markers", async () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
getComponent();
|
||||
// centered on default event
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({
|
||||
lon: 41,
|
||||
lat: 51,
|
||||
});
|
||||
// marker added
|
||||
await waitFor(() => {
|
||||
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not render any own beacon status when user is not live sharing", () => {
|
||||
// default event belongs to alice, we are bob
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { queryByText } = getComponent();
|
||||
expect(queryByText("Live location enabled")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders own beacon status when user is live sharing", () => {
|
||||
// default event belongs to alice
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
// mock own beacon store to show default event as alice's live beacon
|
||||
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockReturnValue([beacon.identifier]);
|
||||
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockReturnValue(beacon);
|
||||
const { container } = getComponent();
|
||||
expect(container.querySelector(".mx_DialogOwnBeaconStatus")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("updates markers on changes to beacons", async () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { container } = getComponent();
|
||||
|
||||
// one marker
|
||||
expect(mockMarker.addTo).toHaveBeenCalledTimes(1);
|
||||
expect(container.getElementsByClassName("mx_Marker").length).toEqual(1);
|
||||
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||
act(() => {
|
||||
// emits RoomStateEvent.BeaconLiveness
|
||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||
const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!;
|
||||
beacon2.addLocations([location1]);
|
||||
});
|
||||
|
||||
// two markers now!
|
||||
expect(container.getElementsByClassName("mx_Marker").length).toEqual(2);
|
||||
});
|
||||
|
||||
it("does not update bounds or center on changing beacons", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { container } = getComponent();
|
||||
expect(container.getElementsByClassName("mx_Marker").length).toEqual(1);
|
||||
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||
act(() => {
|
||||
// emits RoomStateEvent.BeaconLiveness
|
||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||
const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!;
|
||||
beacon2.addLocations([location1]);
|
||||
});
|
||||
// called once on init
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("renders a fallback when there are no locations", () => {
|
||||
// this is a cornercase, should not be a reachable state in UI anymore
|
||||
const onFinished = jest.fn();
|
||||
const room = setupRoom([defaultEvent]);
|
||||
room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
const { getByTestId } = getComponent({ onFinished });
|
||||
|
||||
// map placeholder
|
||||
expect(getByTestId("beacon-view-dialog-map-fallback")).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByTestId("beacon-view-dialog-fallback-close"));
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders map without markers when no live beacons remain", () => {
|
||||
const onFinished = jest.fn();
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { container } = getComponent({ onFinished });
|
||||
expect(container.getElementsByClassName("mx_Marker").length).toEqual(1);
|
||||
|
||||
// this will replace the defaultEvent
|
||||
// leading to no more live beacons
|
||||
const anotherBeaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-2");
|
||||
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 41 });
|
||||
// reset call counts
|
||||
mocked(mockMap.setCenter).mockClear();
|
||||
mocked(mockMap.fitBounds).mockClear();
|
||||
|
||||
act(() => {
|
||||
// emits RoomStateEvent.BeaconLiveness
|
||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||
});
|
||||
|
||||
// no more avatars
|
||||
expect(container.getElementsByClassName("mx_Marker").length).toEqual(0);
|
||||
// map still rendered
|
||||
expect(container.querySelector("#mx_Map_mx_BeaconViewDialog")).toBeInTheDocument();
|
||||
// map location unchanged
|
||||
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
||||
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("sidebar", () => {
|
||||
it("opens sidebar on view list button click", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { container, getByTestId } = getComponent();
|
||||
|
||||
openSidebar(getByTestId);
|
||||
|
||||
expect(container.querySelector(".mx_DialogSidebar")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("closes sidebar on close button click", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||
beacon.addLocations([location1]);
|
||||
const { container, getByTestId } = getComponent();
|
||||
|
||||
// open the sidebar
|
||||
openSidebar(getByTestId);
|
||||
|
||||
expect(container.querySelector(".mx_DialogSidebar")).toBeInTheDocument();
|
||||
|
||||
// now close it
|
||||
fireEvent.click(getByTestId("dialog-sidebar-close"));
|
||||
|
||||
expect(container.querySelector(".mx_DialogSidebar")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("focused beacons", () => {
|
||||
const beacon2Event = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-2");
|
||||
|
||||
const location2 = makeBeaconEvent(bobId, {
|
||||
beaconInfoId: beacon2Event.getId(),
|
||||
geoUri: "geo:33,22",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
|
||||
const fitBoundsOptions = { maxZoom: 15, padding: 100 };
|
||||
|
||||
it("opens map with both beacons in view on first load without initialFocusedBeacon", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
getComponent({ beacons: [beacon1, beacon2] });
|
||||
|
||||
// start centered on mid point between both beacons
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 42, lon: 31.5 });
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fit both beacons, only called once
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([22, 33], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("opens map with both beacons in view on first load with an initially focused beacon", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
getComponent({ beacons: [beacon1, beacon2], initialFocusedBeacon: beacon1 });
|
||||
|
||||
// start centered on initialFocusedBeacon
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 41 });
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fit both beacons, only called once
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([22, 33], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("focuses on beacon location on sidebar list item click", () => {
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] });
|
||||
|
||||
// reset call counts on map mocks after initial render
|
||||
jest.clearAllMocks();
|
||||
|
||||
openSidebar(getByTestId);
|
||||
|
||||
act(() => {
|
||||
const listItems = container.querySelectorAll(".mx_BeaconListItem");
|
||||
// click on the first beacon in the list
|
||||
fireEvent.click(listItems[0]!);
|
||||
});
|
||||
|
||||
// centered on clicked beacon
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 41 });
|
||||
// only called once
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||
// bounds fitted just to clicked beacon
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(
|
||||
new maplibregl.LngLatBounds([41, 51], [41, 51]),
|
||||
fitBoundsOptions,
|
||||
);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("refocuses on same beacon when clicking list item again", () => {
|
||||
// test the map responds to refocusing the same beacon
|
||||
const [beacon1, beacon2] = makeRoomWithBeacons(
|
||||
roomId,
|
||||
mockClient,
|
||||
[defaultEvent, beacon2Event],
|
||||
[location1, location2],
|
||||
);
|
||||
|
||||
const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] });
|
||||
|
||||
// reset call counts on map mocks after initial render
|
||||
jest.clearAllMocks();
|
||||
|
||||
openSidebar(getByTestId);
|
||||
|
||||
act(() => {
|
||||
// click on the second beacon in the list
|
||||
const listItems = container.querySelectorAll(".mx_BeaconListItem");
|
||||
fireEvent.click(listItems[1]!);
|
||||
});
|
||||
|
||||
const expectedBounds = new maplibregl.LngLatBounds([22, 33], [22, 33]);
|
||||
|
||||
// date is mocked but this relies on timestamp, manually mock a tick
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now + 1);
|
||||
|
||||
act(() => {
|
||||
// click on the second beacon in the list
|
||||
const listItems = container.querySelectorAll(".mx_BeaconListItem");
|
||||
fireEvent.click(listItems[1]!);
|
||||
});
|
||||
|
||||
// centered on clicked beacon
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 33, lon: 22 });
|
||||
// bounds fitted just to clicked beacon
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledWith(expectedBounds, fitBoundsOptions);
|
||||
// each called once per click
|
||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(2);
|
||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { ComponentProps } from "react";
|
||||
import { act, fireEvent, render } from "jest-matrix-react";
|
||||
|
||||
import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../test-utils";
|
||||
|
||||
describe("<DialogSidebar />", () => {
|
||||
const defaultProps: ComponentProps<typeof DialogSidebar> = {
|
||||
beacons: [],
|
||||
requestClose: jest.fn(),
|
||||
onBeaconClick: jest.fn(),
|
||||
};
|
||||
|
||||
const now = 1647270879403;
|
||||
|
||||
const roomId = "!room:server.org";
|
||||
const aliceId = "@alice:server.org";
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
|
||||
const beaconEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true, timestamp: now }, "$alice-room1-1");
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: beaconEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now,
|
||||
});
|
||||
|
||||
const getComponent = (props = {}) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<DialogSidebar {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
// mock now so time based text in snapshots is stable
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(Date, "now").mockRestore();
|
||||
});
|
||||
|
||||
it("renders sidebar correctly without beacons", () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders sidebar correctly with beacons", () => {
|
||||
const [beacon] = makeRoomWithBeacons(roomId, client, [beaconEvent], [location1]);
|
||||
const { container } = render(getComponent({ beacons: [beacon] }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("calls on beacon click", () => {
|
||||
const onBeaconClick = jest.fn();
|
||||
const [beacon] = makeRoomWithBeacons(roomId, client, [beaconEvent], [location1]);
|
||||
const { container } = render(getComponent({ beacons: [beacon], onBeaconClick }));
|
||||
|
||||
act(() => {
|
||||
const [listItem] = container.getElementsByClassName("mx_BeaconListItem");
|
||||
fireEvent.click(listItem);
|
||||
});
|
||||
|
||||
expect(onBeaconClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("closes on close button click", () => {
|
||||
const requestClose = jest.fn();
|
||||
const { getByTestId } = render(getComponent({ requestClose }));
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByTestId("dialog-sidebar-close"));
|
||||
});
|
||||
expect(requestClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { act, fireEvent, render } from "jest-matrix-react";
|
||||
import { Beacon, BeaconIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import LeftPanelLiveShareWarning from "../../../../src/components/views/beacon/LeftPanelLiveShareWarning";
|
||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
|
||||
import { flushPromises, makeBeaconInfoEvent } from "../../../test-utils";
|
||||
import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../../../../src/stores/OwnBeaconStore", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const EventEmitter = require("events");
|
||||
class MockOwnBeaconStore extends EventEmitter {
|
||||
public getLiveBeaconIdsWithLocationPublishError = jest.fn().mockReturnValue([]);
|
||||
public getBeaconById = jest.fn();
|
||||
public getLiveBeaconIds = jest.fn().mockReturnValue([]);
|
||||
public readonly beaconUpdateErrors = new Map<BeaconIdentifier, Error>();
|
||||
public readonly beacons = new Map<BeaconIdentifier, Beacon>();
|
||||
}
|
||||
return {
|
||||
// @ts-ignore
|
||||
...jest.requireActual("../../../../src/stores/OwnBeaconStore"),
|
||||
OwnBeaconStore: {
|
||||
instance: new MockOwnBeaconStore() as unknown as OwnBeaconStore,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("<LeftPanelLiveShareWarning />", () => {
|
||||
const getComponent = (props = {}) => render(<LeftPanelLiveShareWarning {...props} />);
|
||||
|
||||
const roomId1 = "!room1:server";
|
||||
const roomId2 = "!room2:server";
|
||||
const aliceId = "@alive:server";
|
||||
|
||||
const now = 1647270879403;
|
||||
const HOUR_MS = 3600000;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
jest.spyOn(dispatcher, "dispatch")
|
||||
.mockClear()
|
||||
.mockImplementation(() => {});
|
||||
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.clear();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(global.Date, "now").mockRestore();
|
||||
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
// 12h old, 12h left
|
||||
const beacon1 = new Beacon(
|
||||
makeBeaconInfoEvent(aliceId, roomId1, { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, "$1"),
|
||||
);
|
||||
// 10h left
|
||||
const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId, roomId2, { timeout: HOUR_MS * 10, timestamp: now }, "$2"));
|
||||
|
||||
it("renders nothing when user has no live beacons", () => {
|
||||
const { container } = getComponent();
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("when user has live location monitor", () => {
|
||||
beforeAll(() => {
|
||||
mocked(OwnBeaconStore.instance).getBeaconById.mockImplementation((beaconId) => {
|
||||
if (beaconId === beacon1.identifier) {
|
||||
return beacon1;
|
||||
}
|
||||
return beacon2;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore writing to readonly variable
|
||||
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = true;
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([]);
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIds.mockReturnValue([beacon2.identifier, beacon1.identifier]);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(document, "addEventListener").mockRestore();
|
||||
});
|
||||
|
||||
it("renders correctly when not minimized", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("goes to room of latest beacon when clicked", () => {
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
metricsTrigger: undefined,
|
||||
// latest beacon's room
|
||||
room_id: roomId2,
|
||||
event_id: beacon2.beaconInfoId,
|
||||
highlighted: true,
|
||||
scroll_into_view: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("renders correctly when minimized", () => {
|
||||
const { asFragment } = getComponent({ isMinimized: true });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders location publish error", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("goes to room of latest beacon with location publish error when clicked", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
metricsTrigger: undefined,
|
||||
// error beacon's room
|
||||
room_id: roomId1,
|
||||
event_id: beacon1.beaconInfoId,
|
||||
highlighted: true,
|
||||
scroll_into_view: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("goes back to default style when wire errors are cleared", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
const { container, rerender } = getComponent();
|
||||
// error mode
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning")?.textContent).toEqual(
|
||||
"An error occurred whilst sharing your live location",
|
||||
);
|
||||
|
||||
act(() => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([]);
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.LocationPublishError, "abc");
|
||||
});
|
||||
|
||||
rerender(<LeftPanelLiveShareWarning />);
|
||||
|
||||
// default mode
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning")?.textContent).toEqual(
|
||||
"You are sharing your live location",
|
||||
);
|
||||
});
|
||||
|
||||
it("removes itself when user stops having live beacons", async () => {
|
||||
const { container, rerender } = getComponent({ isMinimized: true });
|
||||
// started out rendered
|
||||
expect(container.innerHTML).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
// @ts-ignore writing to readonly variable
|
||||
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = false;
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
rerender(<LeftPanelLiveShareWarning />);
|
||||
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it("refreshes beacon liveness monitors when pagevisibilty changes to visible", () => {
|
||||
OwnBeaconStore.instance.beacons.set(beacon1.identifier, beacon1);
|
||||
OwnBeaconStore.instance.beacons.set(beacon2.identifier, beacon2);
|
||||
const beacon1MonitorSpy = jest.spyOn(beacon1, "monitorLiveness");
|
||||
const beacon2MonitorSpy = jest.spyOn(beacon1, "monitorLiveness");
|
||||
|
||||
jest.spyOn(document, "addEventListener").mockImplementation((_e, listener) =>
|
||||
(listener as EventListener)(new Event("")),
|
||||
);
|
||||
|
||||
expect(beacon1MonitorSpy).not.toHaveBeenCalled();
|
||||
|
||||
getComponent();
|
||||
|
||||
expect(beacon1MonitorSpy).toHaveBeenCalled();
|
||||
expect(beacon2MonitorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("stopping errors", () => {
|
||||
it("renders stopping error", () => {
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it("starts rendering stopping error on beaconUpdateError emit", () => {
|
||||
const { container } = getComponent();
|
||||
// no error
|
||||
expect(container.textContent).toEqual("You are sharing your live location");
|
||||
|
||||
act(() => {
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.BeaconUpdateError, beacon2.identifier, true);
|
||||
});
|
||||
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it("renders stopping error when beacons have stopping and location errors", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
expect(container.textContent).toEqual("An error occurred while stopping your live location");
|
||||
});
|
||||
|
||||
it("goes to room of latest beacon with stopping error when clicked", () => {
|
||||
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithLocationPublishError.mockReturnValue([
|
||||
beacon1.identifier,
|
||||
]);
|
||||
OwnBeaconStore.instance.beaconUpdateErrors.set(beacon2.identifier, new Error("error"));
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
metricsTrigger: undefined,
|
||||
// stopping error beacon's room
|
||||
room_id: beacon2.roomId,
|
||||
event_id: beacon2.beaconInfoId,
|
||||
highlighted: true,
|
||||
scroll_into_view: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
155
test/unit-tests/components/views/beacon/OwnBeaconStatus-test.tsx
Normal file
155
test/unit-tests/components/views/beacon/OwnBeaconStatus-test.tsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { Beacon } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import OwnBeaconStatus from "../../../../src/components/views/beacon/OwnBeaconStatus";
|
||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
||||
import { useOwnLiveBeacons } from "../../../../src/utils/beacon";
|
||||
import { makeBeaconInfoEvent } from "../../../test-utils";
|
||||
|
||||
jest.mock("../../../../src/utils/beacon/useOwnLiveBeacons", () => ({
|
||||
useOwnLiveBeacons: jest.fn(),
|
||||
}));
|
||||
|
||||
const defaultLiveBeaconsState = {
|
||||
onStopSharing: jest.fn(),
|
||||
onResetLocationPublishError: jest.fn(),
|
||||
stoppingInProgress: false,
|
||||
hasStopSharingError: false,
|
||||
hasLocationPublishError: false,
|
||||
};
|
||||
|
||||
describe("<OwnBeaconStatus />", () => {
|
||||
const defaultProps = {
|
||||
displayStatus: BeaconDisplayStatus.Loading,
|
||||
};
|
||||
const userId = "@user:server";
|
||||
const roomId = "!room:server";
|
||||
let defaultBeacon: Beacon;
|
||||
const renderComponent = (props: Partial<React.ComponentProps<typeof OwnBeaconStatus>> = {}) =>
|
||||
render(<OwnBeaconStatus {...defaultProps} {...props} />);
|
||||
const getRetryButton = () => screen.getByRole("button", { name: "Retry" });
|
||||
const getStopButton = () => screen.getByRole("button", { name: "Stop" });
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(123456789);
|
||||
mocked(useOwnLiveBeacons).mockClear().mockReturnValue(defaultLiveBeaconsState);
|
||||
|
||||
defaultBeacon = new Beacon(makeBeaconInfoEvent(userId, roomId));
|
||||
});
|
||||
|
||||
it("renders without a beacon instance", () => {
|
||||
const { asFragment } = renderComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("Active state", () => {
|
||||
it("renders stop button", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
onStopSharing: jest.fn(),
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(screen.getByText("Live location enabled")).toBeInTheDocument();
|
||||
expect(getStopButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("stops sharing on stop button click", async () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onStopSharing = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
onStopSharing,
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
await userEvent.click(getStopButton());
|
||||
expect(onStopSharing).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
it("renders in error mode when displayStatus is error", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Error;
|
||||
renderComponent({ displayStatus });
|
||||
expect(screen.getByText("Live location error")).toBeInTheDocument();
|
||||
|
||||
// no actions for plain error
|
||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("with location publish error", () => {
|
||||
it("renders in error mode", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
hasLocationPublishError: true,
|
||||
onResetLocationPublishError: jest.fn(),
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(screen.getByText("Live location error")).toBeInTheDocument();
|
||||
// retry button
|
||||
expect(getRetryButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("retry button resets location publish error", async () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onResetLocationPublishError = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
hasLocationPublishError: true,
|
||||
onResetLocationPublishError,
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
await userEvent.click(getRetryButton());
|
||||
|
||||
expect(onResetLocationPublishError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("with stopping error", () => {
|
||||
it("renders in error mode", () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
hasLocationPublishError: false,
|
||||
hasStopSharingError: true,
|
||||
onStopSharing: jest.fn(),
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
expect(screen.getByText("Live location error")).toBeInTheDocument();
|
||||
// retry button
|
||||
expect(getRetryButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("retry button retries stop sharing", async () => {
|
||||
const displayStatus = BeaconDisplayStatus.Active;
|
||||
const onStopSharing = jest.fn();
|
||||
mocked(useOwnLiveBeacons).mockReturnValue({
|
||||
...defaultLiveBeaconsState,
|
||||
hasStopSharingError: true,
|
||||
onStopSharing,
|
||||
});
|
||||
renderComponent({ displayStatus, beacon: defaultBeacon });
|
||||
await userEvent.click(getRetryButton());
|
||||
|
||||
expect(onStopSharing).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("renders loading state correctly", () => {
|
||||
const component = renderComponent();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
118
test/unit-tests/components/views/beacon/RoomCallBanner-test.tsx
Normal file
118
test/unit-tests/components/views/beacon/RoomCallBanner-test.tsx
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Room, PendingEventOrdering, MatrixClient, RoomMember, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { ClientWidgetApi, Widget } from "matrix-widget-api";
|
||||
import { act, cleanup, render, screen } from "jest-matrix-react";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
|
||||
import { mkRoomMember, MockedCall, setupAsyncStoreWithClient, stubClient, useMockedCalls } from "../../../test-utils";
|
||||
import RoomCallBanner from "../../../../src/components/views/beacon/RoomCallBanner";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { ConnectionState } from "../../../../src/models/Call";
|
||||
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||
|
||||
describe("<RoomCallBanner />", () => {
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
let alice: RoomMember;
|
||||
useMockedCalls();
|
||||
|
||||
const defaultProps = {
|
||||
roomId: "!1:example.org",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
|
||||
client = mocked(MatrixClientPeg.safeGet());
|
||||
|
||||
room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null));
|
||||
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
|
||||
});
|
||||
|
||||
const renderBanner = async (props = {}): Promise<void> => {
|
||||
render(<RoomCallBanner {...defaultProps} {...props} />);
|
||||
await act(() => Promise.resolve()); // Let effects settle
|
||||
};
|
||||
|
||||
it("renders nothing when there is no call", async () => {
|
||||
await renderBanner();
|
||||
const banner = await screen.queryByText("Video call");
|
||||
expect(banner).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("call started", () => {
|
||||
let call: MockedCall;
|
||||
let widget: Widget;
|
||||
|
||||
beforeEach(() => {
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
if (!(maybeCall instanceof MockedCall)) {
|
||||
throw new Error("Failed to create call");
|
||||
}
|
||||
call = maybeCall;
|
||||
|
||||
widget = new Widget(call.widget);
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||
stop: () => {},
|
||||
} as unknown as ClientWidgetApi);
|
||||
});
|
||||
afterEach(() => {
|
||||
cleanup(); // Unmount before we do any cleanup that might update the component
|
||||
call.destroy();
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
});
|
||||
|
||||
it("renders if there is a call", async () => {
|
||||
await renderBanner();
|
||||
await screen.findByText("Video call");
|
||||
});
|
||||
|
||||
it("shows Join button if the user has not joined", async () => {
|
||||
await renderBanner();
|
||||
await screen.findByText("Join");
|
||||
});
|
||||
|
||||
it("doesn't show banner if the call is connected", async () => {
|
||||
call.setConnectionState(ConnectionState.Connected);
|
||||
await renderBanner();
|
||||
const banner = await screen.queryByText("Video call");
|
||||
expect(banner).toBeFalsy();
|
||||
});
|
||||
|
||||
it("doesn't show banner if the call is shown", async () => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall");
|
||||
mocked(SdkContextClass.instance.roomViewStore.isViewingCall).mockReturnValue(true);
|
||||
await renderBanner();
|
||||
const banner = await screen.queryByText("Video call");
|
||||
expect(banner).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: test clicking buttons
|
||||
// TODO: add live location share warning test (should not render if there is an active live location share)
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render } from "jest-matrix-react";
|
||||
|
||||
import ShareLatestLocation from "../../../../src/components/views/beacon/ShareLatestLocation";
|
||||
import { copyPlaintext } from "../../../../src/utils/strings";
|
||||
import { flushPromises } from "../../../test-utils";
|
||||
|
||||
jest.mock("../../../../src/utils/strings", () => ({
|
||||
copyPlaintext: jest.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
describe("<ShareLatestLocation />", () => {
|
||||
const defaultProps = {
|
||||
latestLocationState: {
|
||||
uri: "geo:51,42;u=35",
|
||||
timestamp: 123,
|
||||
},
|
||||
};
|
||||
const getComponent = (props = {}) => render(<ShareLatestLocation {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders null when no location", () => {
|
||||
const { container } = getComponent({ latestLocationState: undefined });
|
||||
expect(container.innerHTML).toBeFalsy();
|
||||
});
|
||||
|
||||
it("renders share buttons when there is a location", async () => {
|
||||
const { container, asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(container.querySelector(".mx_CopyableText_copyButton")!);
|
||||
await flushPromises();
|
||||
|
||||
expect(copyPlaintext).toHaveBeenCalledWith("51,42");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
|
||||
import StyledLiveBeaconIcon from "../../../../src/components/views/beacon/StyledLiveBeaconIcon";
|
||||
|
||||
describe("<StyledLiveBeaconIcon />", () => {
|
||||
const defaultProps = {};
|
||||
const getComponent = (props = {}) => render(<StyledLiveBeaconIcon {...defaultProps} {...props} />);
|
||||
|
||||
it("renders", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BeaconListItem /> when a beacon is live and has locations renders beacon info 1`] = `
|
||||
<DocumentFragment>
|
||||
<li
|
||||
class="mx_BeaconListItem"
|
||||
>
|
||||
<div
|
||||
class="mx_StyledLiveBeaconIcon mx_BeaconListItem_avatarIcon"
|
||||
/>
|
||||
<div
|
||||
class="mx_BeaconListItem_info"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Active mx_BeaconListItem_status"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_label"
|
||||
>
|
||||
Alice's car
|
||||
</span>
|
||||
<span
|
||||
class="mx_BeaconStatus_expiryTime"
|
||||
>
|
||||
Live until 16:04
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BeaconListItem_interactions"
|
||||
>
|
||||
<a
|
||||
aria-labelledby="floating-ui-1"
|
||||
data-testid="open-location-in-osm"
|
||||
href="https://www.openstreetmap.org/?mlat=51&mlon=41#map=16/51/41"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareLatestLocation_icon"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="mx_CopyableText mx_ShareLatestLocation_copy"
|
||||
>
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mx_BeaconListItem_lastUpdated"
|
||||
>
|
||||
Updated a few seconds ago
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,28 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
|
||||
<DocumentFragment>
|
||||
<span>
|
||||
<div
|
||||
class="mx_Marker mx_Username_color6"
|
||||
id="!room:server_@alice:server"
|
||||
>
|
||||
<div
|
||||
class="mx_Marker_border"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="6"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 36px;"
|
||||
title="@alice:server"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,77 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BeaconStatus /> active state renders with children 1`] = `
|
||||
<span
|
||||
data-testid="test-child"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`<BeaconStatus /> active state renders without children 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Active"
|
||||
>
|
||||
<div
|
||||
class="mx_StyledLiveBeaconIcon mx_BeaconStatus_icon"
|
||||
/>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_label"
|
||||
>
|
||||
test label
|
||||
</span>
|
||||
<span
|
||||
class="mx_BeaconStatus_expiryTime"
|
||||
>
|
||||
Live until 11:17
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<BeaconStatus /> renders loading state 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Loading"
|
||||
>
|
||||
<div
|
||||
class="mx_StyledLiveBeaconIcon mx_BeaconStatus_icon mx_StyledLiveBeaconIcon_idle"
|
||||
/>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_description_status"
|
||||
>
|
||||
Loading live location…
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<BeaconStatus /> renders stopped state 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Stopped"
|
||||
>
|
||||
<div
|
||||
class="mx_StyledLiveBeaconIcon mx_BeaconStatus_icon mx_StyledLiveBeaconIcon_idle"
|
||||
/>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_description_status"
|
||||
>
|
||||
Live location ended
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,73 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BeaconViewDialog /> renders a fallback when there are no locations 1`] = `
|
||||
<div
|
||||
class="mx_MapFallback mx_BeaconViewDialog_map"
|
||||
data-testid="beacon-view-dialog-map-fallback"
|
||||
>
|
||||
<div
|
||||
class="mx_MapFallback_bg"
|
||||
/>
|
||||
<div
|
||||
class="mx_MapFallback_icon"
|
||||
/>
|
||||
<span
|
||||
class="mx_BeaconViewDialog_mapFallbackMessage"
|
||||
>
|
||||
No live locations
|
||||
</span>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
data-testid="beacon-view-dialog-fallback-close"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Close
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<BeaconViewDialog /> renders own beacon status when user is live sharing 1`] = `
|
||||
<div
|
||||
class="mx_DialogOwnBeaconStatus"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_DialogOwnBeaconStatus_avatar _avatar-imageless_mcap2_61"
|
||||
data-color="6"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
title="@alice:server"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Active mx_DialogOwnBeaconStatus_status"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_label"
|
||||
>
|
||||
Live location enabled
|
||||
</span>
|
||||
<span
|
||||
class="mx_LiveTimeRemaining"
|
||||
data-testid="room-live-share-expiry"
|
||||
>
|
||||
1h left
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_OwnBeaconStatus_button mx_OwnBeaconStatus_destructiveButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
data-testid="beacon-status-stop-beacon"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Stop
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,152 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<DialogSidebar /> renders sidebar correctly with beacons 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_DialogSidebar"
|
||||
>
|
||||
<div
|
||||
class="mx_DialogSidebar_header"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
View list
|
||||
</h4>
|
||||
<div
|
||||
aria-label="Close sidebar"
|
||||
class="mx_AccessibleButton mx_DialogSidebar_closeButton"
|
||||
data-testid="dialog-sidebar-close"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
class="mx_DialogSidebar_closeButtonIcon"
|
||||
fill="currentColor"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<ol
|
||||
class="mx_DialogSidebar_list"
|
||||
>
|
||||
<li
|
||||
class="mx_BeaconListItem"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_BeaconListItem_avatar _avatar-imageless_mcap2_61"
|
||||
data-color="1"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
|
||||
</span>
|
||||
<div
|
||||
class="mx_BeaconListItem_info"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Active mx_BeaconListItem_status"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_label"
|
||||
>
|
||||
@alice:server.org
|
||||
</span>
|
||||
<span
|
||||
class="mx_BeaconStatus_expiryTime"
|
||||
>
|
||||
Live until 16:14
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BeaconListItem_interactions"
|
||||
>
|
||||
<a
|
||||
aria-labelledby="floating-ui-8"
|
||||
data-testid="open-location-in-osm"
|
||||
href="https://www.openstreetmap.org/?mlat=51&mlon=41#map=16/51/41"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareLatestLocation_icon"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="mx_CopyableText mx_ShareLatestLocation_copy"
|
||||
>
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mx_BeaconListItem_lastUpdated"
|
||||
>
|
||||
Updated a few seconds ago
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<DialogSidebar /> renders sidebar correctly without beacons 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_DialogSidebar"
|
||||
>
|
||||
<div
|
||||
class="mx_DialogSidebar_header"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
View list
|
||||
</h4>
|
||||
<div
|
||||
aria-label="Close sidebar"
|
||||
class="mx_AccessibleButton mx_DialogSidebar_closeButton"
|
||||
data-testid="dialog-sidebar-close"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
class="mx_DialogSidebar_closeButtonIcon"
|
||||
fill="currentColor"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DialogSidebar_noResults"
|
||||
>
|
||||
No live locations
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,40 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders correctly when minimized 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-label="You are sharing your live location"
|
||||
class="mx_AccessibleButton mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__minimized"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
height="10"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders correctly when not minimized 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LeftPanelLiveShareWarning"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
You are sharing your live location
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders location publish error 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__error"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
An error occurred whilst sharing your live location
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,19 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<OwnBeaconStatus /> renders without a beacon instance 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BeaconStatus mx_BeaconStatus_Loading"
|
||||
>
|
||||
<div
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_description_status"
|
||||
>
|
||||
Loading live location…
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,27 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<ShareLatestLocation /> renders share buttons when there is a location 1`] = `
|
||||
<DocumentFragment>
|
||||
<a
|
||||
aria-labelledby="floating-ui-1"
|
||||
data-testid="open-location-in-osm"
|
||||
href="https://www.openstreetmap.org/?mlat=51&mlon=42#map=16/51/42"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareLatestLocation_icon"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="mx_CopyableText mx_ShareLatestLocation_copy"
|
||||
>
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<StyledLiveBeaconIcon /> renders 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_StyledLiveBeaconIcon"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
Loading…
Add table
Add a link
Reference in a new issue