test functional EventUtils (#8386)

* extract poll event test util

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test isContentActionable

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test canEditContent

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test functional eventutils

Signed-off-by: Kerry Archibald <kerrya@element.io>

* copyrights

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry 2022-04-21 18:14:10 +02:00 committed by GitHub
parent c70816d763
commit 73e8387799
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 413 additions and 37 deletions

View File

@ -82,7 +82,7 @@ export function canEditContent(mxEvent: MatrixEvent): boolean {
M_POLL_START.matches(mxEvent.getType()) ||
(
(msgtype === MsgType.Text || msgtype === MsgType.Emote) &&
body &&
!!body &&
typeof body === 'string'
)
);

View File

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
import { POLL_ANSWER, M_TEXT, M_POLL_KIND_DISCLOSED, M_POLL_START } from "matrix-events-sdk";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { PollStartEventPreview } from "../../../../src/stores/room-list/previews/PollStartEventPreview";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import { makePollStartEvent } from "../../../test-utils";
jest.spyOn(MatrixClientPeg, 'get').mockReturnValue({
getUserId: () => "@me:example.com",
@ -26,47 +26,15 @@ jest.spyOn(MatrixClientPeg, 'get').mockReturnValue({
describe("PollStartEventPreview", () => {
it("shows the question for a poll I created", async () => {
const pollStartEvent = newPollStartEvent("My Question", "@me:example.com");
const pollStartEvent = makePollStartEvent("My Question", "@me:example.com");
const preview = new PollStartEventPreview();
expect(preview.getTextFor(pollStartEvent)).toBe("My Question");
});
it("shows the sender and question for a poll created by someone else", async () => {
const pollStartEvent = newPollStartEvent("Your Question", "@yo:example.com");
const pollStartEvent = makePollStartEvent("Your Question", "@yo:example.com");
const preview = new PollStartEventPreview();
expect(preview.getTextFor(pollStartEvent)).toBe("@yo:example.com: Your Question");
});
});
function newPollStartEvent(
question: string,
sender: string,
answers?: POLL_ANSWER[],
): MatrixEvent {
if (!answers) {
answers = [
{ "id": "socks", [M_TEXT.name]: "Socks" },
{ "id": "shoes", [M_TEXT.name]: "Shoes" },
];
}
return new MatrixEvent(
{
"event_id": "$mypoll",
"room_id": "#myroom:example.com",
"sender": sender,
"type": M_POLL_START.name,
"content": {
[M_POLL_START.name]: {
"question": {
[M_TEXT.name]: question,
},
"kind": M_POLL_KIND_DISCLOSED.name,
"answers": answers,
},
[M_TEXT.name]: `${question}: answers`,
},
},
);
}

View File

@ -2,6 +2,7 @@ export * from './beacon';
export * from './client';
export * from './location';
export * from './platform';
export * from './poll';
export * from './room';
export * from './test-utils';
export * from './video';

50
test/test-utils/poll.ts Normal file
View File

@ -0,0 +1,50 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { M_TEXT, M_POLL_START, POLL_ANSWER, M_POLL_KIND_DISCLOSED } from "matrix-events-sdk";
export const makePollStartEvent = (
question: string,
sender: string,
answers?: POLL_ANSWER[],
): MatrixEvent => {
if (!answers) {
answers = [
{ "id": "socks", [M_TEXT.name]: "Socks" },
{ "id": "shoes", [M_TEXT.name]: "Shoes" },
];
}
return new MatrixEvent(
{
"event_id": "$mypoll",
"room_id": "#myroom:example.com",
"sender": sender,
"type": M_POLL_START.name,
"content": {
[M_POLL_START.name]: {
"question": {
[M_TEXT.name]: question,
},
"kind": M_POLL_KIND_DISCLOSED.name,
"answers": answers,
},
[M_TEXT.name]: `${question}: answers`,
},
},
);
};

View File

@ -0,0 +1,357 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { M_LOCATION } from "matrix-js-sdk/src/@types/location";
import {
EventStatus,
EventType,
MatrixEvent,
MsgType,
RelationType,
} from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
import {
canCancel,
canEditContent,
canEditOwnEvent,
canForward,
isContentActionable,
isLocationEvent,
isVoiceMessage,
} from "../../src/utils/EventUtils";
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent } from "../test-utils";
describe('EventUtils', () => {
const userId = '@user:server';
const roomId = '!room:server';
const mockClient = getMockClientWithEventEmitter({
getUserId: jest.fn().mockReturnValue(userId),
});
beforeEach(() => {
mockClient.getUserId.mockClear().mockReturnValue(userId);
});
afterAll(() => {
jest.spyOn(MatrixClientPeg, 'get').mockRestore();
});
// setup events
const unsentEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
});
unsentEvent.status = EventStatus.ENCRYPTING;
const redactedEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
});
redactedEvent.makeRedacted(redactedEvent);
const stateEvent = makeBeaconInfoEvent(userId, roomId);
const roomMemberEvent = new MatrixEvent({
type: EventType.RoomMember,
sender: userId,
});
const stickerEvent = new MatrixEvent({
type: EventType.Sticker,
sender: userId,
});
const pollStartEvent = makePollStartEvent('What?', userId);
const notDecryptedEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: 'm.bad.encrypted',
},
});
const noMsgType = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: undefined,
},
});
const noContentBody = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Image,
},
});
const emptyContentBody = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Text,
body: '',
},
});
const objectContentBody = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.File,
body: {},
},
});
const niceTextMessage = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Text,
body: 'Hello',
},
});
const bobsTextMessage = new MatrixEvent({
type: EventType.RoomMessage,
sender: '@bob:server',
content: {
msgtype: MsgType.Text,
body: 'Hello from Bob',
},
});
describe('isContentActionable()', () => {
type TestCase = [string, MatrixEvent];
it.each<TestCase>([
['unsent event', unsentEvent],
['redacted event', redactedEvent],
['state event', stateEvent],
['undecrypted event', notDecryptedEvent],
['room member event', roomMemberEvent],
['event without msgtype', noMsgType],
['event without content body property', noContentBody],
])('returns false for %s', (_description, event) => {
expect(isContentActionable(event)).toBe(false);
});
it.each<TestCase>([
['sticker event', stickerEvent],
['poll start event', pollStartEvent],
['event with empty content body', emptyContentBody],
['event with a content body', niceTextMessage],
])('returns true for %s', (_description, event) => {
expect(isContentActionable(event)).toBe(true);
});
});
describe('editable content helpers', () => {
const replaceRelationEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Text,
body: 'Hello',
['m.relates_to']: {
rel_type: RelationType.Replace,
event_id: '1',
},
},
});
const referenceRelationEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Text,
body: 'Hello',
['m.relates_to']: {
rel_type: RelationType.Reference,
event_id: '1',
},
},
});
const emoteEvent = new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
msgtype: MsgType.Emote,
body: '🧪',
},
});
type TestCase = [string, MatrixEvent];
const uneditableCases: TestCase[] = [
['redacted event', redactedEvent],
['state event', stateEvent],
['event that is not room message', roomMemberEvent],
['event without msgtype', noMsgType],
['event without content body property', noContentBody],
['event with empty content body property', emptyContentBody],
['event with non-string body', objectContentBody],
['event not sent by current user', bobsTextMessage],
['event with a replace relation', replaceRelationEvent],
];
const editableCases: TestCase[] = [
['event with reference relation', referenceRelationEvent],
['emote event', emoteEvent],
['poll start event', pollStartEvent],
['event with a content body', niceTextMessage],
];
describe('canEditContent()', () => {
it.each<TestCase>(uneditableCases)('returns false for %s', (_description, event) => {
expect(canEditContent(event)).toBe(false);
});
it.each<TestCase>(editableCases)('returns true for %s', (_description, event) => {
expect(canEditContent(event)).toBe(true);
});
});
describe('canEditOwnContent()', () => {
it.each<TestCase>(uneditableCases)('returns false for %s', (_description, event) => {
expect(canEditOwnEvent(event)).toBe(false);
});
it.each<TestCase>(editableCases)('returns true for %s', (_description, event) => {
expect(canEditOwnEvent(event)).toBe(true);
});
});
});
describe('isVoiceMessage()', () => {
it('returns true for an event with msc2516.voice content', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
['org.matrix.msc2516.voice']: {},
},
});
expect(isVoiceMessage(event)).toBe(true);
});
it('returns true for an event with msc3245.voice content', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
['org.matrix.msc3245.voice']: {},
},
});
expect(isVoiceMessage(event)).toBe(true);
});
it('returns false for an event with voice content', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
body: 'hello',
},
});
expect(isVoiceMessage(event)).toBe(false);
});
});
describe('isLocationEvent()', () => {
it('returns true for an event with m.location stable type', () => {
const event = new MatrixEvent({
type: M_LOCATION.altName,
});
expect(isLocationEvent(event)).toBe(true);
});
it('returns true for an event with m.location unstable prefixed type', () => {
const event = new MatrixEvent({
type: M_LOCATION.name,
});
expect(isLocationEvent(event)).toBe(true);
});
it('returns true for a room message with stable m.location msgtype', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
msgtype: M_LOCATION.altName,
},
});
expect(isLocationEvent(event)).toBe(true);
});
it('returns true for a room message with unstable m.location msgtype', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
msgtype: M_LOCATION.name,
},
});
expect(isLocationEvent(event)).toBe(true);
});
it('returns false for a non location event', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
body: 'Hello',
},
});
expect(isLocationEvent(event)).toBe(false);
});
});
describe('canForward()', () => {
it('returns false for a location event', () => {
const event = new MatrixEvent({
type: M_LOCATION.name,
});
expect(canForward(event)).toBe(false);
});
it('returns false for a poll event', () => {
const event = makePollStartEvent('Who?', userId);
expect(canForward(event)).toBe(false);
});
it('returns true for a room message event', () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
body: 'Hello',
},
});
expect(canForward(event)).toBe(true);
});
});
describe('canCancel()', () => {
it.each([
[EventStatus.QUEUED],
[EventStatus.NOT_SENT],
[EventStatus.ENCRYPTING],
])('return true for status %s', (status) => {
expect(canCancel(status)).toBe(true);
});
it.each([
[EventStatus.SENDING],
[EventStatus.CANCELLED],
[EventStatus.SENT],
['invalid-status' as unknown as EventStatus],
])('return false for status %s', (status) => {
expect(canCancel(status)).toBe(false);
});
});
});