Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -68,16 +68,18 @@ describe("CallEvent", () => {
|
|||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
bob = mkRoomMember(room.roomId, "@bob:example.org");
|
||||
jest.spyOn(room, "getMember").mockImplementation(
|
||||
userId => [alice, bob].find(member => member.userId === userId) ?? null,
|
||||
(userId) => [alice, bob].find((member) => member.userId === userId) ?? null,
|
||||
);
|
||||
|
||||
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map(
|
||||
store => setupAsyncStoreWithClient(store, client),
|
||||
));
|
||||
await Promise.all(
|
||||
[CallStore.instance, WidgetMessagingStore.instance].map((store) =>
|
||||
setupAsyncStoreWithClient(store, client),
|
||||
),
|
||||
);
|
||||
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
|
@ -99,7 +101,9 @@ describe("CallEvent", () => {
|
|||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const renderEvent = () => { render(<CallEvent mxEvent={call.event} />); };
|
||||
const renderEvent = () => {
|
||||
render(<CallEvent mxEvent={call.event} />);
|
||||
};
|
||||
|
||||
it("shows a message and duration if the call was ended", () => {
|
||||
jest.advanceTimersByTime(90000);
|
||||
|
@ -121,7 +125,10 @@ describe("CallEvent", () => {
|
|||
|
||||
it("shows call details and connection controls if the call is loaded", async () => {
|
||||
jest.advanceTimersByTime(90000);
|
||||
call.participants = new Map([[alice, new Set(["a"])], [bob, new Set(["b"])]]);
|
||||
call.participants = new Map([
|
||||
[alice, new Set(["a"])],
|
||||
[bob, new Set(["b"])],
|
||||
]);
|
||||
renderEvent();
|
||||
|
||||
screen.getByText("@alice:example.org started a video call");
|
||||
|
@ -132,11 +139,13 @@ describe("CallEvent", () => {
|
|||
const dispatcherSpy = jest.fn();
|
||||
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Join" }));
|
||||
await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
}));
|
||||
await waitFor(() =>
|
||||
expect(dispatcherSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
view_call: true,
|
||||
}),
|
||||
);
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
await act(() => call.connect());
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ describe("DateSeparator", () => {
|
|||
const HOUR_MS = 3600000;
|
||||
const DAY_MS = HOUR_MS * 24;
|
||||
// Friday Dec 17 2021, 9:09am
|
||||
const now = '2021-12-17T08:09:00.000Z';
|
||||
const now = "2021-12-17T08:09:00.000Z";
|
||||
const nowMs = 1639728540000;
|
||||
const defaultProps = {
|
||||
ts: nowMs,
|
||||
|
@ -47,23 +47,23 @@ describe("DateSeparator", () => {
|
|||
|
||||
const mockClient = getMockClientWithEventEmitter({});
|
||||
const getComponent = (props = {}) =>
|
||||
render((
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<DateSeparator {...defaultProps} {...props} />
|
||||
</MatrixClientContext.Provider>
|
||||
));
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
type TestCase = [string, number, string];
|
||||
const testCases: TestCase[] = [
|
||||
['the exact same moment', nowMs, 'Today'],
|
||||
['same day as current day', nowMs - HOUR_MS, 'Today'],
|
||||
['day before the current day', nowMs - (HOUR_MS * 12), 'Yesterday'],
|
||||
['2 days ago', nowMs - DAY_MS * 2, 'Wednesday'],
|
||||
['144 hours ago', nowMs - HOUR_MS * 144, 'Sat, Dec 11 2021'],
|
||||
["the exact same moment", nowMs, "Today"],
|
||||
["same day as current day", nowMs - HOUR_MS, "Today"],
|
||||
["day before the current day", nowMs - HOUR_MS * 12, "Yesterday"],
|
||||
["2 days ago", nowMs - DAY_MS * 2, "Wednesday"],
|
||||
["144 hours ago", nowMs - HOUR_MS * 144, "Sat, Dec 11 2021"],
|
||||
[
|
||||
'6 days ago, but less than 144h',
|
||||
new Date('Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)').getTime(),
|
||||
'Saturday',
|
||||
"6 days ago, but less than 144h",
|
||||
new Date("Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)").getTime(),
|
||||
"Saturday",
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -80,24 +80,25 @@ describe("DateSeparator", () => {
|
|||
global.Date = RealDate;
|
||||
});
|
||||
|
||||
it('renders the date separator correctly', () => {
|
||||
it("renders the date separator correctly", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
expect(SettingsStore.getValue).toHaveBeenCalledWith(UIFeature.TimelineEnableRelativeDates);
|
||||
});
|
||||
|
||||
it.each(testCases)('formats date correctly when current time is %s', (_d, ts, result) => {
|
||||
it.each(testCases)("formats date correctly when current time is %s", (_d, ts, result) => {
|
||||
expect(getComponent({ ts, forExport: false }).container.textContent).toEqual(result);
|
||||
});
|
||||
|
||||
describe('when forExport is true', () => {
|
||||
it.each(testCases)('formats date in full when current time is %s', (_d, ts) => {
|
||||
expect(getComponent({ ts, forExport: true }).container.textContent)
|
||||
.toEqual(formatFullDateNoTime(new Date(ts)));
|
||||
describe("when forExport is true", () => {
|
||||
it.each(testCases)("formats date in full when current time is %s", (_d, ts) => {
|
||||
expect(getComponent({ ts, forExport: true }).container.textContent).toEqual(
|
||||
formatFullDateNoTime(new Date(ts)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Settings.TimelineEnableRelativeDates is falsy', () => {
|
||||
describe("when Settings.TimelineEnableRelativeDates is falsy", () => {
|
||||
beforeEach(() => {
|
||||
(SettingsStore.getValue as jest.Mock) = jest.fn((arg) => {
|
||||
if (arg === UIFeature.TimelineEnableRelativeDates) {
|
||||
|
@ -105,13 +106,14 @@ describe("DateSeparator", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
it.each(testCases)('formats date in full when current time is %s', (_d, ts) => {
|
||||
expect(getComponent({ ts, forExport: false }).container.textContent)
|
||||
.toEqual(formatFullDateNoTime(new Date(ts)));
|
||||
it.each(testCases)("formats date in full when current time is %s", (_d, ts) => {
|
||||
expect(getComponent({ ts, forExport: false }).container.textContent).toEqual(
|
||||
formatFullDateNoTime(new Date(ts)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature_jump_to_date is enabled', () => {
|
||||
describe("when feature_jump_to_date is enabled", () => {
|
||||
beforeEach(() => {
|
||||
mocked(SettingsStore).getValue.mockImplementation((arg): any => {
|
||||
if (arg === "feature_jump_to_date") {
|
||||
|
@ -119,7 +121,7 @@ describe("DateSeparator", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
it('renders the date separator correctly', () => {
|
||||
it("renders the date separator correctly", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -14,22 +14,24 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import EncryptionEvent from "../../../../src/components/views/messages/EncryptionEvent";
|
||||
import { createTestClient, mkMessage } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import { LocalRoom } from '../../../../src/models/LocalRoom';
|
||||
import DMRoomMap from '../../../../src/utils/DMRoomMap';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { LocalRoom } from "../../../../src/models/LocalRoom";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
const renderEncryptionEvent = (client: MatrixClient, event: MatrixEvent) => {
|
||||
render(<MatrixClientContext.Provider value={client}>
|
||||
<EncryptionEvent mxEvent={event} />
|
||||
</MatrixClientContext.Provider>);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<EncryptionEvent mxEvent={event} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
const checkTexts = (title: string, subTitle: string) => {
|
||||
|
@ -69,8 +71,8 @@ describe("EncryptionEvent", () => {
|
|||
renderEncryptionEvent(client, event);
|
||||
checkTexts(
|
||||
"Encryption enabled",
|
||||
"Messages in this room are end-to-end encrypted. "
|
||||
+ "When people join, you can verify them in their profile, just tap on their avatar.",
|
||||
"Messages in this room are end-to-end encrypted. " +
|
||||
"When people join, you can verify them in their profile, just tap on their avatar.",
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -83,10 +85,7 @@ describe("EncryptionEvent", () => {
|
|||
|
||||
it("should show the expected texts", () => {
|
||||
renderEncryptionEvent(client, event);
|
||||
checkTexts(
|
||||
"Encryption enabled",
|
||||
"Some encryption parameters have been changed.",
|
||||
);
|
||||
checkTexts("Encryption enabled", "Some encryption parameters have been changed.");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -14,68 +14,58 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import {
|
||||
BeaconEvent,
|
||||
getBeaconInfoIdentifier,
|
||||
RelationType,
|
||||
MatrixEvent,
|
||||
EventType,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { Relations } from 'matrix-js-sdk/src/models/relations';
|
||||
import { M_BEACON } from 'matrix-js-sdk/src/@types/beacon';
|
||||
import { mount } from "enzyme";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { BeaconEvent, getBeaconInfoIdentifier, RelationType, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import { Relations } from "matrix-js-sdk/src/models/relations";
|
||||
import { M_BEACON } from "matrix-js-sdk/src/@types/beacon";
|
||||
|
||||
import MBeaconBody from '../../../../src/components/views/messages/MBeaconBody';
|
||||
import MBeaconBody from "../../../../src/components/views/messages/MBeaconBody";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
makeRoomWithStateEvents,
|
||||
} from '../../../test-utils';
|
||||
import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks';
|
||||
import { MediaEventHelper } from '../../../../src/utils/MediaEventHelper';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import Modal from '../../../../src/Modal';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import { MapError } from '../../../../src/components/views/location/MapError';
|
||||
import * as mapUtilHooks from '../../../../src/utils/location/useMap';
|
||||
import { LocationShareError } from '../../../../src/utils/location';
|
||||
} from "../../../test-utils";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import Modal from "../../../../src/Modal";
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
import { MapError } from "../../../../src/components/views/location/MapError";
|
||||
import * as mapUtilHooks from "../../../../src/utils/location/useMap";
|
||||
import { LocationShareError } from "../../../../src/utils/location";
|
||||
|
||||
describe('<MBeaconBody />', () => {
|
||||
describe("<MBeaconBody />", () => {
|
||||
// 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';
|
||||
jest.spyOn(global.Date, "now").mockReturnValue(now);
|
||||
const roomId = "!room:server";
|
||||
const aliceId = "@alice:server";
|
||||
|
||||
const mockMap = new maplibregl.Map();
|
||||
const mockMarker = new maplibregl.Marker();
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
getUserId: jest.fn().mockReturnValue(aliceId),
|
||||
getRoom: jest.fn(),
|
||||
redactEvent: jest.fn(),
|
||||
});
|
||||
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const defaultProps = {
|
||||
mxEvent: defaultEvent,
|
||||
highlights: [],
|
||||
highlightLink: '',
|
||||
highlightLink: "",
|
||||
onHeightChanged: jest.fn(),
|
||||
onMessageAllowed: jest.fn(),
|
||||
// we dont use these and they pollute the snapshots
|
||||
|
@ -89,7 +79,7 @@ describe('<MBeaconBody />', () => {
|
|||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
|
||||
const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined);
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined);
|
||||
|
||||
beforeAll(() => {
|
||||
maplibregl.AttributionControl = jest.fn();
|
||||
|
@ -100,73 +90,66 @@ describe('<MBeaconBody />', () => {
|
|||
});
|
||||
|
||||
const testBeaconStatuses = () => {
|
||||
it('renders stopped beacon UI for an explicitly stopped beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
it("renders stopped beacon UI for an explicitly stopped beacon", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-1");
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders stopped beacon UI for an expired beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
it("renders stopped beacon UI for an expired beacon", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders loading beacon UI for a beacon that has not started yet', () => {
|
||||
it("renders loading beacon UI for a beacon that has not started yet", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// puts this beacons start timestamp in the future
|
||||
{ isLive: true, timestamp: now + 60000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Loading live location...");
|
||||
});
|
||||
|
||||
it('does not open maximised map when on click when beacon is stopped', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
it("does not open maximised map when on click when beacon is stopped", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
act(() => {
|
||||
component.find('.mx_MBeaconBody_map').at(0).simulate('click');
|
||||
component.find(".mx_MBeaconBody_map").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(modalSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is not the latest beacon for a user', () => {
|
||||
it("renders stopped UI when a beacon event is not the latest beacon for a user", () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-2");
|
||||
|
||||
makeRoomWithStateEvents([aliceBeaconInfo1, aliceBeaconInfo2], { roomId, mockClient });
|
||||
|
||||
|
@ -175,21 +158,16 @@ describe('<MBeaconBody />', () => {
|
|||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is replaced', () => {
|
||||
it("renders stopped UI when a beacon event is replaced", () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
"$alice-room1-1",
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-2");
|
||||
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo1], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo1 });
|
||||
|
@ -210,14 +188,9 @@ describe('<MBeaconBody />', () => {
|
|||
|
||||
testBeaconStatuses();
|
||||
|
||||
describe('on liveness change', () => {
|
||||
it('renders stopped UI when a beacon stops being live', () => {
|
||||
const aliceBeaconInfo = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
describe("on liveness change", () => {
|
||||
it("renders stopped UI when a beacon stops being live", () => {
|
||||
const aliceBeaconInfo = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo));
|
||||
|
@ -236,97 +209,96 @@ describe('<MBeaconBody />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('latestLocationState', () => {
|
||||
const aliceBeaconInfo = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
describe("latestLocationState", () => {
|
||||
const aliceBeaconInfo = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: aliceBeaconInfo.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
);
|
||||
const location2 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: aliceBeaconInfo.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 },
|
||||
);
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconInfo.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
const location2 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: aliceBeaconInfo.getId(),
|
||||
geoUri: "geo:52,42",
|
||||
timestamp: now + 10000,
|
||||
});
|
||||
|
||||
it('renders a live beacon without a location correctly', () => {
|
||||
it("renders a live beacon without a location correctly", () => {
|
||||
makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
expect(component.text()).toEqual("Loading live location...");
|
||||
});
|
||||
|
||||
it('does nothing on click when a beacon has no location', () => {
|
||||
it("does nothing on click when a beacon has no location", () => {
|
||||
makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
act(() => {
|
||||
component.find('.mx_MBeaconBody_map').at(0).simulate('click');
|
||||
component.find(".mx_MBeaconBody_map").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(modalSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders a live beacon with a location correctly', () => {
|
||||
it("renders a live beacon with a location correctly", () => {
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo));
|
||||
beaconInstance.addLocations([location1]);
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
expect(component.find('Map').length).toBeTruthy;
|
||||
expect(component.find("Map").length).toBeTruthy;
|
||||
});
|
||||
|
||||
it('opens maximised map view on click when beacon has a live location', () => {
|
||||
it("opens maximised map view on click when beacon has a live location", () => {
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo));
|
||||
beaconInstance.addLocations([location1]);
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
act(() => {
|
||||
component.find('Map').simulate('click');
|
||||
component.find("Map").simulate("click");
|
||||
});
|
||||
|
||||
// opens modal
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing on click when a beacon has no location', () => {
|
||||
it("does nothing on click when a beacon has no location", () => {
|
||||
makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
act(() => {
|
||||
component.find('.mx_MBeaconBody_map').at(0).simulate('click');
|
||||
component.find(".mx_MBeaconBody_map").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(modalSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders a live beacon with a location correctly', () => {
|
||||
it("renders a live beacon with a location correctly", () => {
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo));
|
||||
beaconInstance.addLocations([location1]);
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
expect(component.find('Map').length).toBeTruthy;
|
||||
expect(component.find("Map").length).toBeTruthy;
|
||||
});
|
||||
|
||||
it('opens maximised map view on click when beacon has a live location', () => {
|
||||
it("opens maximised map view on click when beacon has a live location", () => {
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo));
|
||||
beaconInstance.addLocations([location1]);
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
act(() => {
|
||||
component.find('Map').simulate('click');
|
||||
component.find("Map").simulate("click");
|
||||
});
|
||||
|
||||
// opens modal
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates latest location', () => {
|
||||
it("updates latest location", () => {
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo });
|
||||
|
||||
|
@ -349,33 +321,30 @@ describe('<MBeaconBody />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('redaction', () => {
|
||||
describe("redaction", () => {
|
||||
const makeEvents = (): {
|
||||
beaconInfoEvent: MatrixEvent;
|
||||
location1: MatrixEvent;
|
||||
location2: MatrixEvent;
|
||||
} => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
aliceId,
|
||||
{ beaconInfoId: beaconInfoEvent.getId(), geoUri: "geo:51,41", timestamp: now + 1 },
|
||||
roomId,
|
||||
);
|
||||
location1.event.event_id = '1';
|
||||
location1.event.event_id = "1";
|
||||
const location2 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 },
|
||||
aliceId,
|
||||
{ beaconInfoId: beaconInfoEvent.getId(), geoUri: "geo:52,42", timestamp: now + 10000 },
|
||||
roomId,
|
||||
);
|
||||
location2.event.event_id = '2';
|
||||
location2.event.event_id = "2";
|
||||
return { beaconInfoEvent, location1, location2 };
|
||||
};
|
||||
|
||||
const redactionEvent = new MatrixEvent({ type: EventType.RoomRedaction, content: { reason: 'test reason' } });
|
||||
const redactionEvent = new MatrixEvent({ type: EventType.RoomRedaction, content: { reason: "test reason" } });
|
||||
|
||||
const setupRoomWithBeacon = (beaconInfoEvent, locationEvents: MatrixEvent[] = []) => {
|
||||
const room = makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
|
@ -384,14 +353,14 @@ describe('<MBeaconBody />', () => {
|
|||
};
|
||||
const mockGetRelationsForEvent = (locationEvents: MatrixEvent[] = []) => {
|
||||
const relations = new Relations(RelationType.Reference, M_BEACON.name, mockClient);
|
||||
jest.spyOn(relations, 'getRelations').mockReturnValue(locationEvents);
|
||||
jest.spyOn(relations, "getRelations").mockReturnValue(locationEvents);
|
||||
|
||||
const getRelationsForEvent = jest.fn().mockReturnValue(relations);
|
||||
|
||||
return getRelationsForEvent;
|
||||
};
|
||||
|
||||
it('does nothing when getRelationsForEvent is falsy', () => {
|
||||
it("does nothing when getRelationsForEvent is falsy", () => {
|
||||
const { beaconInfoEvent, location1, location2 } = makeEvents();
|
||||
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);
|
||||
|
||||
|
@ -405,10 +374,10 @@ describe('<MBeaconBody />', () => {
|
|||
expect(mockClient.redactEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cleans up redaction listener on unmount', () => {
|
||||
it("cleans up redaction listener on unmount", () => {
|
||||
const { beaconInfoEvent, location1, location2 } = makeEvents();
|
||||
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);
|
||||
const removeListenerSpy = jest.spyOn(beaconInfoEvent, 'removeListener');
|
||||
const removeListenerSpy = jest.spyOn(beaconInfoEvent, "removeListener");
|
||||
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
|
||||
|
@ -419,7 +388,7 @@ describe('<MBeaconBody />', () => {
|
|||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing when beacon has no related locations', async () => {
|
||||
it("does nothing when beacon has no related locations", async () => {
|
||||
const { beaconInfoEvent } = makeEvents();
|
||||
// no locations
|
||||
setupRoomWithBeacon(beaconInfoEvent, []);
|
||||
|
@ -432,12 +401,14 @@ describe('<MBeaconBody />', () => {
|
|||
});
|
||||
|
||||
expect(getRelationsForEvent).toHaveBeenCalledWith(
|
||||
beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name,
|
||||
beaconInfoEvent.getId(),
|
||||
RelationType.Reference,
|
||||
M_BEACON.name,
|
||||
);
|
||||
expect(mockClient.redactEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('redacts related locations on beacon redaction', async () => {
|
||||
it("redacts related locations on beacon redaction", async () => {
|
||||
const { beaconInfoEvent, location1, location2 } = makeEvents();
|
||||
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);
|
||||
|
||||
|
@ -450,43 +421,36 @@ describe('<MBeaconBody />', () => {
|
|||
});
|
||||
|
||||
expect(getRelationsForEvent).toHaveBeenCalledWith(
|
||||
beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name,
|
||||
beaconInfoEvent.getId(),
|
||||
RelationType.Reference,
|
||||
M_BEACON.name,
|
||||
);
|
||||
expect(mockClient.redactEvent).toHaveBeenCalledTimes(2);
|
||||
expect(mockClient.redactEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
location1.getId(),
|
||||
undefined,
|
||||
{ reason: 'test reason' },
|
||||
);
|
||||
expect(mockClient.redactEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
location2.getId(),
|
||||
undefined,
|
||||
{ reason: 'test reason' },
|
||||
);
|
||||
expect(mockClient.redactEvent).toHaveBeenCalledWith(roomId, location1.getId(), undefined, {
|
||||
reason: "test reason",
|
||||
});
|
||||
expect(mockClient.redactEvent).toHaveBeenCalledWith(roomId, location2.getId(), undefined, {
|
||||
reason: "test reason",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when map display is not configured', () => {
|
||||
describe("when map display is not configured", () => {
|
||||
beforeEach(() => {
|
||||
// mock map utils to raise MapStyleUrlNotConfigured error
|
||||
jest.spyOn(mapUtilHooks, 'useMap').mockImplementation(
|
||||
({ onError }) => {
|
||||
onError(new Error(LocationShareError.MapStyleUrlNotConfigured));
|
||||
return mockMap;
|
||||
});
|
||||
jest.spyOn(mapUtilHooks, "useMap").mockImplementation(({ onError }) => {
|
||||
onError(new Error(LocationShareError.MapStyleUrlNotConfigured));
|
||||
return mockMap;
|
||||
});
|
||||
});
|
||||
|
||||
it('renders maps unavailable error for a live beacon with location', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
);
|
||||
it("renders maps unavailable error for a live beacon with location", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1");
|
||||
const location1 = makeBeaconEvent(aliceId, {
|
||||
beaconInfoId: beaconInfoEvent.getId(),
|
||||
geoUri: "geo:51,41",
|
||||
timestamp: now + 1,
|
||||
});
|
||||
|
||||
makeRoomWithBeacons(roomId, mockClient, [beaconInfoEvent], [location1]);
|
||||
|
||||
|
|
|
@ -48,8 +48,8 @@ describe("<MImageBody/>", () => {
|
|||
getIgnoredUsers: jest.fn(),
|
||||
getVersions: jest.fn().mockResolvedValue({
|
||||
unstable_features: {
|
||||
'org.matrix.msc3882': true,
|
||||
'org.matrix.msc3886': true,
|
||||
"org.matrix.msc3882": true,
|
||||
"org.matrix.msc3886": true,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
@ -75,11 +75,13 @@ describe("<MImageBody/>", () => {
|
|||
it("should show error when encrypted media cannot be downloaded", async () => {
|
||||
fetchMock.getOnce(url, { status: 500 });
|
||||
|
||||
render(<MImageBody
|
||||
{...props}
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>);
|
||||
render(
|
||||
<MImageBody
|
||||
{...props}
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
);
|
||||
|
||||
await screen.findByText("Error downloading image");
|
||||
});
|
||||
|
@ -88,11 +90,13 @@ describe("<MImageBody/>", () => {
|
|||
fetchMock.getOnce(url, "thisistotallyanencryptedpng");
|
||||
mocked(encrypt.decryptAttachment).mockRejectedValue(new Error("Failed to decrypt"));
|
||||
|
||||
render(<MImageBody
|
||||
{...props}
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>);
|
||||
render(
|
||||
<MImageBody
|
||||
{...props}
|
||||
mxEvent={encryptedMediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(encryptedMediaEvent)}
|
||||
/>,
|
||||
);
|
||||
|
||||
await screen.findByText("Error decrypting image");
|
||||
});
|
||||
|
|
|
@ -14,33 +14,40 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import { EventEmitter } from 'events';
|
||||
import { MatrixEvent, EventType } from 'matrix-js-sdk/src/matrix';
|
||||
import { CryptoEvent } from 'matrix-js-sdk/src/crypto';
|
||||
import { UserTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning';
|
||||
import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest';
|
||||
import React from "react";
|
||||
import TestRenderer from "react-test-renderer";
|
||||
import { EventEmitter } from "events";
|
||||
import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||
import { UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
|
||||
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import MKeyVerificationConclusion from '../../../../src/components/views/messages/MKeyVerificationConclusion';
|
||||
import { getMockClientWithEventEmitter } from '../../../test-utils';
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import MKeyVerificationConclusion from "../../../../src/components/views/messages/MKeyVerificationConclusion";
|
||||
import { getMockClientWithEventEmitter } from "../../../test-utils";
|
||||
|
||||
const trustworthy = ({ isCrossSigningVerified: () => true }) as unknown as UserTrustLevel;
|
||||
const untrustworthy = ({ isCrossSigningVerified: () => false }) as unknown as UserTrustLevel;
|
||||
const trustworthy = { isCrossSigningVerified: () => true } as unknown as UserTrustLevel;
|
||||
const untrustworthy = { isCrossSigningVerified: () => false } as unknown as UserTrustLevel;
|
||||
|
||||
describe("MKeyVerificationConclusion", () => {
|
||||
const userId = '@user:server';
|
||||
const userId = "@user:server";
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getRoom: jest.fn(),
|
||||
getUserId: jest.fn().mockReturnValue(userId),
|
||||
checkUserTrust: jest.fn(),
|
||||
});
|
||||
|
||||
const getMockVerificationRequest = (
|
||||
{ pending, cancelled, done, otherUserId }:
|
||||
{ pending?: boolean, cancelled?: boolean, done?: boolean, otherUserId?: string },
|
||||
) => {
|
||||
const getMockVerificationRequest = ({
|
||||
pending,
|
||||
cancelled,
|
||||
done,
|
||||
otherUserId,
|
||||
}: {
|
||||
pending?: boolean;
|
||||
cancelled?: boolean;
|
||||
done?: boolean;
|
||||
otherUserId?: string;
|
||||
}) => {
|
||||
class MockVerificationRequest extends EventEmitter {
|
||||
constructor(
|
||||
public readonly pending: boolean,
|
||||
|
@ -60,41 +67,33 @@ describe("MKeyVerificationConclusion", () => {
|
|||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockRestore();
|
||||
});
|
||||
|
||||
it("shouldn't render if there's no verificationRequest", () => {
|
||||
const event = new MatrixEvent({});
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
it("shouldn't render if the verificationRequest is pending", () => {
|
||||
const event = new MatrixEvent({});
|
||||
event.verificationRequest = getMockVerificationRequest({ pending: true });
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
it("shouldn't render if the event type is cancel but the request type isn't", () => {
|
||||
const event = new MatrixEvent({ type: EventType.KeyVerificationCancel });
|
||||
event.verificationRequest = getMockVerificationRequest({ cancelled: false });
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
it("shouldn't render if the event type is done but the request type isn't", () => {
|
||||
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||
event.verificationRequest = getMockVerificationRequest({ done: false });
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
|
@ -103,9 +102,7 @@ describe("MKeyVerificationConclusion", () => {
|
|||
|
||||
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||
event.verificationRequest = getMockVerificationRequest({ done: true });
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
});
|
||||
|
||||
|
@ -114,9 +111,7 @@ describe("MKeyVerificationConclusion", () => {
|
|||
|
||||
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||
event.verificationRequest = getMockVerificationRequest({ done: true, otherUserId: "@someuser:domain" });
|
||||
const renderer = TestRenderer.create(
|
||||
<MKeyVerificationConclusion mxEvent={event} />,
|
||||
);
|
||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
||||
expect(renderer.toJSON()).toBeNull();
|
||||
|
||||
mockClient.checkUserTrust.mockReturnValue(trustworthy);
|
||||
|
|
|
@ -14,33 +14,33 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from "enzyme";
|
||||
import { LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
||||
import { ClientEvent, RoomMember } from 'matrix-js-sdk/src/matrix';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { SyncState } from 'matrix-js-sdk/src/sync';
|
||||
import { ClientEvent, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { SyncState } from "matrix-js-sdk/src/sync";
|
||||
|
||||
import MLocationBody from "../../../../src/components/views/messages/MLocationBody";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";
|
||||
import Modal from '../../../../src/Modal';
|
||||
import Modal from "../../../../src/Modal";
|
||||
import SdkConfig from "../../../../src/SdkConfig";
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||
import { makeLocationEvent } from "../../../test-utils/location";
|
||||
import { getMockClientWithEventEmitter } from '../../../test-utils';
|
||||
import { getMockClientWithEventEmitter } from "../../../test-utils";
|
||||
|
||||
describe("MLocationBody", () => {
|
||||
describe('<MLocationBody>', () => {
|
||||
const roomId = '!room:server';
|
||||
const userId = '@user:server';
|
||||
describe("<MLocationBody>", () => {
|
||||
const roomId = "!room:server";
|
||||
const userId = "@user:server";
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getClientWellKnown: jest.fn().mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
}),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
|
@ -48,26 +48,27 @@ describe("MLocationBody", () => {
|
|||
const defaultProps = {
|
||||
mxEvent: defaultEvent,
|
||||
highlights: [],
|
||||
highlightLink: '',
|
||||
highlightLink: "",
|
||||
onHeightChanged: jest.fn(),
|
||||
onMessageAllowed: jest.fn(),
|
||||
permalinkCreator: {} as RoomPermalinkCreator,
|
||||
mediaEventHelper: {} as MediaEventHelper,
|
||||
};
|
||||
const getComponent = (props = {}) => mount(<MLocationBody {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<MLocationBody {...defaultProps} {...props} />, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: { value: mockClient },
|
||||
});
|
||||
const getMapErrorComponent = () => {
|
||||
const mockMap = new maplibregl.Map();
|
||||
mockClient.getClientWellKnown.mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'bad-tile-server.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "bad-tile-server.com" },
|
||||
});
|
||||
const component = getComponent();
|
||||
|
||||
// simulate error initialising map in maplibregl
|
||||
// @ts-ignore
|
||||
mockMap.emit('error', { status: 404 });
|
||||
mockMap.emit("error", { status: 404 });
|
||||
|
||||
return component;
|
||||
};
|
||||
|
@ -80,33 +81,33 @@ describe("MLocationBody", () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('with error', () => {
|
||||
describe("with error", () => {
|
||||
let sdkConfigSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
// eat expected errors to keep console clean
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => { });
|
||||
jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||
mockClient.getClientWellKnown.mockReturnValue({});
|
||||
sdkConfigSpy = jest.spyOn(SdkConfig, 'get').mockReturnValue({});
|
||||
sdkConfigSpy = jest.spyOn(SdkConfig, "get").mockReturnValue({});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
sdkConfigSpy.mockRestore();
|
||||
jest.spyOn(logger, 'error').mockRestore();
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
});
|
||||
|
||||
it('displays correct fallback content without error style when map_style_url is not configured', () => {
|
||||
it("displays correct fallback content without error style when map_style_url is not configured", () => {
|
||||
const component = getComponent();
|
||||
expect(component.find(".mx_EventTile_body")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('displays correct fallback content when map_style_url is misconfigured', () => {
|
||||
it("displays correct fallback content when map_style_url is misconfigured", () => {
|
||||
const component = getMapErrorComponent();
|
||||
component.setProps({});
|
||||
expect(component.find(".mx_EventTile_body")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should clear the error on reconnect', () => {
|
||||
it("should clear the error on reconnect", () => {
|
||||
const component = getMapErrorComponent();
|
||||
expect((component.state() as React.ComponentState).error).toBeDefined();
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Reconnecting, SyncState.Error);
|
||||
|
@ -114,57 +115,58 @@ describe("MLocationBody", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('without error', () => {
|
||||
describe("without error", () => {
|
||||
beforeEach(() => {
|
||||
mockClient.getClientWellKnown.mockReturnValue({
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' },
|
||||
[TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" },
|
||||
});
|
||||
|
||||
// MLocationBody uses random number for map id
|
||||
// stabilise for test
|
||||
jest.spyOn(global.Math, 'random').mockReturnValue(0.123456);
|
||||
jest.spyOn(global.Math, "random").mockReturnValue(0.123456);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(global.Math, 'random').mockRestore();
|
||||
jest.spyOn(global.Math, "random").mockRestore();
|
||||
});
|
||||
|
||||
it('renders map correctly', () => {
|
||||
it("renders map correctly", () => {
|
||||
const mockMap = new maplibregl.Map();
|
||||
const component = getComponent();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
// map was centered
|
||||
expect(mockMap.setCenter).toHaveBeenCalledWith({
|
||||
lat: 51.5076, lon: -0.1276,
|
||||
lat: 51.5076,
|
||||
lon: -0.1276,
|
||||
});
|
||||
});
|
||||
|
||||
it('opens map dialog on click', () => {
|
||||
const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined);
|
||||
it("opens map dialog on click", () => {
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined);
|
||||
const component = getComponent();
|
||||
|
||||
act(() => {
|
||||
component.find('Map').at(0).simulate('click');
|
||||
component.find("Map").at(0).simulate("click");
|
||||
});
|
||||
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders marker correctly for a non-self share', () => {
|
||||
it("renders marker correctly for a non-self share", () => {
|
||||
const mockMap = new maplibregl.Map();
|
||||
const component = getComponent();
|
||||
|
||||
expect(component.find('SmartMarker').at(0).props()).toEqual(
|
||||
expect(component.find("SmartMarker").at(0).props()).toEqual(
|
||||
expect.objectContaining({
|
||||
map: mockMap,
|
||||
geoUri: 'geo:51.5076,-0.1276',
|
||||
geoUri: "geo:51.5076,-0.1276",
|
||||
roomMember: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders marker correctly for a self share', () => {
|
||||
it("renders marker correctly for a self share", () => {
|
||||
const selfShareEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Self);
|
||||
const member = new RoomMember(roomId, userId);
|
||||
// @ts-ignore cheat assignment to property
|
||||
|
@ -172,9 +174,7 @@ describe("MLocationBody", () => {
|
|||
const component = getComponent({ mxEvent: selfShareEvent });
|
||||
|
||||
// render self locations with user avatars
|
||||
expect(component.find('SmartMarker').at(0).props()['roomMember']).toEqual(
|
||||
member,
|
||||
);
|
||||
expect(component.find("SmartMarker").at(0).props()["roomMember"]).toEqual(member);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -49,7 +49,7 @@ const CHECKED = "mx_MPollBody_option_checked";
|
|||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
getUserId: jest.fn().mockReturnValue("@me:example.com"),
|
||||
sendEvent: jest.fn().mockReturnValue(Promise.resolve({ "event_id": "fake_send_id" })),
|
||||
sendEvent: jest.fn().mockReturnValue(Promise.resolve({ event_id: "fake_send_id" })),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
|
||||
|
@ -76,9 +76,7 @@ describe("MPollBody", () => {
|
|||
const ev2 = responseEvent();
|
||||
const badEvent = badResponseEvent();
|
||||
|
||||
const voteRelations = new RelatedRelations([
|
||||
newVoteRelations([ev1, badEvent, ev2]),
|
||||
]);
|
||||
const voteRelations = new RelatedRelations([newVoteRelations([ev1, badEvent, ev2])]);
|
||||
expect(
|
||||
allVotes(
|
||||
{ getRoomId: () => "$room" } as MatrixEvent,
|
||||
|
@ -87,21 +85,13 @@ describe("MPollBody", () => {
|
|||
new RelatedRelations([newEndRelations([])]),
|
||||
),
|
||||
).toEqual([
|
||||
new UserVote(
|
||||
ev1.getTs(),
|
||||
ev1.getSender(),
|
||||
ev1.getContent()[M_POLL_RESPONSE.name].answers,
|
||||
),
|
||||
new UserVote(ev1.getTs(), ev1.getSender(), ev1.getContent()[M_POLL_RESPONSE.name].answers),
|
||||
new UserVote(
|
||||
badEvent.getTs(),
|
||||
badEvent.getSender(),
|
||||
[], // should be spoiled
|
||||
),
|
||||
new UserVote(
|
||||
ev2.getTs(),
|
||||
ev2.getSender(),
|
||||
ev2.getContent()[M_POLL_RESPONSE.name].answers,
|
||||
),
|
||||
new UserVote(ev2.getTs(), ev2.getSender(), ev2.getContent()[M_POLL_RESPONSE.name].answers),
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -117,13 +107,7 @@ describe("MPollBody", () => {
|
|||
|
||||
setRedactionAllowedForMeOnly(mockClient);
|
||||
|
||||
expect(
|
||||
pollEndTs(
|
||||
{ getRoomId: () => "$room" } as MatrixEvent,
|
||||
mockClient,
|
||||
endRelations,
|
||||
),
|
||||
).toBe(12);
|
||||
expect(pollEndTs({ getRoomId: () => "$room" } as MatrixEvent, mockClient, endRelations)).toBe(12);
|
||||
});
|
||||
|
||||
it("ignores unauthorised end poll event when finding end ts", () => {
|
||||
|
@ -138,13 +122,7 @@ describe("MPollBody", () => {
|
|||
|
||||
setRedactionAllowedForMeOnly(mockClient);
|
||||
|
||||
expect(
|
||||
pollEndTs(
|
||||
{ getRoomId: () => "$room" } as MatrixEvent,
|
||||
mockClient,
|
||||
endRelations,
|
||||
),
|
||||
).toBe(13);
|
||||
expect(pollEndTs({ getRoomId: () => "$room" } as MatrixEvent, mockClient, endRelations)).toBe(13);
|
||||
});
|
||||
|
||||
it("counts only votes before the end poll event", () => {
|
||||
|
@ -157,18 +135,9 @@ describe("MPollBody", () => {
|
|||
responseEvent("ps@matrix.org", "wings", 19),
|
||||
]),
|
||||
]);
|
||||
const endRelations = new RelatedRelations([
|
||||
newEndRelations([
|
||||
endEvent("@me:example.com", 25),
|
||||
]),
|
||||
]);
|
||||
const endRelations = new RelatedRelations([newEndRelations([endEvent("@me:example.com", 25)])]);
|
||||
expect(
|
||||
allVotes(
|
||||
{ getRoomId: () => "$room" } as MatrixEvent,
|
||||
MatrixClientPeg.get(),
|
||||
voteRelations,
|
||||
endRelations,
|
||||
),
|
||||
allVotes({ getRoomId: () => "$room" } as MatrixEvent, MatrixClientPeg.get(), voteRelations, endRelations),
|
||||
).toEqual([
|
||||
new UserVote(13, "sf@matrix.org", ["wings"]),
|
||||
new UserVote(13, "id@matrix.org", ["wings"]),
|
||||
|
@ -184,8 +153,7 @@ describe("MPollBody", () => {
|
|||
expect(votesCount(body, "italian")).toBe("");
|
||||
expect(votesCount(body, "wings")).toBe("");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("No votes cast");
|
||||
expect(body.find('h2').html())
|
||||
.toEqual("<h2>What should we order for the party?</h2>");
|
||||
expect(body.find("h2").html()).toEqual("<h2>What should we order for the party?</h2>");
|
||||
});
|
||||
|
||||
it("finds votes from multiple people", () => {
|
||||
|
@ -210,9 +178,7 @@ describe("MPollBody", () => {
|
|||
responseEvent("@catrd:example.com", "poutine"),
|
||||
responseEvent("@dune2:example.com", "wings"),
|
||||
];
|
||||
const ends = [
|
||||
endEvent("@notallowed:example.com", 12),
|
||||
];
|
||||
const ends = [endEvent("@notallowed:example.com", 12)];
|
||||
const body = newMPollBody(votes, ends);
|
||||
|
||||
// Even though an end event was sent, we render the poll as unfinished
|
||||
|
@ -236,27 +202,23 @@ describe("MPollBody", () => {
|
|||
expect(votesCount(body, "poutine")).toBe("");
|
||||
expect(votesCount(body, "italian")).toBe("");
|
||||
expect(votesCount(body, "wings")).toBe("");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe(
|
||||
"4 votes cast. Vote to see the results");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("4 votes cast. Vote to see the results");
|
||||
});
|
||||
|
||||
it("hides a single vote if I have not voted", () => {
|
||||
const votes = [
|
||||
responseEvent("@alice:example.com", "pizza"),
|
||||
];
|
||||
const votes = [responseEvent("@alice:example.com", "pizza")];
|
||||
const body = newMPollBody(votes);
|
||||
expect(votesCount(body, "pizza")).toBe("");
|
||||
expect(votesCount(body, "poutine")).toBe("");
|
||||
expect(votesCount(body, "italian")).toBe("");
|
||||
expect(votesCount(body, "wings")).toBe("");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe(
|
||||
"1 vote cast. Vote to see the results");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("1 vote cast. Vote to see the results");
|
||||
});
|
||||
|
||||
it("takes someone's most recent vote if they voted several times", () => {
|
||||
const votes = [
|
||||
responseEvent("@me:example.com", "pizza", 12),
|
||||
responseEvent("@me:example.com", "wings", 20), // latest me
|
||||
responseEvent("@me:example.com", "wings", 20), // latest me
|
||||
responseEvent("@qbert:example.com", "pizza", 14),
|
||||
responseEvent("@qbert:example.com", "poutine", 16), // latest qbert
|
||||
responseEvent("@qbert:example.com", "wings", 15),
|
||||
|
@ -321,8 +283,7 @@ describe("MPollBody", () => {
|
|||
const votes = [responseEvent("@me:example.com", "pizza", 100)];
|
||||
const body = newMPollBody(votes);
|
||||
const props: IBodyProps = body.instance().props as IBodyProps;
|
||||
const voteRelations = props!.getRelationsForEvent!(
|
||||
"$mypoll", "m.reference", M_POLL_RESPONSE.name);
|
||||
const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name);
|
||||
expect(voteRelations).toBeDefined();
|
||||
clickRadio(body, "pizza");
|
||||
|
||||
|
@ -343,8 +304,7 @@ describe("MPollBody", () => {
|
|||
const votes = [responseEvent("@me:example.com", "pizza")];
|
||||
const body = newMPollBody(votes);
|
||||
const props: IBodyProps = body.instance().props as IBodyProps;
|
||||
const voteRelations = props!.getRelationsForEvent!(
|
||||
"$mypoll", "m.reference", M_POLL_RESPONSE.name);
|
||||
const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name);
|
||||
expect(voteRelations).toBeDefined();
|
||||
clickRadio(body, "pizza");
|
||||
|
||||
|
@ -369,10 +329,7 @@ describe("MPollBody", () => {
|
|||
|
||||
it("highlights my vote even if I did it on another device", () => {
|
||||
// Given I voted italian
|
||||
const votes = [
|
||||
responseEvent("@me:example.com", "italian"),
|
||||
responseEvent("@nf:example.com", "wings"),
|
||||
];
|
||||
const votes = [responseEvent("@me:example.com", "italian"), responseEvent("@nf:example.com", "wings")];
|
||||
const body = newMPollBody(votes);
|
||||
|
||||
// But I didn't click anything locally
|
||||
|
@ -384,10 +341,7 @@ describe("MPollBody", () => {
|
|||
|
||||
it("ignores extra answers", () => {
|
||||
// When cb votes for 2 things, we consider the first only
|
||||
const votes = [
|
||||
responseEvent("@cb:example.com", ["pizza", "wings"]),
|
||||
responseEvent("@me:example.com", "wings"),
|
||||
];
|
||||
const votes = [responseEvent("@cb:example.com", ["pizza", "wings"]), responseEvent("@me:example.com", "wings")];
|
||||
const body = newMPollBody(votes);
|
||||
expect(votesCount(body, "pizza")).toBe("1 vote");
|
||||
expect(votesCount(body, "poutine")).toBe("0 votes");
|
||||
|
@ -470,14 +424,12 @@ describe("MPollBody", () => {
|
|||
|
||||
it("renders the first 20 answers if 21 were given", () => {
|
||||
const answers = Array.from(Array(21).keys()).map((i) => {
|
||||
return { "id": `id${i}`, [M_TEXT.name]: `Name ${i}` };
|
||||
return { id: `id${i}`, [M_TEXT.name]: `Name ${i}` };
|
||||
});
|
||||
const votes = [];
|
||||
const ends = [];
|
||||
const body = newMPollBody(votes, ends, answers);
|
||||
expect(
|
||||
body.find('.mx_MPollBody_option').length,
|
||||
).toBe(20);
|
||||
expect(body.find(".mx_MPollBody_option").length).toBe(20);
|
||||
});
|
||||
|
||||
it("hides scores if I voted but the poll is undisclosed", () => {
|
||||
|
@ -493,8 +445,7 @@ describe("MPollBody", () => {
|
|||
expect(votesCount(body, "poutine")).toBe("");
|
||||
expect(votesCount(body, "italian")).toBe("");
|
||||
expect(votesCount(body, "wings")).toBe("");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe(
|
||||
"Results will be visible when the poll is ended");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Results will be visible when the poll is ended");
|
||||
});
|
||||
|
||||
it("highlights my vote if the poll is undisclosed", () => {
|
||||
|
@ -522,16 +473,13 @@ describe("MPollBody", () => {
|
|||
responseEvent("@catrd:example.com", "poutine"),
|
||||
responseEvent("@dune2:example.com", "wings"),
|
||||
];
|
||||
const ends = [
|
||||
endEvent("@me:example.com", 12),
|
||||
];
|
||||
const ends = [endEvent("@me:example.com", 12)];
|
||||
const body = newMPollBody(votes, ends, null, false);
|
||||
expect(endedVotesCount(body, "pizza")).toBe("3 votes");
|
||||
expect(endedVotesCount(body, "poutine")).toBe("1 vote");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("1 vote");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe(
|
||||
"Final result based on 5 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes");
|
||||
});
|
||||
|
||||
it("sends a vote event when I choose an option", () => {
|
||||
|
@ -548,9 +496,7 @@ describe("MPollBody", () => {
|
|||
clickRadio(body, "wings");
|
||||
clickRadio(body, "wings");
|
||||
clickRadio(body, "wings");
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
...expectedResponseEventCall("wings"),
|
||||
);
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("wings"));
|
||||
});
|
||||
|
||||
it("sends no vote event when I click what I already chose", () => {
|
||||
|
@ -576,13 +522,8 @@ describe("MPollBody", () => {
|
|||
});
|
||||
|
||||
it("sends no events when I click in an ended poll", () => {
|
||||
const ends = [
|
||||
endEvent("@me:example.com", 25),
|
||||
];
|
||||
const votes = [
|
||||
responseEvent("@uy:example.com", "wings", 15),
|
||||
responseEvent("@uy:example.com", "poutine", 15),
|
||||
];
|
||||
const ends = [endEvent("@me:example.com", 25)];
|
||||
const votes = [responseEvent("@uy:example.com", "wings", 15), responseEvent("@uy:example.com", "poutine", 15)];
|
||||
const body = newMPollBody(votes, ends);
|
||||
clickEndedOption(body, "wings");
|
||||
clickEndedOption(body, "italian");
|
||||
|
@ -622,9 +563,7 @@ describe("MPollBody", () => {
|
|||
responseEvent("@fa:example.com", "poutine", 18),
|
||||
responseEvent("@of:example.com", "poutine", 31), // Late
|
||||
];
|
||||
const ends = [
|
||||
endEvent("@me:example.com", 25),
|
||||
];
|
||||
const ends = [endEvent("@me:example.com", 25)];
|
||||
expect(runFindTopAnswer(votes, ends)).toEqual("Italian, Pizza and Poutine");
|
||||
});
|
||||
|
||||
|
@ -646,7 +585,7 @@ describe("MPollBody", () => {
|
|||
it("counts votes as normal if the poll is ended", () => {
|
||||
const votes = [
|
||||
responseEvent("@me:example.com", "pizza", 12),
|
||||
responseEvent("@me:example.com", "wings", 20), // latest me
|
||||
responseEvent("@me:example.com", "wings", 20), // latest me
|
||||
responseEvent("@qbert:example.com", "pizza", 14),
|
||||
responseEvent("@qbert:example.com", "poutine", 16), // latest qbert
|
||||
responseEvent("@qbert:example.com", "wings", 15),
|
||||
|
@ -657,9 +596,7 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("1 vote");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("1 vote");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 2 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 2 votes");
|
||||
});
|
||||
|
||||
it("counts a single vote as normal if the poll is ended", () => {
|
||||
|
@ -670,9 +607,7 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("1 vote");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("0 votes");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 1 vote");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 1 vote");
|
||||
});
|
||||
|
||||
it("shows ended vote counts of different numbers", () => {
|
||||
|
@ -692,18 +627,16 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("3 votes");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 5 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes");
|
||||
});
|
||||
|
||||
it("ignores votes that arrived after poll ended", () => {
|
||||
const votes = [
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@ff:example.com", "wings", 20),
|
||||
responseEvent("@ut:example.com", "wings", 14),
|
||||
responseEvent("@iu:example.com", "wings", 15),
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@wf:example.com", "pizza", 15),
|
||||
responseEvent("@ld:example.com", "pizza", 15),
|
||||
];
|
||||
|
@ -714,23 +647,21 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("3 votes");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 5 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes");
|
||||
});
|
||||
|
||||
it("counts votes that arrived after an unauthorised poll end event", () => {
|
||||
const votes = [
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@ff:example.com", "wings", 20),
|
||||
responseEvent("@ut:example.com", "wings", 14),
|
||||
responseEvent("@iu:example.com", "wings", 15),
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@wf:example.com", "pizza", 15),
|
||||
responseEvent("@ld:example.com", "pizza", 15),
|
||||
];
|
||||
const ends = [
|
||||
endEvent("@unauthorised:example.com", 5), // Should be ignored
|
||||
endEvent("@unauthorised:example.com", 5), // Should be ignored
|
||||
endEvent("@me:example.com", 25),
|
||||
];
|
||||
const body = newMPollBody(votes, ends);
|
||||
|
@ -739,9 +670,7 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("3 votes");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 5 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes");
|
||||
});
|
||||
|
||||
it("ignores votes that arrived after the first end poll event", () => {
|
||||
|
@ -749,11 +678,11 @@ describe("MPollBody", () => {
|
|||
// "Votes sent on or before the end event's timestamp are valid votes"
|
||||
|
||||
const votes = [
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@sd:example.com", "wings", 30), // Late
|
||||
responseEvent("@ff:example.com", "wings", 20),
|
||||
responseEvent("@ut:example.com", "wings", 14),
|
||||
responseEvent("@iu:example.com", "wings", 25), // Just on time
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@iu:example.com", "wings", 25), // Just on time
|
||||
responseEvent("@jf:example.com", "wings", 35), // Late
|
||||
responseEvent("@wf:example.com", "pizza", 15),
|
||||
responseEvent("@ld:example.com", "pizza", 15),
|
||||
];
|
||||
|
@ -768,9 +697,7 @@ describe("MPollBody", () => {
|
|||
expect(endedVotesCount(body, "poutine")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "italian")).toBe("0 votes");
|
||||
expect(endedVotesCount(body, "wings")).toBe("3 votes");
|
||||
expect(
|
||||
body.find(".mx_MPollBody_totalVotes").text(),
|
||||
).toBe("Final result based on 5 votes");
|
||||
expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes");
|
||||
});
|
||||
|
||||
it("highlights the winning vote in an ended poll", () => {
|
||||
|
@ -788,12 +715,8 @@ describe("MPollBody", () => {
|
|||
expect(endedVoteChecked(body, "pizza")).toBe(false);
|
||||
|
||||
// Double-check by looking for the endedOptionWinner class
|
||||
expect(
|
||||
endedVoteDiv(body, "wings").hasClass("mx_MPollBody_endedOptionWinner"),
|
||||
).toBe(true);
|
||||
expect(
|
||||
endedVoteDiv(body, "pizza").hasClass("mx_MPollBody_endedOptionWinner"),
|
||||
).toBe(false);
|
||||
expect(endedVoteDiv(body, "wings").hasClass("mx_MPollBody_endedOptionWinner")).toBe(true);
|
||||
expect(endedVoteDiv(body, "pizza").hasClass("mx_MPollBody_endedOptionWinner")).toBe(false);
|
||||
});
|
||||
|
||||
it("highlights multiple winning votes", () => {
|
||||
|
@ -836,9 +759,9 @@ describe("MPollBody", () => {
|
|||
|
||||
it("says poll is not ended if asking for relations returns undefined", () => {
|
||||
const pollEvent = new MatrixEvent({
|
||||
"event_id": "$mypoll",
|
||||
"room_id": "#myroom:example.com",
|
||||
"content": newPollStart([]),
|
||||
event_id: "$mypoll",
|
||||
room_id: "#myroom:example.com",
|
||||
content: newPollStart([]),
|
||||
});
|
||||
mockClient.getRoom.mockImplementation((_roomId) => {
|
||||
return {
|
||||
|
@ -849,45 +772,38 @@ describe("MPollBody", () => {
|
|||
},
|
||||
} as unknown as Room;
|
||||
});
|
||||
const getRelationsForEvent =
|
||||
(eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
expect(M_POLL_END.matches(eventType)).toBe(true);
|
||||
return undefined;
|
||||
};
|
||||
expect(
|
||||
isPollEnded(
|
||||
pollEvent,
|
||||
MatrixClientPeg.get(),
|
||||
getRelationsForEvent,
|
||||
),
|
||||
).toBe(false);
|
||||
const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
expect(M_POLL_END.matches(eventType)).toBe(true);
|
||||
return undefined;
|
||||
};
|
||||
expect(isPollEnded(pollEvent, MatrixClientPeg.get(), getRelationsForEvent)).toBe(false);
|
||||
});
|
||||
|
||||
it("Displays edited content and new answer IDs if the poll has been edited", () => {
|
||||
const pollEvent = new MatrixEvent({
|
||||
"type": M_POLL_START.name,
|
||||
"event_id": "$mypoll",
|
||||
"room_id": "#myroom:example.com",
|
||||
"content": newPollStart(
|
||||
type: M_POLL_START.name,
|
||||
event_id: "$mypoll",
|
||||
room_id: "#myroom:example.com",
|
||||
content: newPollStart(
|
||||
[
|
||||
{ "id": "o1", [M_TEXT.name]: "old answer 1" },
|
||||
{ "id": "o2", [M_TEXT.name]: "old answer 2" },
|
||||
{ id: "o1", [M_TEXT.name]: "old answer 1" },
|
||||
{ id: "o2", [M_TEXT.name]: "old answer 2" },
|
||||
],
|
||||
"old question",
|
||||
),
|
||||
});
|
||||
const replacingEvent = new MatrixEvent({
|
||||
"type": M_POLL_START.name,
|
||||
"event_id": "$mypollreplacement",
|
||||
"room_id": "#myroom:example.com",
|
||||
"content": {
|
||||
type: M_POLL_START.name,
|
||||
event_id: "$mypollreplacement",
|
||||
room_id: "#myroom:example.com",
|
||||
content: {
|
||||
"m.new_content": newPollStart(
|
||||
[
|
||||
{ "id": "n1", [M_TEXT.name]: "new answer 1" },
|
||||
{ "id": "n2", [M_TEXT.name]: "new answer 2" },
|
||||
{ "id": "n3", [M_TEXT.name]: "new answer 3" },
|
||||
{ id: "n1", [M_TEXT.name]: "new answer 1" },
|
||||
{ id: "n2", [M_TEXT.name]: "new answer 2" },
|
||||
{ id: "n3", [M_TEXT.name]: "new answer 3" },
|
||||
],
|
||||
"new question",
|
||||
),
|
||||
|
@ -895,18 +811,15 @@ describe("MPollBody", () => {
|
|||
});
|
||||
pollEvent.makeReplaced(replacingEvent);
|
||||
const body = newMPollBodyFromEvent(pollEvent, []);
|
||||
expect(body.find('h2').html())
|
||||
.toEqual(
|
||||
"<h2>new question"
|
||||
+ "<span class=\"mx_MPollBody_edited\"> (edited)</span>"
|
||||
+ "</h2>",
|
||||
);
|
||||
expect(body.find("h2").html()).toEqual(
|
||||
"<h2>new question" + '<span class="mx_MPollBody_edited"> (edited)</span>' + "</h2>",
|
||||
);
|
||||
const inputs = body.find('input[type="radio"]');
|
||||
expect(inputs).toHaveLength(3);
|
||||
expect(inputs.at(0).prop("value")).toEqual("n1");
|
||||
expect(inputs.at(1).prop("value")).toEqual("n2");
|
||||
expect(inputs.at(2).prop("value")).toEqual("n3");
|
||||
const options = body.find('.mx_MPollBody_optionText');
|
||||
const options = body.find(".mx_MPollBody_optionText");
|
||||
expect(options).toHaveLength(3);
|
||||
expect(options.at(0).text()).toEqual("new answer 1");
|
||||
expect(options.at(1).text()).toEqual("new answer 2");
|
||||
|
@ -1027,10 +940,7 @@ function newEndRelations(relationEvents: Array<MatrixEvent>): Relations {
|
|||
return newRelations(relationEvents, M_POLL_END.name);
|
||||
}
|
||||
|
||||
function newRelations(
|
||||
relationEvents: Array<MatrixEvent>,
|
||||
eventType: string,
|
||||
): Relations {
|
||||
function newRelations(relationEvents: Array<MatrixEvent>, eventType: string): Relations {
|
||||
const voteRelations = new Relations("m.reference", eventType, null);
|
||||
for (const ev of relationEvents) {
|
||||
voteRelations.addEvent(ev);
|
||||
|
@ -1045,10 +955,10 @@ function newMPollBody(
|
|||
disclosed = true,
|
||||
): ReactWrapper {
|
||||
const mxEvent = new MatrixEvent({
|
||||
"type": M_POLL_START.name,
|
||||
"event_id": "$mypoll",
|
||||
"room_id": "#myroom:example.com",
|
||||
"content": newPollStart(answers, null, disclosed),
|
||||
type: M_POLL_START.name,
|
||||
event_id: "$mypoll",
|
||||
room_id: "#myroom:example.com",
|
||||
content: newPollStart(answers, null, disclosed),
|
||||
});
|
||||
return newMPollBodyFromEvent(mxEvent, relationEvents, endEvents);
|
||||
}
|
||||
|
@ -1060,10 +970,10 @@ function newMPollBodyFromEvent(
|
|||
): ReactWrapper {
|
||||
const voteRelations = newVoteRelations(relationEvents);
|
||||
const endRelations = newEndRelations(endEvents);
|
||||
return mount(<MPollBody
|
||||
mxEvent={mxEvent}
|
||||
getRelationsForEvent={
|
||||
(eventId: string, relationType: string, eventType: string) => {
|
||||
return mount(
|
||||
<MPollBody
|
||||
mxEvent={mxEvent}
|
||||
getRelationsForEvent={(eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
if (M_POLL_RESPONSE.matches(eventType)) {
|
||||
|
@ -1073,22 +983,22 @@ function newMPollBodyFromEvent(
|
|||
} else {
|
||||
fail("Unexpected eventType: " + eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't use any of these props, but they're required.
|
||||
highlightLink="unused"
|
||||
highlights={[]}
|
||||
mediaEventHelper={null}
|
||||
onHeightChanged={() => {}}
|
||||
onMessageAllowed={() => {}}
|
||||
permalinkCreator={null}
|
||||
/>, {
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: {
|
||||
value: mockClient,
|
||||
}}
|
||||
// We don't use any of these props, but they're required.
|
||||
highlightLink="unused"
|
||||
highlights={[]}
|
||||
mediaEventHelper={null}
|
||||
onHeightChanged={() => {}}
|
||||
onMessageAllowed={() => {}}
|
||||
permalinkCreator={null}
|
||||
/>,
|
||||
{
|
||||
wrappingComponent: MatrixClientContext.Provider,
|
||||
wrappingComponentProps: {
|
||||
value: mockClient,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
function clickRadio(wrapper: ReactWrapper, value: string) {
|
||||
|
@ -1104,21 +1014,15 @@ function clickEndedOption(wrapper: ReactWrapper, value: string) {
|
|||
}
|
||||
|
||||
function voteButton(wrapper: ReactWrapper, value: string): ReactWrapper {
|
||||
return wrapper.find(
|
||||
`div.mx_MPollBody_option`,
|
||||
).findWhere(w => w.key() === value);
|
||||
return wrapper.find(`div.mx_MPollBody_option`).findWhere((w) => w.key() === value);
|
||||
}
|
||||
|
||||
function votesCount(wrapper: ReactWrapper, value: string): string {
|
||||
return wrapper.find(
|
||||
`StyledRadioButton[value="${value}"] .mx_MPollBody_optionVoteCount`,
|
||||
).text();
|
||||
return wrapper.find(`StyledRadioButton[value="${value}"] .mx_MPollBody_optionVoteCount`).text();
|
||||
}
|
||||
|
||||
function endedVoteChecked(wrapper: ReactWrapper, value: string): boolean {
|
||||
return endedVoteDiv(wrapper, value)
|
||||
.closest(".mx_MPollBody_option")
|
||||
.hasClass("mx_MPollBody_option_checked");
|
||||
return endedVoteDiv(wrapper, value).closest(".mx_MPollBody_option").hasClass("mx_MPollBody_option_checked");
|
||||
}
|
||||
|
||||
function endedVoteDiv(wrapper: ReactWrapper, value: string): ReactWrapper {
|
||||
|
@ -1126,22 +1030,16 @@ function endedVoteDiv(wrapper: ReactWrapper, value: string): ReactWrapper {
|
|||
}
|
||||
|
||||
function endedVotesCount(wrapper: ReactWrapper, value: string): string {
|
||||
return wrapper.find(
|
||||
`div[data-value="${value}"] .mx_MPollBody_optionVoteCount`,
|
||||
).text();
|
||||
return wrapper.find(`div[data-value="${value}"] .mx_MPollBody_optionVoteCount`).text();
|
||||
}
|
||||
|
||||
function newPollStart(
|
||||
answers?: POLL_ANSWER[],
|
||||
question?: string,
|
||||
disclosed = true,
|
||||
): M_POLL_START_EVENT_CONTENT {
|
||||
function newPollStart(answers?: POLL_ANSWER[], question?: string, disclosed = true): M_POLL_START_EVENT_CONTENT {
|
||||
if (!answers) {
|
||||
answers = [
|
||||
{ "id": "pizza", [M_TEXT.name]: "Pizza" },
|
||||
{ "id": "poutine", [M_TEXT.name]: "Poutine" },
|
||||
{ "id": "italian", [M_TEXT.name]: "Italian" },
|
||||
{ "id": "wings", [M_TEXT.name]: "Wings" },
|
||||
{ id: "pizza", [M_TEXT.name]: "Pizza" },
|
||||
{ id: "poutine", [M_TEXT.name]: "Poutine" },
|
||||
{ id: "italian", [M_TEXT.name]: "Italian" },
|
||||
{ id: "wings", [M_TEXT.name]: "Wings" },
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1149,43 +1047,35 @@ function newPollStart(
|
|||
question = "What should we order for the party?";
|
||||
}
|
||||
|
||||
const answersFallback = answers
|
||||
.map((a, i) => `${i + 1}. ${a[M_TEXT.name]}`)
|
||||
.join("\n");
|
||||
const answersFallback = answers.map((a, i) => `${i + 1}. ${a[M_TEXT.name]}`).join("\n");
|
||||
|
||||
const fallback = `${question}\n${answersFallback}`;
|
||||
|
||||
return {
|
||||
[M_POLL_START.name]: {
|
||||
"question": {
|
||||
question: {
|
||||
[M_TEXT.name]: question,
|
||||
},
|
||||
"kind": (
|
||||
disclosed
|
||||
? M_POLL_KIND_DISCLOSED.name
|
||||
: M_POLL_KIND_UNDISCLOSED.name
|
||||
),
|
||||
"answers": answers,
|
||||
kind: disclosed ? M_POLL_KIND_DISCLOSED.name : M_POLL_KIND_UNDISCLOSED.name,
|
||||
answers: answers,
|
||||
},
|
||||
[M_TEXT.name]: fallback,
|
||||
};
|
||||
}
|
||||
|
||||
function badResponseEvent(): MatrixEvent {
|
||||
return new MatrixEvent(
|
||||
{
|
||||
"event_id": nextId(),
|
||||
"type": M_POLL_RESPONSE.name,
|
||||
"sender": "@malicious:example.com",
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$mypoll",
|
||||
},
|
||||
// Does not actually contain a response
|
||||
return new MatrixEvent({
|
||||
event_id: nextId(),
|
||||
type: M_POLL_RESPONSE.name,
|
||||
sender: "@malicious:example.com",
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
rel_type: "m.reference",
|
||||
event_id: "$mypoll",
|
||||
},
|
||||
// Does not actually contain a response
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function responseEvent(
|
||||
|
@ -1194,116 +1084,103 @@ function responseEvent(
|
|||
ts = 0,
|
||||
): MatrixEvent {
|
||||
const ans = typeof answers === "string" ? [answers] : answers;
|
||||
return new MatrixEvent(
|
||||
{
|
||||
"event_id": nextId(),
|
||||
"room_id": "#myroom:example.com",
|
||||
"origin_server_ts": ts,
|
||||
"type": M_POLL_RESPONSE.name,
|
||||
"sender": sender,
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$mypoll",
|
||||
},
|
||||
[M_POLL_RESPONSE.name]: {
|
||||
"answers": ans,
|
||||
},
|
||||
return new MatrixEvent({
|
||||
event_id: nextId(),
|
||||
room_id: "#myroom:example.com",
|
||||
origin_server_ts: ts,
|
||||
type: M_POLL_RESPONSE.name,
|
||||
sender: sender,
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
rel_type: "m.reference",
|
||||
event_id: "$mypoll",
|
||||
},
|
||||
[M_POLL_RESPONSE.name]: {
|
||||
answers: ans,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function expectedResponseEvent(answer: string) {
|
||||
return {
|
||||
"content": {
|
||||
content: {
|
||||
[M_POLL_RESPONSE.name]: {
|
||||
"answers": [answer],
|
||||
answers: [answer],
|
||||
},
|
||||
"m.relates_to": {
|
||||
"event_id": "$mypoll",
|
||||
"rel_type": "m.reference",
|
||||
event_id: "$mypoll",
|
||||
rel_type: "m.reference",
|
||||
},
|
||||
},
|
||||
"roomId": "#myroom:example.com",
|
||||
"eventType": M_POLL_RESPONSE.name,
|
||||
"txnId": undefined,
|
||||
"callback": undefined,
|
||||
roomId: "#myroom:example.com",
|
||||
eventType: M_POLL_RESPONSE.name,
|
||||
txnId: undefined,
|
||||
callback: undefined,
|
||||
};
|
||||
}
|
||||
function expectedResponseEventCall(answer: string) {
|
||||
const {
|
||||
content, roomId, eventType,
|
||||
} = expectedResponseEvent(answer);
|
||||
return [
|
||||
roomId, eventType, content,
|
||||
];
|
||||
const { content, roomId, eventType } = expectedResponseEvent(answer);
|
||||
return [roomId, eventType, content];
|
||||
}
|
||||
|
||||
function endEvent(
|
||||
sender = "@me:example.com",
|
||||
ts = 0,
|
||||
): MatrixEvent {
|
||||
return new MatrixEvent(
|
||||
{
|
||||
"event_id": nextId(),
|
||||
"room_id": "#myroom:example.com",
|
||||
"origin_server_ts": ts,
|
||||
"type": M_POLL_END.name,
|
||||
"sender": sender,
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$mypoll",
|
||||
},
|
||||
[M_POLL_END.name]: {},
|
||||
[M_TEXT.name]: "The poll has ended. Something.",
|
||||
function endEvent(sender = "@me:example.com", ts = 0): MatrixEvent {
|
||||
return new MatrixEvent({
|
||||
event_id: nextId(),
|
||||
room_id: "#myroom:example.com",
|
||||
origin_server_ts: ts,
|
||||
type: M_POLL_END.name,
|
||||
sender: sender,
|
||||
content: {
|
||||
"m.relates_to": {
|
||||
rel_type: "m.reference",
|
||||
event_id: "$mypoll",
|
||||
},
|
||||
[M_POLL_END.name]: {},
|
||||
[M_TEXT.name]: "The poll has ended. Something.",
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function runIsPollEnded(ends: MatrixEvent[]) {
|
||||
const pollEvent = new MatrixEvent({
|
||||
"event_id": "$mypoll",
|
||||
"room_id": "#myroom:example.com",
|
||||
"type": M_POLL_START.name,
|
||||
"content": newPollStart(),
|
||||
event_id: "$mypoll",
|
||||
room_id: "#myroom:example.com",
|
||||
type: M_POLL_START.name,
|
||||
content: newPollStart(),
|
||||
});
|
||||
|
||||
setRedactionAllowedForMeOnly(mockClient);
|
||||
|
||||
const getRelationsForEvent =
|
||||
(eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
expect(M_POLL_END.matches(eventType)).toBe(true);
|
||||
return newEndRelations(ends);
|
||||
};
|
||||
const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
expect(M_POLL_END.matches(eventType)).toBe(true);
|
||||
return newEndRelations(ends);
|
||||
};
|
||||
|
||||
return isPollEnded(pollEvent, mockClient, getRelationsForEvent);
|
||||
}
|
||||
|
||||
function runFindTopAnswer(votes: MatrixEvent[], ends: MatrixEvent[]) {
|
||||
const pollEvent = new MatrixEvent({
|
||||
"event_id": "$mypoll",
|
||||
"room_id": "#myroom:example.com",
|
||||
"type": M_POLL_START.name,
|
||||
"content": newPollStart(),
|
||||
event_id: "$mypoll",
|
||||
room_id: "#myroom:example.com",
|
||||
type: M_POLL_START.name,
|
||||
content: newPollStart(),
|
||||
});
|
||||
|
||||
const getRelationsForEvent =
|
||||
(eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
if (M_POLL_RESPONSE.matches(eventType)) {
|
||||
return newVoteRelations(votes);
|
||||
} else if (M_POLL_END.matches(eventType)) {
|
||||
return newEndRelations(ends);
|
||||
} else {
|
||||
fail(`eventType should be end or vote but was ${eventType}`);
|
||||
}
|
||||
};
|
||||
const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => {
|
||||
expect(eventId).toBe("$mypoll");
|
||||
expect(relationType).toBe("m.reference");
|
||||
if (M_POLL_RESPONSE.matches(eventType)) {
|
||||
return newVoteRelations(votes);
|
||||
} else if (M_POLL_END.matches(eventType)) {
|
||||
return newEndRelations(ends);
|
||||
} else {
|
||||
fail(`eventType should be end or vote but was ${eventType}`);
|
||||
}
|
||||
};
|
||||
|
||||
return findTopAnswer(pollEvent, MatrixClientPeg.get(), getRelationsForEvent);
|
||||
}
|
||||
|
|
|
@ -14,25 +14,26 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MatrixEvent } from 'matrix-js-sdk/src/matrix';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";
|
||||
import { getMockClientWithEventEmitter } from '../../../test-utils';
|
||||
import MVideoBody from '../../../../src/components/views/messages/MVideoBody';
|
||||
import { getMockClientWithEventEmitter } from "../../../test-utils";
|
||||
import MVideoBody from "../../../../src/components/views/messages/MVideoBody";
|
||||
|
||||
jest.mock(
|
||||
"../../../../src/customisations/Media",
|
||||
() => {
|
||||
return { mediaFromContent: () => { return { isEncrypted: false }; } };
|
||||
},
|
||||
);
|
||||
jest.mock("../../../../src/customisations/Media", () => {
|
||||
return {
|
||||
mediaFromContent: () => {
|
||||
return { isEncrypted: false };
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("MVideoBody", () => {
|
||||
it('does not crash when given a portrait image', () => {
|
||||
it("does not crash when given a portrait image", () => {
|
||||
// Check for an unreliable crash caused by a fractional-sized
|
||||
// image dimension being used for a CanvasImageData.
|
||||
const { asFragment } = makeMVideoBody(720, 1280);
|
||||
|
@ -68,7 +69,7 @@ function makeMVideoBody(w: number, h: number): RenderResult {
|
|||
const defaultProps = {
|
||||
mxEvent: event,
|
||||
highlights: [],
|
||||
highlightLink: '',
|
||||
highlightLink: "",
|
||||
onHeightChanged: jest.fn(),
|
||||
onMessageAllowed: jest.fn(),
|
||||
permalinkCreator: {} as RoomPermalinkCreator,
|
||||
|
|
|
@ -14,57 +14,50 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { act } from 'react-test-renderer';
|
||||
import {
|
||||
EventType,
|
||||
EventStatus,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
MsgType,
|
||||
Room,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { FeatureSupport, Thread } from 'matrix-js-sdk/src/models/thread';
|
||||
import React from "react";
|
||||
import { render, fireEvent } from "@testing-library/react";
|
||||
import { act } from "react-test-renderer";
|
||||
import { EventType, EventStatus, MatrixEvent, MatrixEventEvent, MsgType, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread";
|
||||
|
||||
import MessageActionBar from '../../../../src/components/views/messages/MessageActionBar';
|
||||
import MessageActionBar from "../../../../src/components/views/messages/MessageActionBar";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
mockClientMethodsEvents,
|
||||
makeBeaconInfoEvent,
|
||||
} from '../../../test-utils';
|
||||
import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../../src/contexts/RoomContext';
|
||||
import { IRoomState } from '../../../../src/components/structures/RoomView';
|
||||
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';
|
||||
} from "../../../test-utils";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../src/components/structures/RoomView";
|
||||
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";
|
||||
|
||||
jest.mock('../../../../src/dispatcher/dispatcher');
|
||||
jest.mock("../../../../src/dispatcher/dispatcher");
|
||||
|
||||
describe('<MessageActionBar />', () => {
|
||||
const userId = '@alice:server.org';
|
||||
const roomId = '!room:server.org';
|
||||
describe("<MessageActionBar />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const roomId = "!room:server.org";
|
||||
const alicesMessageEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
body: "Hello",
|
||||
},
|
||||
event_id: "$alices_message",
|
||||
});
|
||||
|
||||
const bobsMessageEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: '@bob:server.org',
|
||||
sender: "@bob:server.org",
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'I am bob',
|
||||
body: "I am bob",
|
||||
},
|
||||
event_id: "$bobs_message",
|
||||
});
|
||||
|
@ -84,7 +77,7 @@ describe('<MessageActionBar />', () => {
|
|||
const localStorageMock = (() => {
|
||||
let store = {};
|
||||
return {
|
||||
getItem: jest.fn().mockImplementation(key => store[key] ?? null),
|
||||
getItem: jest.fn().mockImplementation((key) => store[key] ?? null),
|
||||
setItem: jest.fn().mockImplementation((key, value) => {
|
||||
store[key] = value;
|
||||
}),
|
||||
|
@ -94,13 +87,13 @@ describe('<MessageActionBar />', () => {
|
|||
removeItem: jest.fn().mockImplementation((key) => delete store[key]),
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
value: localStorageMock,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
const room = new Room(roomId, client, userId);
|
||||
jest.spyOn(room, 'getPendingEvents').mockReturnValue([]);
|
||||
jest.spyOn(room, "getPendingEvents").mockReturnValue([]);
|
||||
|
||||
client.getRoom.mockReturnValue(room);
|
||||
|
||||
|
@ -121,22 +114,23 @@ describe('<MessageActionBar />', () => {
|
|||
render(
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
|
||||
<MessageActionBar {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>);
|
||||
</RoomContext.Provider>,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
alicesMessageEvent.setStatus(EventStatus.SENT);
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, 'setValue').mockResolvedValue(undefined);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockRestore();
|
||||
jest.spyOn(SettingsStore, 'setValue').mockRestore();
|
||||
jest.spyOn(SettingsStore, "getValue").mockRestore();
|
||||
jest.spyOn(SettingsStore, "setValue").mockRestore();
|
||||
});
|
||||
|
||||
it('kills event listeners on unmount', () => {
|
||||
const offSpy = jest.spyOn(alicesMessageEvent, 'off').mockClear();
|
||||
it("kills event listeners on unmount", () => {
|
||||
const offSpy = jest.spyOn(alicesMessageEvent, "off").mockClear();
|
||||
const wrapper = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
|
@ -150,24 +144,24 @@ describe('<MessageActionBar />', () => {
|
|||
expect(client.decryptEventIfNeeded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('decryption', () => {
|
||||
it('decrypts event if needed', () => {
|
||||
describe("decryption", () => {
|
||||
it("decrypts event if needed", () => {
|
||||
getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates component on decrypted event', () => {
|
||||
it("updates component on decrypted event", () => {
|
||||
const decryptingEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {},
|
||||
});
|
||||
jest.spyOn(decryptingEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||
jest.spyOn(decryptingEvent, "isBeingDecrypted").mockReturnValue(true);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: decryptingEvent });
|
||||
|
||||
// still encrypted event is not actionable => no reply button
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
expect(queryByLabelText("Reply")).toBeFalsy();
|
||||
|
||||
act(() => {
|
||||
// ''decrypt'' the event
|
||||
|
@ -177,46 +171,46 @@ describe('<MessageActionBar />', () => {
|
|||
});
|
||||
|
||||
// new available actions after decryption
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
expect(queryByLabelText("Reply")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('status', () => {
|
||||
it('updates component when event status changes', () => {
|
||||
describe("status", () => {
|
||||
it("updates component when event status changes", () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.QUEUED);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
// pending event status, cancel action available
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
alicesMessageEvent.setStatus(EventStatus.SENT);
|
||||
});
|
||||
|
||||
// event is sent, no longer cancelable
|
||||
expect(queryByLabelText('Delete')).toBeFalsy();
|
||||
expect(queryByLabelText("Delete")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('redaction', () => {
|
||||
describe("redaction", () => {
|
||||
// this doesn't do what it's supposed to
|
||||
// because beforeRedaction event is fired... before redaction
|
||||
// event is unchanged at point when this component updates
|
||||
// TODO file bug
|
||||
xit('updates component on before redaction event', () => {
|
||||
xit("updates component on before redaction event", () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
body: "Hello",
|
||||
},
|
||||
});
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
|
||||
// no pending redaction => no delete button
|
||||
expect(queryByLabelText('Delete')).toBeFalsy();
|
||||
expect(queryByLabelText("Delete")).toBeFalsy();
|
||||
|
||||
act(() => {
|
||||
const redactionEvent = new MatrixEvent({
|
||||
|
@ -229,110 +223,110 @@ describe('<MessageActionBar />', () => {
|
|||
});
|
||||
|
||||
// updated with local redaction event, delete now available
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('options button', () => {
|
||||
it('renders options menu', () => {
|
||||
describe("options button", () => {
|
||||
it("renders options menu", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Options')).toBeTruthy();
|
||||
expect(queryByLabelText("Options")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens message context menu on click', () => {
|
||||
it("opens message context menu on click", () => {
|
||||
const { getByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('Options'));
|
||||
fireEvent.click(queryByLabelText("Options"));
|
||||
});
|
||||
expect(getByTestId('mx_MessageContextMenu')).toBeTruthy();
|
||||
expect(getByTestId("mx_MessageContextMenu")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reply button', () => {
|
||||
it('renders reply button on own actionable event', () => {
|
||||
describe("reply button", () => {
|
||||
it("renders reply button on own actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
expect(queryByLabelText("Reply")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders reply button on others actionable event', () => {
|
||||
it("renders reply button on others actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }, { canSendMessages: true });
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
expect(queryByLabelText("Reply")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render reply button on non-actionable event', () => {
|
||||
it("does not render reply button on non-actionable event", () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent });
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
expect(queryByLabelText("Reply")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render reply button when user cannot send messaged', () => {
|
||||
it("does not render reply button when user cannot send messaged", () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canSendMessages: false });
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
expect(queryByLabelText("Reply")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('dispatches reply event on click', () => {
|
||||
it("dispatches reply event on click", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('Reply'));
|
||||
fireEvent.click(queryByLabelText("Reply"));
|
||||
});
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: 'reply_to_event',
|
||||
action: "reply_to_event",
|
||||
event: alicesMessageEvent,
|
||||
context: TimelineRenderingType.Room,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('react button', () => {
|
||||
it('renders react button on own actionable event', () => {
|
||||
describe("react button", () => {
|
||||
it("renders react button on own actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('React')).toBeTruthy();
|
||||
expect(queryByLabelText("React")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders react button on others actionable event', () => {
|
||||
it("renders react button on others actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent });
|
||||
expect(queryByLabelText('React')).toBeTruthy();
|
||||
expect(queryByLabelText("React")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render react button on non-actionable event', () => {
|
||||
it("does not render react button on non-actionable event", () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent });
|
||||
expect(queryByLabelText('React')).toBeFalsy();
|
||||
expect(queryByLabelText("React")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render react button when user cannot react', () => {
|
||||
it("does not render react button when user cannot react", () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canReact: false });
|
||||
expect(queryByLabelText('React')).toBeFalsy();
|
||||
expect(queryByLabelText("React")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('opens reaction picker on click', () => {
|
||||
it("opens reaction picker on click", () => {
|
||||
const { queryByLabelText, getByTestId } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('React'));
|
||||
fireEvent.click(queryByLabelText("React"));
|
||||
});
|
||||
expect(getByTestId('mx_EmojiPicker')).toBeTruthy();
|
||||
expect(getByTestId("mx_EmojiPicker")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancel button', () => {
|
||||
it('renders cancel button for an event with a cancelable status', () => {
|
||||
describe("cancel button", () => {
|
||||
it("renders cancel button for an event with a cancelable status", () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.QUEUED);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel button for an event with a pending edit', () => {
|
||||
it("renders cancel button for an event with a pending edit", () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
body: "Hello",
|
||||
},
|
||||
});
|
||||
event.setStatus(EventStatus.SENT);
|
||||
|
@ -342,23 +336,23 @@ describe('<MessageActionBar />', () => {
|
|||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'replacing event body',
|
||||
body: "replacing event body",
|
||||
},
|
||||
});
|
||||
replacingEvent.setStatus(EventStatus.QUEUED);
|
||||
event.makeReplaced(replacingEvent);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel button for an event with a pending redaction', () => {
|
||||
it("renders cancel button for an event with a pending redaction", () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
body: "Hello",
|
||||
},
|
||||
});
|
||||
event.setStatus(EventStatus.SENT);
|
||||
|
@ -372,45 +366,45 @@ describe('<MessageActionBar />', () => {
|
|||
|
||||
event.markLocallyRedacted(redactionEvent);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel and retry button for an event with NOT_SENT status', () => {
|
||||
it("renders cancel and retry button for an event with NOT_SENT status", () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.NOT_SENT);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Retry')).toBeTruthy();
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
expect(queryByLabelText("Retry")).toBeTruthy();
|
||||
expect(queryByLabelText("Delete")).toBeTruthy();
|
||||
});
|
||||
|
||||
it.todo('unsends event on cancel click');
|
||||
it.todo('retrys event on retry click');
|
||||
it.todo("unsends event on cancel click");
|
||||
it.todo("retrys event on retry click");
|
||||
});
|
||||
|
||||
describe('thread button', () => {
|
||||
describe("thread button", () => {
|
||||
beforeEach(() => {
|
||||
Thread.setServerSideSupport(FeatureSupport.Stable);
|
||||
});
|
||||
|
||||
describe('when threads feature is not enabled', () => {
|
||||
it('does not render thread button when threads does not have server support', () => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
describe("when threads feature is not enabled", () => {
|
||||
it("does not render thread button when threads does not have server support", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
Thread.setServerSideSupport(FeatureSupport.None);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Reply in thread')).toBeFalsy();
|
||||
expect(queryByLabelText("Reply in thread")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders thread button when threads has server support', () => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
it("renders thread button when threads has server support", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Reply in thread')).toBeTruthy();
|
||||
expect(queryByLabelText("Reply in thread")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens user settings on click', () => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
it("opens user settings on click", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText('Reply in thread'));
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
|
@ -420,27 +414,27 @@ describe('<MessageActionBar />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when threads feature is enabled', () => {
|
||||
describe("when threads feature is enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockImplementation(setting => setting === 'feature_thread');
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_thread");
|
||||
});
|
||||
|
||||
it('renders thread button on own actionable event', () => {
|
||||
it("renders thread button on own actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Reply in thread')).toBeTruthy();
|
||||
expect(queryByLabelText("Reply in thread")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render thread button for a beacon_info event', () => {
|
||||
it("does not render thread button for a beacon_info event", () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(userId, roomId);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(queryByLabelText('Reply in thread')).toBeFalsy();
|
||||
expect(queryByLabelText("Reply in thread")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('opens thread on click', () => {
|
||||
it("opens thread on click", () => {
|
||||
const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText('Reply in thread'));
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
|
@ -450,26 +444,26 @@ describe('<MessageActionBar />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('opens parent thread for a thread reply message', () => {
|
||||
it("opens parent thread for a thread reply message", () => {
|
||||
const threadReplyEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'this is a thread reply',
|
||||
body: "this is a thread reply",
|
||||
},
|
||||
});
|
||||
// mock the thread stuff
|
||||
jest.spyOn(threadReplyEvent, 'isThreadRoot', 'get').mockReturnValue(false);
|
||||
jest.spyOn(threadReplyEvent, "isThreadRoot", "get").mockReturnValue(false);
|
||||
// set alicesMessageEvent as the root event
|
||||
jest.spyOn(threadReplyEvent, 'getThread').mockReturnValue(
|
||||
{ rootEvent: alicesMessageEvent } as unknown as Thread,
|
||||
);
|
||||
jest.spyOn(threadReplyEvent, "getThread").mockReturnValue({
|
||||
rootEvent: alicesMessageEvent,
|
||||
} as unknown as Thread);
|
||||
const { getByLabelText } = getComponent({ mxEvent: threadReplyEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText('Reply in thread'));
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
|
@ -484,113 +478,115 @@ describe('<MessageActionBar />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('favourite button', () => {
|
||||
describe("favourite button", () => {
|
||||
//for multiple event usecase
|
||||
const favButton = (evt: MatrixEvent) => {
|
||||
return getComponent({ mxEvent: evt }).getByTestId(evt.getId());
|
||||
};
|
||||
|
||||
describe('when favourite_messages feature is enabled', () => {
|
||||
describe("when favourite_messages feature is enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue')
|
||||
.mockImplementation(setting => setting === 'feature_favourite_messages');
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(setting) => setting === "feature_favourite_messages",
|
||||
);
|
||||
localStorageMock.clear();
|
||||
});
|
||||
|
||||
it('renders favourite button on own actionable event', () => {
|
||||
it("renders favourite button on own actionable event", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Favourite')).toBeTruthy();
|
||||
expect(queryByLabelText("Favourite")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders favourite button on other actionable events', () => {
|
||||
it("renders favourite button on other actionable events", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent });
|
||||
expect(queryByLabelText('Favourite')).toBeTruthy();
|
||||
expect(queryByLabelText("Favourite")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render Favourite button on non-actionable event', () => {
|
||||
it("does not render Favourite button on non-actionable event", () => {
|
||||
//redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent });
|
||||
expect(queryByLabelText('Favourite')).toBeFalsy();
|
||||
expect(queryByLabelText("Favourite")).toBeFalsy();
|
||||
});
|
||||
|
||||
it('remembers favourited state of multiple events, and handles the localStorage of the events accordingly',
|
||||
() => {
|
||||
const alicesAction = favButton(alicesMessageEvent);
|
||||
const bobsAction = favButton(bobsMessageEvent);
|
||||
it("remembers favourited state of multiple events, and handles the localStorage of the events accordingly", () => {
|
||||
const alicesAction = favButton(alicesMessageEvent);
|
||||
const bobsAction = favButton(bobsMessageEvent);
|
||||
|
||||
//default state before being clicked
|
||||
expect(alicesAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(localStorageMock.getItem('io_element_favouriteMessages')).toBeNull();
|
||||
//default state before being clicked
|
||||
expect(alicesAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(localStorageMock.getItem("io_element_favouriteMessages")).toBeNull();
|
||||
|
||||
//if only alice's event is fired
|
||||
act(() => {
|
||||
fireEvent.click(alicesAction);
|
||||
});
|
||||
|
||||
expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(localStorageMock.setItem)
|
||||
.toHaveBeenCalledWith('io_element_favouriteMessages', '["$alices_message"]');
|
||||
|
||||
//when bob's event is fired,both should be styled and stored in localStorage
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
|
||||
expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(bobsAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(localStorageMock.setItem)
|
||||
.toHaveBeenCalledWith('io_element_favouriteMessages', '["$alices_message","$bobs_message"]');
|
||||
|
||||
//finally, at this point the localStorage should contain the two eventids
|
||||
expect(localStorageMock.getItem('io_element_favouriteMessages'))
|
||||
.toEqual('["$alices_message","$bobs_message"]');
|
||||
|
||||
//if decided to unfavourite bob's event by clicking again
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar');
|
||||
expect(localStorageMock.getItem('io_element_favouriteMessages')).toEqual('["$alices_message"]');
|
||||
//if only alice's event is fired
|
||||
act(() => {
|
||||
fireEvent.click(alicesAction);
|
||||
});
|
||||
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
"io_element_favouriteMessages",
|
||||
'["$alices_message"]',
|
||||
);
|
||||
|
||||
//when bob's event is fired,both should be styled and stored in localStorage
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(bobsAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
||||
"io_element_favouriteMessages",
|
||||
'["$alices_message","$bobs_message"]',
|
||||
);
|
||||
|
||||
//finally, at this point the localStorage should contain the two eventids
|
||||
expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual(
|
||||
'["$alices_message","$bobs_message"]',
|
||||
);
|
||||
|
||||
//if decided to unfavourite bob's event by clicking again
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual('["$alices_message"]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when favourite_messages feature is disabled', () => {
|
||||
it('does not render', () => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
describe("when favourite_messages feature is disabled", () => {
|
||||
it("does not render", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Favourite')).toBeFalsy();
|
||||
expect(queryByLabelText("Favourite")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["React"],
|
||||
["Reply"],
|
||||
["Reply in thread"],
|
||||
["Favourite"],
|
||||
["Edit"],
|
||||
])("does not show context menu when right-clicking", (buttonLabel: string) => {
|
||||
// For favourite button
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(true);
|
||||
it.each([["React"], ["Reply"], ["Reply in thread"], ["Favourite"], ["Edit"]])(
|
||||
"does not show context menu when right-clicking",
|
||||
(buttonLabel: string) => {
|
||||
// For favourite button
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
|
||||
const event = new MouseEvent("contextmenu", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.stopPropagation = jest.fn();
|
||||
event.preventDefault = jest.fn();
|
||||
const event = new MouseEvent("contextmenu", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.stopPropagation = jest.fn();
|
||||
event.preventDefault = jest.fn();
|
||||
|
||||
const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent(queryByLabelText(buttonLabel), event);
|
||||
});
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(queryByTestId("mx_MessageContextMenu")).toBeFalsy();
|
||||
});
|
||||
const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent(queryByLabelText(buttonLabel), event);
|
||||
});
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(queryByTestId("mx_MessageContextMenu")).toBeFalsy();
|
||||
},
|
||||
);
|
||||
|
||||
it("does shows context menu when right-clicking options", () => {
|
||||
const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
|
|
@ -26,11 +26,11 @@ import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalink
|
|||
|
||||
jest.mock("../../../../src/components/views/messages/UnknownBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => (<div data-testid="unknown-body" />),
|
||||
default: () => <div data-testid="unknown-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/voice-broadcast/components/VoiceBroadcastBody", () => ({
|
||||
VoiceBroadcastBody: () => (<div data-testid="voice-broadcast-body" />),
|
||||
VoiceBroadcastBody: () => <div data-testid="voice-broadcast-body" />,
|
||||
}));
|
||||
|
||||
describe("MessageEvent", () => {
|
||||
|
@ -39,11 +39,13 @@ describe("MessageEvent", () => {
|
|||
let event: MatrixEvent;
|
||||
|
||||
const renderMessageEvent = (): RenderResult => {
|
||||
return render(<MessageEvent
|
||||
mxEvent={event}
|
||||
onHeightChanged={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>);
|
||||
return render(
|
||||
<MessageEvent
|
||||
mxEvent={event}
|
||||
onHeightChanged={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -31,7 +31,7 @@ import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";
|
|||
|
||||
describe("<TextualBody />", () => {
|
||||
afterEach(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockRestore();
|
||||
});
|
||||
|
||||
const defaultRoom = mkStubRoom("room_id", "test room", undefined);
|
||||
|
@ -58,7 +58,7 @@ describe("<TextualBody />", () => {
|
|||
const defaultProps = {
|
||||
mxEvent: defaultEvent,
|
||||
highlights: [],
|
||||
highlightLink: '',
|
||||
highlightLink: "",
|
||||
onMessageAllowed: jest.fn(),
|
||||
onHeightChanged: jest.fn(),
|
||||
permalinkCreator: new RoomPermalinkCreator(defaultRoom),
|
||||
|
@ -107,7 +107,7 @@ describe("<TextualBody />", () => {
|
|||
const wrapper = getComponent({ mxEvent: ev });
|
||||
expect(wrapper.text()).toBe(ev.getContent().body);
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe(`<span class="mx_EventTile_body" dir="auto">${ ev.getContent().body }</span>`);
|
||||
expect(content.html()).toBe(`<span class="mx_EventTile_body" dir="auto">${ev.getContent().body}</span>`);
|
||||
});
|
||||
|
||||
describe("renders plain-text m.text correctly", () => {
|
||||
|
@ -130,7 +130,7 @@ describe("<TextualBody />", () => {
|
|||
const wrapper = getComponent({ mxEvent: ev });
|
||||
expect(wrapper.text()).toBe(ev.getContent().body);
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe(`<span class="mx_EventTile_body" dir="auto">${ ev.getContent().body }</span>`);
|
||||
expect(content.html()).toBe(`<span class="mx_EventTile_body" dir="auto">${ev.getContent().body}</span>`);
|
||||
});
|
||||
|
||||
// If pills were rendered within a Portal/same shadow DOM then it'd be easier to test
|
||||
|
@ -149,9 +149,11 @@ describe("<TextualBody />", () => {
|
|||
const wrapper = getComponent({ mxEvent: ev });
|
||||
expect(wrapper.text()).toBe(ev.getContent().body);
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe('<span class="mx_EventTile_body" dir="auto">' +
|
||||
'Visit <a href="https://matrix.org/" class="linkified" target="_blank" rel="noreferrer noopener">' +
|
||||
'https://matrix.org/</a></span>');
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body" dir="auto">' +
|
||||
'Visit <a href="https://matrix.org/" class="linkified" target="_blank" rel="noreferrer noopener">' +
|
||||
"https://matrix.org/</a></span>",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -188,8 +190,11 @@ describe("<TextualBody />", () => {
|
|||
const wrapper = getComponent({ mxEvent: ev }, matrixClient);
|
||||
expect(wrapper.text()).toBe("foo baz bar del u");
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe('<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
ev.getContent().formatted_body + '</span>');
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
ev.getContent().formatted_body +
|
||||
"</span>",
|
||||
);
|
||||
});
|
||||
|
||||
it("spoilers get injected properly into the DOM", () => {
|
||||
|
@ -201,7 +206,7 @@ describe("<TextualBody />", () => {
|
|||
body: "Hey [Spoiler for movie](mxc://someserver/somefile)",
|
||||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "Hey <span data-mx-spoiler=\"movie\">the movie was awesome</span>",
|
||||
formatted_body: 'Hey <span data-mx-spoiler="movie">the movie was awesome</span>',
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
|
@ -209,12 +214,14 @@ describe("<TextualBody />", () => {
|
|||
const wrapper = getComponent({ mxEvent: ev }, matrixClient);
|
||||
expect(wrapper.text()).toBe("Hey (movie) the movie was awesome");
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe('<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
'Hey <span>' +
|
||||
'<span class="mx_EventTile_spoiler">' +
|
||||
'<span class="mx_EventTile_spoiler_reason">(movie)</span> ' +
|
||||
'<span class="mx_EventTile_spoiler_content"><span>the movie was awesome</span></span>' +
|
||||
'</span></span></span>');
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
"Hey <span>" +
|
||||
'<span class="mx_EventTile_spoiler">' +
|
||||
'<span class="mx_EventTile_spoiler_reason">(movie)</span> ' +
|
||||
'<span class="mx_EventTile_spoiler_content"><span>the movie was awesome</span></span>' +
|
||||
"</span></span></span>",
|
||||
);
|
||||
});
|
||||
|
||||
it("linkification is not applied to code blocks", () => {
|
||||
|
@ -247,7 +254,7 @@ describe("<TextualBody />", () => {
|
|||
body: "Hey User",
|
||||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "Hey <a href=\"https://matrix.to/#/@user:server\">Member</a>",
|
||||
formatted_body: 'Hey <a href="https://matrix.to/#/@user:server">Member</a>',
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
|
@ -290,8 +297,8 @@ describe("<TextualBody />", () => {
|
|||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body:
|
||||
"An <a href=\"https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/" +
|
||||
"$16085560162aNpaH:example.com?via=example.com\">event link</a> with text",
|
||||
'An <a href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
||||
'$16085560162aNpaH:example.com?via=example.com">event link</a> with text',
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
|
@ -301,9 +308,9 @@ describe("<TextualBody />", () => {
|
|||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
'An <a href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
||||
'$16085560162aNpaH:example.com?via=example.com" ' +
|
||||
'rel="noreferrer noopener">event link</a> with text</span>',
|
||||
'An <a href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
||||
'$16085560162aNpaH:example.com?via=example.com" ' +
|
||||
'rel="noreferrer noopener">event link</a> with text</span>',
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -319,8 +326,8 @@ describe("<TextualBody />", () => {
|
|||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body:
|
||||
"A <a href=\"https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com" +
|
||||
"?via=example.com&via=bob.com\">room link</a> with vias",
|
||||
'A <a href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com' +
|
||||
'?via=example.com&via=bob.com">room link</a> with vias',
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
|
@ -330,17 +337,17 @@ describe("<TextualBody />", () => {
|
|||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||
'A <span><bdi><a class="mx_Pill mx_RoomPill" ' +
|
||||
'href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com' +
|
||||
'?via=example.com&via=bob.com"' +
|
||||
'><img class="mx_BaseAvatar mx_BaseAvatar_image" ' +
|
||||
'src="mxc://avatar.url/room.png" ' +
|
||||
'style="width: 16px; height: 16px;" alt="" data-testid="avatar-img" aria-hidden="true">' +
|
||||
'<span class="mx_Pill_linkText">room name</span></a></bdi></span> with vias</span>',
|
||||
'A <span><bdi><a class="mx_Pill mx_RoomPill" ' +
|
||||
'href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com' +
|
||||
'?via=example.com&via=bob.com"' +
|
||||
'><img class="mx_BaseAvatar mx_BaseAvatar_image" ' +
|
||||
'src="mxc://avatar.url/room.png" ' +
|
||||
'style="width: 16px; height: 16px;" alt="" data-testid="avatar-img" aria-hidden="true">' +
|
||||
'<span class="mx_Pill_linkText">room name</span></a></bdi></span> with vias</span>',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders formatted body without html corretly', () => {
|
||||
it("renders formatted body without html corretly", () => {
|
||||
const ev = mkEvent({
|
||||
type: "m.room.message",
|
||||
room: "room_id",
|
||||
|
@ -358,15 +365,13 @@ describe("<TextualBody />", () => {
|
|||
|
||||
const content = wrapper.find(".mx_EventTile_body");
|
||||
expect(content.html()).toBe(
|
||||
'<span class="mx_EventTile_body" dir="auto">' +
|
||||
'escaped *markdown*' +
|
||||
'</span>',
|
||||
'<span class="mx_EventTile_body" dir="auto">' + "escaped *markdown*" + "</span>",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("renders url previews correctly", () => {
|
||||
languageHandler.setMissingEntryGenerator(key => key.split('|', 2)[1]);
|
||||
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
|
||||
const matrixClient = getMockClientWithEventEmitter({
|
||||
getRoom: () => mkStubRoom("room_id", "room name", undefined),
|
||||
|
@ -408,21 +413,24 @@ describe("<TextualBody />", () => {
|
|||
},
|
||||
event: true,
|
||||
});
|
||||
jest.spyOn(ev, 'replacingEventDate').mockReturnValue(new Date(1993, 7, 3));
|
||||
jest.spyOn(ev, "replacingEventDate").mockReturnValue(new Date(1993, 7, 3));
|
||||
ev.makeReplaced(ev2);
|
||||
|
||||
wrapper.setProps({
|
||||
mxEvent: ev,
|
||||
replacingEventId: ev.getId(),
|
||||
}, () => {
|
||||
expect(wrapper.text()).toBe(ev2.getContent()["m.new_content"].body + "(edited)");
|
||||
wrapper.setProps(
|
||||
{
|
||||
mxEvent: ev,
|
||||
replacingEventId: ev.getId(),
|
||||
},
|
||||
() => {
|
||||
expect(wrapper.text()).toBe(ev2.getContent()["m.new_content"].body + "(edited)");
|
||||
|
||||
// XXX: this is to give TextualBody enough time for state to settle
|
||||
wrapper.setState({}, () => {
|
||||
widgets = wrapper.find("LinkPreviewGroup");
|
||||
// at this point we should have exactly two links (not the matrix.org one anymore)
|
||||
expect(widgets.at(0).prop("links")).toEqual(["https://vector.im/", "https://riot.im/"]);
|
||||
});
|
||||
});
|
||||
// XXX: this is to give TextualBody enough time for state to settle
|
||||
wrapper.setState({}, () => {
|
||||
widgets = wrapper.find("LinkPreviewGroup");
|
||||
// at this point we should have exactly two links (not the matrix.org one anymore)
|
||||
expect(widgets.at(0).prop("links")).toEqual(["https://vector.im/", "https://riot.im/"]);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,20 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
import MediaProcessingError from '../../../../../src/components/views/messages/shared/MediaProcessingError';
|
||||
import MediaProcessingError from "../../../../../src/components/views/messages/shared/MediaProcessingError";
|
||||
|
||||
describe('<MediaProcessingError />', () => {
|
||||
describe("<MediaProcessingError />", () => {
|
||||
const defaultProps = {
|
||||
className: 'test-classname',
|
||||
children: 'Something went wrong',
|
||||
className: "test-classname",
|
||||
children: "Something went wrong",
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
render(<MediaProcessingError {...defaultProps} {...props} />);
|
||||
const getComponent = (props = {}) => render(<MediaProcessingError {...defaultProps} {...props} />);
|
||||
|
||||
it('renders', () => {
|
||||
it("renders", () => {
|
||||
const { container } = getComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue