Merge branch 'develop' into weeman1337/mute-broadcast-notifications
This commit is contained in:
@@ -15,11 +15,10 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
import { screen, render, RenderResult } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import PictureInPictureDragger, {
|
||||
CreatePipChildren,
|
||||
} from "../../../../src/components/views/voip/PictureInPictureDragger";
|
||||
import PictureInPictureDragger, { CreatePipChildren } from "../../../src/components/structures/PictureInPictureDragger";
|
||||
|
||||
describe("PictureInPictureDragger", () => {
|
||||
let renderResult: RenderResult;
|
||||
@@ -82,4 +81,29 @@ describe("PictureInPictureDragger", () => {
|
||||
expect(renderResult.container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't leak drag events to children as clicks", async () => {
|
||||
const clickSpy = jest.fn();
|
||||
render(
|
||||
<PictureInPictureDragger draggable={true}>
|
||||
{[
|
||||
({ onStartMoving }) => (
|
||||
<div onMouseDown={onStartMoving} onClick={clickSpy}>
|
||||
Hello
|
||||
</div>
|
||||
),
|
||||
]}
|
||||
</PictureInPictureDragger>,
|
||||
);
|
||||
const target = screen.getByText("Hello");
|
||||
|
||||
// A click without a drag motion should go through
|
||||
await userEvent.pointer([{ keys: "[MouseLeft>]", target }, { keys: "[/MouseLeft]" }]);
|
||||
expect(clickSpy).toHaveBeenCalled();
|
||||
|
||||
// A drag motion should not trigger a click
|
||||
clickSpy.mockClear();
|
||||
await userEvent.pointer([{ keys: "[MouseLeft>]", target }, { coords: { x: 60, y: 60 } }, "[/MouseLeft]"]);
|
||||
expect(clickSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -16,12 +16,14 @@ limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
import { screen, render, act, cleanup, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { screen, render, act, cleanup } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||
import { Widget, ClientWidgetApi } from "matrix-widget-api";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup";
|
||||
|
||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import {
|
||||
@@ -34,18 +36,19 @@ import {
|
||||
wrapInMatrixClientContext,
|
||||
wrapInSdkContext,
|
||||
mkRoomCreateEvent,
|
||||
mockPlatformPeg,
|
||||
flushPromises,
|
||||
} from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import UnwrappedPipView from "../../../../src/components/views/voip/PipView";
|
||||
import ActiveWidgetStore from "../../../../src/stores/ActiveWidgetStore";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { TestSdkContext } from "../../../TestSdkContext";
|
||||
} from "../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import { CallStore } from "../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { PipContainer as UnwrappedPipContainer } from "../../../src/components/structures/PipContainer";
|
||||
import ActiveWidgetStore from "../../../src/stores/ActiveWidgetStore";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import { ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { TestSdkContext } from "../../TestSdkContext";
|
||||
import {
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastPlaybacksStore,
|
||||
@@ -53,15 +56,21 @@ import {
|
||||
VoiceBroadcastPreRecordingStore,
|
||||
VoiceBroadcastRecording,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
} from "../../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { RoomViewStore } from "../../../../src/stores/RoomViewStore";
|
||||
import { IRoomStateEventsActionPayload } from "../../../../src/actions/MatrixActionCreators";
|
||||
} from "../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../voice-broadcast/utils/test-utils";
|
||||
import { RoomViewStore } from "../../../src/stores/RoomViewStore";
|
||||
import { IRoomStateEventsActionPayload } from "../../../src/actions/MatrixActionCreators";
|
||||
import { Container, WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import WidgetStore from "../../../src/stores/WidgetStore";
|
||||
import { WidgetType } from "../../../src/widgets/WidgetType";
|
||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||
import { ElementWidgetActions } from "../../../src/stores/widgets/ElementWidgetActions";
|
||||
|
||||
describe("PipView", () => {
|
||||
describe("PipContainer", () => {
|
||||
useMockedCalls();
|
||||
jest.spyOn(HTMLMediaElement.prototype, "play").mockImplementation(async () => {});
|
||||
|
||||
let user: UserEvent;
|
||||
let sdkContext: TestSdkContext;
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
@@ -78,6 +87,8 @@ describe("PipView", () => {
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
user = userEvent.setup();
|
||||
|
||||
stubClient();
|
||||
client = mocked(MatrixClientPeg.get());
|
||||
DMRoomMap.makeShared();
|
||||
@@ -110,6 +121,8 @@ describe("PipView", () => {
|
||||
);
|
||||
|
||||
sdkContext = new TestSdkContext();
|
||||
// @ts-ignore PipContainer uses SDKContext in the constructor
|
||||
SdkContextClass.instance = sdkContext;
|
||||
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
||||
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
||||
voiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(voiceBroadcastRecordingsStore);
|
||||
@@ -127,11 +140,11 @@ describe("PipView", () => {
|
||||
});
|
||||
|
||||
const renderPip = () => {
|
||||
const PipView = wrapInMatrixClientContext(wrapInSdkContext(UnwrappedPipView, sdkContext));
|
||||
render(<PipView />);
|
||||
const PipContainer = wrapInMatrixClientContext(wrapInSdkContext(UnwrappedPipContainer, sdkContext));
|
||||
render(<PipContainer />);
|
||||
};
|
||||
|
||||
const viewRoom = (roomId: string) =>
|
||||
const viewRoom = (roomId: string) => {
|
||||
defaultDispatcher.dispatch<ViewRoomPayload>(
|
||||
{
|
||||
action: Action.ViewRoom,
|
||||
@@ -140,8 +153,9 @@ describe("PipView", () => {
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
const withCall = async (fn: () => Promise<void>): Promise<void> => {
|
||||
const withCall = async (fn: (call: MockedCall) => Promise<void>): Promise<void> => {
|
||||
MockedCall.create(room, "1");
|
||||
const call = CallStore.instance.getCall(room.roomId);
|
||||
if (!(call instanceof MockedCall)) throw new Error("Failed to create call");
|
||||
@@ -156,16 +170,16 @@ describe("PipView", () => {
|
||||
ActiveWidgetStore.instance.setWidgetPersistence(widget.id, room.roomId, true);
|
||||
});
|
||||
|
||||
await fn();
|
||||
await fn(call);
|
||||
|
||||
cleanup();
|
||||
call.destroy();
|
||||
ActiveWidgetStore.instance.destroyPersistentWidget(widget.id, room.roomId);
|
||||
};
|
||||
|
||||
const withWidget = (fn: () => void): void => {
|
||||
const withWidget = async (fn: () => Promise<void>): Promise<void> => {
|
||||
act(() => ActiveWidgetStore.instance.setWidgetPersistence("1", room.roomId, true));
|
||||
fn();
|
||||
await fn();
|
||||
cleanup();
|
||||
ActiveWidgetStore.instance.destroyPersistentWidget("1", room.roomId);
|
||||
};
|
||||
@@ -197,7 +211,7 @@ describe("PipView", () => {
|
||||
};
|
||||
|
||||
const setUpRoomViewStore = () => {
|
||||
new RoomViewStore(defaultDispatcher, sdkContext);
|
||||
sdkContext._RoomViewStore = new RoomViewStore(defaultDispatcher, sdkContext);
|
||||
};
|
||||
|
||||
const mkVoiceBroadcast = (room: Room): MatrixEvent => {
|
||||
@@ -220,54 +234,104 @@ describe("PipView", () => {
|
||||
expect(screen.queryByRole("complementary")).toBeNull();
|
||||
});
|
||||
|
||||
it("shows an active call with a maximise button", async () => {
|
||||
it("shows an active call with back and leave buttons", async () => {
|
||||
renderPip();
|
||||
|
||||
await withCall(async () => {
|
||||
await withCall(async (call) => {
|
||||
screen.getByRole("complementary");
|
||||
screen.getByText(room.roomId);
|
||||
expect(screen.queryByRole("button", { name: "Pin" })).toBeNull();
|
||||
expect(screen.queryByRole("button", { name: /return/i })).toBeNull();
|
||||
|
||||
// The maximise button should jump to the call
|
||||
// The return button should jump to the call
|
||||
const dispatcherSpy = jest.fn();
|
||||
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Fill screen" }));
|
||||
await waitFor(() =>
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
}),
|
||||
);
|
||||
await user.click(screen.getByRole("button", { name: "Back" }));
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
metricsTrigger: expect.any(String),
|
||||
});
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
|
||||
// The leave button should disconnect from the call
|
||||
const disconnectSpy = jest.spyOn(call, "disconnect");
|
||||
await user.click(screen.getByRole("button", { name: "Leave" }));
|
||||
expect(disconnectSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows a persistent widget with pin and maximise buttons when viewing the room", () => {
|
||||
it("shows a persistent widget with back button when viewing the room", async () => {
|
||||
setUpRoomViewStore();
|
||||
viewRoom(room.roomId);
|
||||
const widget = WidgetStore.instance.addVirtualWidget(
|
||||
{
|
||||
id: "1",
|
||||
creatorUserId: "@alice:exaxmple.org",
|
||||
type: WidgetType.CUSTOM.preferred,
|
||||
url: "https://example.org",
|
||||
name: "Example widget",
|
||||
},
|
||||
room.roomId,
|
||||
);
|
||||
renderPip();
|
||||
|
||||
withWidget(() => {
|
||||
await withWidget(async () => {
|
||||
screen.getByRole("complementary");
|
||||
screen.getByText(room.roomId);
|
||||
screen.getByRole("button", { name: "Pin" });
|
||||
screen.getByRole("button", { name: "Fill screen" });
|
||||
expect(screen.queryByRole("button", { name: /return/i })).toBeNull();
|
||||
|
||||
// The return button should maximize the widget
|
||||
const moveSpy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer");
|
||||
await user.click(screen.getByRole("button", { name: "Back" }));
|
||||
expect(moveSpy).toHaveBeenCalledWith(room, widget, Container.Center);
|
||||
|
||||
expect(screen.queryByRole("button", { name: "Leave" })).toBeNull();
|
||||
});
|
||||
|
||||
WidgetStore.instance.removeVirtualWidget("1", room.roomId);
|
||||
});
|
||||
|
||||
it("shows a persistent widget with a return button when not viewing the room", () => {
|
||||
it("shows a persistent Jitsi widget with back and leave buttons when not viewing the room", async () => {
|
||||
mockPlatformPeg({ supportsJitsiScreensharing: () => true });
|
||||
setUpRoomViewStore();
|
||||
viewRoom(room2.roomId);
|
||||
const widget = WidgetStore.instance.addVirtualWidget(
|
||||
{
|
||||
id: "1",
|
||||
creatorUserId: "@alice:exaxmple.org",
|
||||
type: WidgetType.JITSI.preferred,
|
||||
url: "https://meet.example.org",
|
||||
name: "Jitsi example",
|
||||
},
|
||||
room.roomId,
|
||||
);
|
||||
renderPip();
|
||||
|
||||
withWidget(() => {
|
||||
await withWidget(async () => {
|
||||
screen.getByRole("complementary");
|
||||
screen.getByText(room.roomId);
|
||||
expect(screen.queryByRole("button", { name: "Pin" })).toBeNull();
|
||||
expect(screen.queryByRole("button", { name: "Fill screen" })).toBeNull();
|
||||
screen.getByRole("button", { name: /return/i });
|
||||
|
||||
// The return button should view the room
|
||||
const dispatcherSpy = jest.fn();
|
||||
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
|
||||
await user.click(screen.getByRole("button", { name: "Back" }));
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
metricsTrigger: expect.any(String),
|
||||
});
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
|
||||
// The leave button should hangup the call
|
||||
const sendSpy = jest
|
||||
.fn<
|
||||
ReturnType<ClientWidgetApi["transport"]["send"]>,
|
||||
Parameters<ClientWidgetApi["transport"]["send"]>
|
||||
>()
|
||||
.mockResolvedValue({});
|
||||
const mockMessaging = { transport: { send: sendSpy }, stop: () => {} } as unknown as ClientWidgetApi;
|
||||
WidgetMessagingStore.instance.storeMessaging(new Widget(widget), room.roomId, mockMessaging);
|
||||
await user.click(screen.getByRole("button", { name: "Leave" }));
|
||||
expect(sendSpy).toHaveBeenCalledWith(ElementWidgetActions.HangupCall, {});
|
||||
});
|
||||
|
||||
WidgetStore.instance.removeVirtualWidget("1", room.roomId);
|
||||
});
|
||||
|
||||
describe("when there is a voice broadcast recording and pre-recording", () => {
|
||||
@@ -287,8 +351,8 @@ describe("PipView", () => {
|
||||
await withCall(async () => {
|
||||
// Broadcast: Check for the „Live“ badge to be present
|
||||
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||
// Call: Check for the „Fill screen“ button to be present
|
||||
expect(screen.queryByLabelText("Fill screen")).toBeInTheDocument();
|
||||
// Call: Check for the „Leave“ button to be present
|
||||
screen.getByRole("button", { name: "Leave" });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,8 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from "enzyme";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import * as maplibregl from "maplibre-gl";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||
@@ -43,6 +42,7 @@ describe("<BeaconMarker />", () => {
|
||||
|
||||
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({
|
||||
@@ -64,14 +64,16 @@ describe("<BeaconMarker />", () => {
|
||||
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: "geo:51,41",
|
||||
geoUri: geoUri1,
|
||||
timestamp: now + 1,
|
||||
});
|
||||
const geoUri2 = "geo:52,42";
|
||||
const location2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: defaultEvent.getId(),
|
||||
geoUri: "geo:52,42",
|
||||
geoUri: geoUri2,
|
||||
timestamp: now + 10000,
|
||||
});
|
||||
|
||||
@@ -80,11 +82,15 @@ describe("<BeaconMarker />", () => {
|
||||
beacon: new Beacon(defaultEvent),
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<BeaconMarker {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
const renderComponent = (props = {}) => {
|
||||
const Wrapper = (wrapperProps = {}) => {
|
||||
return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
|
||||
};
|
||||
|
||||
return render(<BeaconMarker {...defaultProps} {...props} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -93,38 +99,45 @@ describe("<BeaconMarker />", () => {
|
||||
it("renders nothing when beacon is not live", () => {
|
||||
const room = setupRoom([notLiveEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(notLiveEvent));
|
||||
const component = getComponent({ beacon });
|
||||
expect(component.html()).toBe(null);
|
||||
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 component = getComponent({ beacon });
|
||||
expect(component.html()).toBe(null);
|
||||
const { asFragment } = renderComponent({ beacon });
|
||||
expect(asFragment()).toMatchInlineSnapshot(`<DocumentFragment />`);
|
||||
expect(screen.queryByTestId("avatar-img")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders marker when beacon has location", () => {
|
||||
const room = setupRoom([defaultEvent]);
|
||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||
beacon.addLocations([location1]);
|
||||
const component = getComponent({ beacon });
|
||||
expect(component).toMatchSnapshot();
|
||||
beacon?.addLocations([location1]);
|
||||
const { asFragment } = renderComponent({ beacon });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
expect(screen.getByTestId("avatar-img")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
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]);
|
||||
const component = getComponent({ beacon });
|
||||
expect(component.find("SmartMarker").props()["geoUri"]).toEqual("geo:51,41");
|
||||
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]);
|
||||
beacon?.addLocations([location2]);
|
||||
});
|
||||
component.setProps({});
|
||||
|
||||
// updated to latest location
|
||||
expect(component.find("SmartMarker").props()["geoUri"]).toEqual("geo:52,42");
|
||||
expect(mockMarker.setLngLat).toHaveBeenLastCalledWith(lonLat2);
|
||||
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,240 +1,38 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
|
||||
<BeaconMarker
|
||||
beacon={
|
||||
Beacon {
|
||||
"_beaconInfo": {
|
||||
"assetType": "m.self",
|
||||
"description": undefined,
|
||||
"live": true,
|
||||
"timeout": 3600000,
|
||||
"timestamp": 1647270879403,
|
||||
},
|
||||
"_events": {
|
||||
"Beacon.Destroy": [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"Beacon.LivenessChange": [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"Beacon.LocationUpdate": [Function],
|
||||
"Beacon.new": [Function],
|
||||
"Beacon.update": [Function],
|
||||
},
|
||||
"_eventsCount": 5,
|
||||
"_isLive": true,
|
||||
"_latestLocationEvent": {
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$alice-room1-1",
|
||||
"rel_type": "m.reference",
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"description": undefined,
|
||||
"uri": "geo:51,41",
|
||||
},
|
||||
"org.matrix.msc3488.ts": 1647270879404,
|
||||
},
|
||||
"room_id": undefined,
|
||||
"sender": "@alice:server",
|
||||
"type": "org.matrix.msc3672.beacon",
|
||||
},
|
||||
"_maxListeners": undefined,
|
||||
"clearLatestLocation": [Function],
|
||||
"livenessWatchTimeout": undefined,
|
||||
"roomId": "!room:server",
|
||||
"rootEvent": {
|
||||
"content": {
|
||||
"description": undefined,
|
||||
"live": true,
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.self",
|
||||
},
|
||||
"org.matrix.msc3488.ts": 1647270879403,
|
||||
"timeout": 3600000,
|
||||
},
|
||||
"event_id": "$alice-room1-1",
|
||||
"origin_server_ts": 1647270879403,
|
||||
"room_id": "!room:server",
|
||||
"sender": "@alice:server",
|
||||
"state_key": "@alice:server",
|
||||
"type": "org.matrix.msc3672.beacon_info",
|
||||
},
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
map={
|
||||
MockMap {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"addControl": [MockFunction],
|
||||
"fitBounds": [MockFunction],
|
||||
"removeControl": [MockFunction],
|
||||
"setCenter": [MockFunction],
|
||||
"setStyle": [MockFunction],
|
||||
"zoomIn": [MockFunction],
|
||||
"zoomOut": [MockFunction],
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
>
|
||||
<SmartMarker
|
||||
geoUri="geo:51,41"
|
||||
id="!room:server_@alice:server"
|
||||
map={
|
||||
MockMap {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"addControl": [MockFunction],
|
||||
"fitBounds": [MockFunction],
|
||||
"removeControl": [MockFunction],
|
||||
"setCenter": [MockFunction],
|
||||
"setStyle": [MockFunction],
|
||||
"zoomIn": [MockFunction],
|
||||
"zoomOut": [MockFunction],
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
roomMember={
|
||||
RoomMember {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_isOutOfBand": false,
|
||||
"_maxListeners": undefined,
|
||||
"disambiguate": false,
|
||||
"events": {},
|
||||
"membership": undefined,
|
||||
"modified": 1647270879403,
|
||||
"name": "@alice:server",
|
||||
"powerLevel": 0,
|
||||
"powerLevelNorm": 0,
|
||||
"rawDisplayName": "@alice:server",
|
||||
"requestedProfileInfo": false,
|
||||
"roomId": "!room:server",
|
||||
"typing": false,
|
||||
"user": undefined,
|
||||
"userId": "@alice:server",
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
useMemberColor={true}
|
||||
>
|
||||
<span>
|
||||
<ForwardRef
|
||||
id="!room:server_@alice:server"
|
||||
roomMember={
|
||||
RoomMember {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_isOutOfBand": false,
|
||||
"_maxListeners": undefined,
|
||||
"disambiguate": false,
|
||||
"events": {},
|
||||
"membership": undefined,
|
||||
"modified": 1647270879403,
|
||||
"name": "@alice:server",
|
||||
"powerLevel": 0,
|
||||
"powerLevelNorm": 0,
|
||||
"rawDisplayName": "@alice:server",
|
||||
"requestedProfileInfo": false,
|
||||
"roomId": "!room:server",
|
||||
"typing": false,
|
||||
"user": undefined,
|
||||
"userId": "@alice:server",
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
useMemberColor={true}
|
||||
<DocumentFragment>
|
||||
<span>
|
||||
<div
|
||||
class="mx_Marker mx_Username_color4"
|
||||
id="!room:server_@alice:server"
|
||||
>
|
||||
<div
|
||||
class="mx_Marker_border"
|
||||
>
|
||||
<div
|
||||
className="mx_Marker mx_Username_color4"
|
||||
id="!room:server_@alice:server"
|
||||
<span
|
||||
class="mx_BaseAvatar"
|
||||
role="presentation"
|
||||
>
|
||||
<OptionalTooltip>
|
||||
<div
|
||||
className="mx_Marker_border"
|
||||
>
|
||||
<MemberAvatar
|
||||
height={36}
|
||||
hideTitle={false}
|
||||
member={
|
||||
RoomMember {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_isOutOfBand": false,
|
||||
"_maxListeners": undefined,
|
||||
"disambiguate": false,
|
||||
"events": {},
|
||||
"membership": undefined,
|
||||
"modified": 1647270879403,
|
||||
"name": "@alice:server",
|
||||
"powerLevel": 0,
|
||||
"powerLevelNorm": 0,
|
||||
"rawDisplayName": "@alice:server",
|
||||
"requestedProfileInfo": false,
|
||||
"roomId": "!room:server",
|
||||
"typing": false,
|
||||
"user": undefined,
|
||||
"userId": "@alice:server",
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
viewUserOnClick={false}
|
||||
width={36}
|
||||
>
|
||||
<BaseAvatar
|
||||
height={36}
|
||||
idName="@alice:server"
|
||||
name="@alice:server"
|
||||
resizeMethod="crop"
|
||||
title="@alice:server"
|
||||
width={36}
|
||||
>
|
||||
<span
|
||||
className="mx_BaseAvatar"
|
||||
role="presentation"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mx_BaseAvatar_initial"
|
||||
style={
|
||||
{
|
||||
"fontSize": "23.400000000000002px",
|
||||
"lineHeight": "36px",
|
||||
"width": "36px",
|
||||
}
|
||||
}
|
||||
>
|
||||
A
|
||||
</span>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="mx_BaseAvatar_image"
|
||||
data-testid="avatar-img"
|
||||
onError={[Function]}
|
||||
src="data:image/png;base64,00"
|
||||
style={
|
||||
{
|
||||
"height": "36px",
|
||||
"width": "36px",
|
||||
}
|
||||
}
|
||||
title="@alice:server"
|
||||
/>
|
||||
</span>
|
||||
</BaseAvatar>
|
||||
</MemberAvatar>
|
||||
</div>
|
||||
</OptionalTooltip>
|
||||
</div>
|
||||
</ForwardRef>
|
||||
</span>
|
||||
</SmartMarker>
|
||||
</BeaconMarker>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="mx_BaseAvatar_initial"
|
||||
style="font-size: 23.400000000000002px; width: 36px; line-height: 36px;"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
<img
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
class="mx_BaseAvatar_image"
|
||||
data-testid="avatar-img"
|
||||
src="data:image/png;base64,00"
|
||||
style="width: 36px; height: 36px;"
|
||||
title="@alice:server"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
@@ -42,14 +42,15 @@ import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { ReadPinsEventId } from "../../../../src/components/views/right_panel/types";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
|
||||
|
||||
jest.mock("../../../../src/utils/strings", () => ({
|
||||
copyPlaintext: jest.fn(),
|
||||
getSelectedText: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../src/utils/EventUtils", () => ({
|
||||
// @ts-ignore don't mock everything
|
||||
...jest.requireActual("../../../../src/utils/EventUtils"),
|
||||
...(jest.requireActual("../../../../src/utils/EventUtils") as object),
|
||||
canEditContent: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../src/dispatcher/dispatcher");
|
||||
@@ -241,6 +242,17 @@ describe("MessageContextMenu", () => {
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should not allow forwarding a voice broadcast", () => {
|
||||
const broadcastStartEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
const menu = createMenu(broadcastStartEvent);
|
||||
expect(menu.find('div[aria-label="Forward"]')).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("forwarding beacons", () => {
|
||||
const aliceId = "@alice:server.org";
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, fireEvent } from "@testing-library/react";
|
||||
import { act } from "react-test-renderer";
|
||||
import { act, render, fireEvent } from "@testing-library/react";
|
||||
import { EventType, EventStatus, MatrixEvent, MatrixEventEvent, MsgType, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread";
|
||||
|
||||
@@ -34,6 +33,8 @@ import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
|
||||
|
||||
jest.mock("../../../../src/dispatcher/dispatcher");
|
||||
|
||||
@@ -405,6 +406,17 @@ describe("<MessageActionBar />", () => {
|
||||
expect(queryByLabelText("Reply in thread")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not render thread button for a voice broadcast", () => {
|
||||
const broadcastEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
userId,
|
||||
"ABC123",
|
||||
);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: broadcastEvent });
|
||||
expect(queryByLabelText("Reply in thread")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens user settings on click", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
@@ -229,7 +229,10 @@ describe("EditWysiwygComposer", () => {
|
||||
},
|
||||
"msgtype": "m.text",
|
||||
};
|
||||
expect(mockClient.sendMessage).toBeCalledWith(mockEvent.getRoomId(), null, expectedContent);
|
||||
await waitFor(() =>
|
||||
expect(mockClient.sendMessage).toBeCalledWith(mockEvent.getRoomId(), null, expectedContent),
|
||||
);
|
||||
|
||||
expect(spyDispatcher).toBeCalledWith({ action: "message_sent" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,8 @@ import { act, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { PlainTextComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer";
|
||||
import * as mockUseSettingsHook from "../../../../../../src/hooks/useSettings";
|
||||
import * as mockKeyboard from "../../../../../../src/Keyboard";
|
||||
|
||||
describe("PlainTextComposer", () => {
|
||||
const customRender = (
|
||||
@@ -37,6 +39,17 @@ describe("PlainTextComposer", () => {
|
||||
);
|
||||
};
|
||||
|
||||
let mockUseSettingValue: jest.SpyInstance;
|
||||
beforeEach(() => {
|
||||
// defaults for these tests are:
|
||||
// ctrlEnterToSend is false
|
||||
mockUseSettingValue = jest.spyOn(mockUseSettingsHook, "useSettingValue").mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("Should have contentEditable at false when disabled", () => {
|
||||
// When
|
||||
customRender(jest.fn(), jest.fn(), true);
|
||||
@@ -64,7 +77,7 @@ describe("PlainTextComposer", () => {
|
||||
expect(onChange).toBeCalledWith(content);
|
||||
});
|
||||
|
||||
it("Should call onSend when Enter is pressed", async () => {
|
||||
it("Should call onSend when Enter is pressed when ctrlEnterToSend is false", async () => {
|
||||
//When
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
@@ -74,9 +87,134 @@ describe("PlainTextComposer", () => {
|
||||
expect(onSend).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Should not call onSend when Enter is pressed when ctrlEnterToSend is true", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
await userEvent.type(screen.getByRole("textbox"), "{enter}");
|
||||
|
||||
// Then it does not send a message
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("Should only call onSend when ctrl+enter is pressed when ctrlEnterToSend is true on windows", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
await userEvent.type(textBox, "hello");
|
||||
|
||||
// Then it does NOT send a message on enter
|
||||
await userEvent.type(textBox, "{enter}");
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
|
||||
// Then it does NOT send a message on windows+enter
|
||||
await userEvent.type(textBox, "{meta>}{enter}{meta/}");
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
|
||||
// Then it does send a message on ctrl+enter
|
||||
await userEvent.type(textBox, "{control>}{enter}{control/}");
|
||||
expect(onSend).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Should only call onSend when cmd+enter is pressed when ctrlEnterToSend is true on mac", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
Object.defineProperty(mockKeyboard, "IS_MAC", { value: true });
|
||||
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
await userEvent.type(textBox, "hello");
|
||||
|
||||
// Then it does NOT send a message on enter
|
||||
await userEvent.type(textBox, "{enter}");
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
|
||||
// Then it does NOT send a message on ctrl+enter
|
||||
await userEvent.type(textBox, "{control>}{enter}{control/}");
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
|
||||
// Then it does send a message on cmd+enter
|
||||
await userEvent.type(textBox, "{meta>}{enter}{meta/}");
|
||||
expect(onSend).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Should insert a newline character when shift enter is pressed when ctrlEnterToSend is false", async () => {
|
||||
//When
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
const inputWithShiftEnter = "new{Shift>}{enter}{/Shift}line";
|
||||
const expectedInnerHtml = "new\nline";
|
||||
|
||||
await userEvent.click(textBox);
|
||||
await userEvent.type(textBox, inputWithShiftEnter);
|
||||
|
||||
// Then it does not send a message, but inserts a newline character
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
expect(textBox.innerHTML).toBe(expectedInnerHtml);
|
||||
});
|
||||
|
||||
it("Should insert a newline character when shift enter is pressed when ctrlEnterToSend is true", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
const keyboardInput = "new{Shift>}{enter}{/Shift}line";
|
||||
const expectedInnerHtml = "new\nline";
|
||||
|
||||
await userEvent.click(textBox);
|
||||
await userEvent.type(textBox, keyboardInput);
|
||||
|
||||
// Then it does not send a message, but inserts a newline character
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
expect(textBox.innerHTML).toBe(expectedInnerHtml);
|
||||
});
|
||||
|
||||
it("Should not insert div and br tags when enter is pressed when ctrlEnterToSend is true", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
const enterThenTypeHtml = "<div>hello</div";
|
||||
|
||||
await userEvent.click(textBox);
|
||||
await userEvent.type(textBox, "{enter}hello");
|
||||
|
||||
// Then it does not send a message, but inserts a newline character
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
expect(textBox).not.toContainHTML(enterThenTypeHtml);
|
||||
});
|
||||
|
||||
it("Should not insert div tags when enter is pressed then user types more when ctrlEnterToSend is true", async () => {
|
||||
//When
|
||||
mockUseSettingValue.mockReturnValue(true);
|
||||
const onSend = jest.fn();
|
||||
customRender(jest.fn(), onSend);
|
||||
const textBox = screen.getByRole("textbox");
|
||||
const defaultEnterHtml = "<div><br></div";
|
||||
|
||||
await userEvent.click(textBox);
|
||||
await userEvent.type(textBox, "{enter}");
|
||||
|
||||
// Then it does not send a message, but inserts a newline character
|
||||
expect(onSend).toBeCalledTimes(0);
|
||||
expect(textBox).not.toContainHTML(defaultEnterHtml);
|
||||
});
|
||||
|
||||
it("Should clear textbox content when clear is called", async () => {
|
||||
//When
|
||||
let composer;
|
||||
let composer: {
|
||||
clear: () => void;
|
||||
insertText: (text: string) => void;
|
||||
};
|
||||
|
||||
render(
|
||||
<PlainTextComposer onChange={jest.fn()} onSend={jest.fn()}>
|
||||
{(ref, composerFunctions) => {
|
||||
@@ -85,9 +223,11 @@ describe("PlainTextComposer", () => {
|
||||
}}
|
||||
</PlainTextComposer>,
|
||||
);
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "content");
|
||||
expect(screen.getByRole("textbox").innerHTML).toBe("content");
|
||||
composer.clear();
|
||||
|
||||
composer!.clear();
|
||||
|
||||
// Then
|
||||
expect(screen.getByRole("textbox").innerHTML).toBeFalsy();
|
||||
@@ -112,7 +252,7 @@ describe("PlainTextComposer", () => {
|
||||
render(<PlainTextComposer onChange={jest.fn()} onSend={jest.fn()} />);
|
||||
|
||||
// Then
|
||||
expect(screen.getByTestId("WysiwygComposerEditor").attributes["data-is-expanded"].value).toBe("false");
|
||||
expect(screen.getByTestId("WysiwygComposerEditor").dataset["isExpanded"]).toBe("false");
|
||||
expect(editor).toBe(screen.getByRole("textbox"));
|
||||
|
||||
// When
|
||||
@@ -126,7 +266,7 @@ describe("PlainTextComposer", () => {
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(screen.getByTestId("WysiwygComposerEditor").attributes["data-is-expanded"].value).toBe("true");
|
||||
expect(screen.getByTestId("WysiwygComposerEditor").dataset["isExpanded"]).toBe("true");
|
||||
|
||||
jest.useRealTimers();
|
||||
(global.ResizeObserver as jest.Mock).mockRestore();
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("createMessageContent", () => {
|
||||
return "$$permalink$$";
|
||||
},
|
||||
} as RoomPermalinkCreator;
|
||||
const message = "<i><b>hello</b> world</i>";
|
||||
const message = "<em><b>hello</b> world</em>";
|
||||
const mockEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
room: "myfakeroom",
|
||||
@@ -37,31 +37,31 @@ describe("createMessageContent", () => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("Should create html message", () => {
|
||||
it("Should create html message", async () => {
|
||||
// When
|
||||
const content = createMessageContent(message, true, { permalinkCreator });
|
||||
const content = await createMessageContent(message, true, { permalinkCreator });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
body: "hello world",
|
||||
body: "*__hello__ world*",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: message,
|
||||
msgtype: "m.text",
|
||||
});
|
||||
});
|
||||
|
||||
it("Should add reply to message content", () => {
|
||||
it("Should add reply to message content", async () => {
|
||||
// When
|
||||
const content = createMessageContent(message, true, { permalinkCreator, replyToEvent: mockEvent });
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, replyToEvent: mockEvent });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
"body": "> <myfakeuser> Replying to this\n\nhello world",
|
||||
"body": "> <myfakeuser> Replying to this\n\n*__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body":
|
||||
'<mx-reply><blockquote><a href="$$permalink$$">In reply to</a>' +
|
||||
' <a href="https://matrix.to/#/myfakeuser">myfakeuser</a>' +
|
||||
"<br>Replying to this</blockquote></mx-reply><i><b>hello</b> world</i>",
|
||||
"<br>Replying to this</blockquote></mx-reply><em><b>hello</b> world</em>",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
@@ -71,17 +71,17 @@ describe("createMessageContent", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("Should add relation to message", () => {
|
||||
it("Should add relation to message", async () => {
|
||||
// When
|
||||
const relation = {
|
||||
rel_type: "m.thread",
|
||||
event_id: "myFakeThreadId",
|
||||
};
|
||||
const content = createMessageContent(message, true, { permalinkCreator, relation });
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, relation });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
"body": "hello world",
|
||||
"body": "*__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": message,
|
||||
"msgtype": "m.text",
|
||||
@@ -92,7 +92,7 @@ describe("createMessageContent", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("Should add fields related to edition", () => {
|
||||
it("Should add fields related to edition", async () => {
|
||||
// When
|
||||
const editedEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
@@ -110,16 +110,16 @@ describe("createMessageContent", () => {
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
const content = createMessageContent(message, true, { permalinkCreator, editedEvent });
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, editedEvent });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
"body": " * hello world",
|
||||
"body": " * *__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": ` * ${message}`,
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
body: "hello world",
|
||||
body: "*__hello__ world*",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: message,
|
||||
msgtype: "m.text",
|
||||
|
||||
@@ -70,6 +70,79 @@ describe("message", () => {
|
||||
expect(spyDispatcher).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("Should not send message when there is no roomId", async () => {
|
||||
// When
|
||||
const mockRoomWithoutId = mkStubRoom("", "room without id", mockClient) as any;
|
||||
const mockRoomContextWithoutId: IRoomState = getRoomContext(mockRoomWithoutId, {});
|
||||
|
||||
await sendMessage(message, true, {
|
||||
roomContext: mockRoomContextWithoutId,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toBeCalledTimes(0);
|
||||
expect(spyDispatcher).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
describe("calls client.sendMessage with", () => {
|
||||
it("a null argument if SendMessageParams is missing relation", async () => {
|
||||
// When
|
||||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith(expect.anything(), null, expect.anything());
|
||||
});
|
||||
it("a null argument if SendMessageParams has relation but relation is missing event_id", async () => {
|
||||
// When
|
||||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toBeCalledWith(expect.anything(), null, expect.anything());
|
||||
});
|
||||
it("a null argument if SendMessageParams has relation but rel_type does not match THREAD_RELATION_TYPE.name", async () => {
|
||||
// When
|
||||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {
|
||||
event_id: "valid_id",
|
||||
rel_type: "m.does_not_match",
|
||||
},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toBeCalledWith(expect.anything(), null, expect.anything());
|
||||
});
|
||||
|
||||
it("the event_id if SendMessageParams has relation and rel_type matches THREAD_RELATION_TYPE.name", async () => {
|
||||
// When
|
||||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {
|
||||
event_id: "valid_id",
|
||||
rel_type: "m.thread",
|
||||
},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toBeCalledWith(expect.anything(), "valid_id", expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
it("Should send html message", async () => {
|
||||
// When
|
||||
await sendMessage(message, true, {
|
||||
@@ -80,7 +153,7 @@ describe("message", () => {
|
||||
|
||||
// Then
|
||||
const expectedContent = {
|
||||
body: "hello world",
|
||||
body: "*__hello__ world*",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "<i><b>hello</b> world</i>",
|
||||
msgtype: "m.text",
|
||||
@@ -114,7 +187,7 @@ describe("message", () => {
|
||||
});
|
||||
|
||||
const expectedContent = {
|
||||
"body": "> <myfakeuser2> My reply\n\nhello world",
|
||||
"body": "> <myfakeuser2> My reply\n\n*__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body":
|
||||
'<mx-reply><blockquote><a href="$$permalink$$">In reply to</a>' +
|
||||
|
||||
@@ -784,6 +784,13 @@ describe("ElementCall", () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
it("disconnects if the widget dies", async () => {
|
||||
await call.connect();
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("tracks participants in room state", async () => {
|
||||
expect(call.participants).toEqual(new Map());
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ import {
|
||||
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../test-utils";
|
||||
import dis from "../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../src/dispatcher/actions";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../src/voice-broadcast/types";
|
||||
|
||||
jest.mock("../../src/dispatcher/dispatcher");
|
||||
|
||||
@@ -151,6 +153,20 @@ describe("EventUtils", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const voiceBroadcastStart = mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
|
||||
const voiceBroadcastStop = mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
|
||||
describe("isContentActionable()", () => {
|
||||
type TestCase = [string, MatrixEvent];
|
||||
it.each<TestCase>([
|
||||
@@ -161,6 +177,7 @@ describe("EventUtils", () => {
|
||||
["room member event", roomMemberEvent],
|
||||
["event without msgtype", noMsgType],
|
||||
["event without content body property", noContentBody],
|
||||
["broadcast stop event", voiceBroadcastStop],
|
||||
])("returns false for %s", (_description, event) => {
|
||||
expect(isContentActionable(event)).toBe(false);
|
||||
});
|
||||
@@ -171,6 +188,7 @@ describe("EventUtils", () => {
|
||||
["event with empty content body", emptyContentBody],
|
||||
["event with a content body", niceTextMessage],
|
||||
["beacon_info event", beaconInfoEvent],
|
||||
["broadcast start event", voiceBroadcastStart],
|
||||
])("returns true for %s", (_description, event) => {
|
||||
expect(isContentActionable(event)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
VoiceBroadcastChunkEventType,
|
||||
VoiceBroadcastInfoEventType,
|
||||
VoiceBroadcastInfoState,
|
||||
} from "../../../src/voice-broadcast";
|
||||
} from "../../../src/voice-broadcast/types";
|
||||
import { mkEvent } from "../../test-utils";
|
||||
|
||||
// timestamp incremented on each call to prevent duplicate timestamp
|
||||
|
||||
Reference in New Issue
Block a user