384 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			384 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
import './skinned-sdk';
 | 
						|
 | 
						|
import { EventType, MatrixEvent } from "matrix-js-sdk";
 | 
						|
import renderer from 'react-test-renderer';
 | 
						|
 | 
						|
import { getSenderName, textForEvent } from "../src/TextForEvent";
 | 
						|
import SettingsStore from "../src/settings/SettingsStore";
 | 
						|
import { createTestClient } from './test-utils';
 | 
						|
import { MatrixClientPeg } from '../src/MatrixClientPeg';
 | 
						|
import UserIdentifierCustomisations from '../src/customisations/UserIdentifier';
 | 
						|
 | 
						|
jest.mock("../src/settings/SettingsStore");
 | 
						|
jest.mock('../src/customisations/UserIdentifier', () => ({
 | 
						|
    getDisplayUserIdentifier: jest.fn().mockImplementation(userId => userId),
 | 
						|
}));
 | 
						|
 | 
						|
function mockPinnedEvent(
 | 
						|
    pinnedMessageIds?: string[],
 | 
						|
    prevPinnedMessageIds?: string[],
 | 
						|
): MatrixEvent {
 | 
						|
    return new MatrixEvent({
 | 
						|
        type: "m.room.pinned_events",
 | 
						|
        state_key: "",
 | 
						|
        sender: "@foo:example.com",
 | 
						|
        content: {
 | 
						|
            pinned: pinnedMessageIds,
 | 
						|
        },
 | 
						|
        prev_content: {
 | 
						|
            pinned: prevPinnedMessageIds,
 | 
						|
        },
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
// Helper function that renders a component to a plain text string.
 | 
						|
// Once snapshots are introduced in tests, this function will no longer be necessary,
 | 
						|
// and should be replaced with snapshots.
 | 
						|
function renderComponent(component): string {
 | 
						|
    const serializeObject = (object): string => {
 | 
						|
        if (typeof object === 'string') {
 | 
						|
            return object === ' ' ? '' : object;
 | 
						|
        }
 | 
						|
 | 
						|
        if (Array.isArray(object) && object.length === 1 && typeof object[0] === 'string') {
 | 
						|
            return object[0];
 | 
						|
        }
 | 
						|
 | 
						|
        if (object['type'] !== undefined && typeof object['children'] !== undefined) {
 | 
						|
            return serializeObject(object.children);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!Array.isArray(object)) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
 | 
						|
        return object.map(child => {
 | 
						|
            return serializeObject(child);
 | 
						|
        }).join('');
 | 
						|
    };
 | 
						|
 | 
						|
    return serializeObject(component.toJSON());
 | 
						|
}
 | 
						|
 | 
						|
describe('TextForEvent', () => {
 | 
						|
    describe("getSenderName()", () => {
 | 
						|
        it("Prefers sender.name", () => {
 | 
						|
            expect(getSenderName({ sender: { name: "Alice" } } as MatrixEvent)).toBe("Alice");
 | 
						|
        });
 | 
						|
        it("Handles missing sender", () => {
 | 
						|
            expect(getSenderName({ getSender: () => "Alice" } as MatrixEvent)).toBe("Alice");
 | 
						|
        });
 | 
						|
        it("Handles missing sender and get sender", () => {
 | 
						|
            expect(getSenderName({ getSender: () => undefined } as MatrixEvent)).toBe("Someone");
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe("TextForPinnedEvent", () => {
 | 
						|
        beforeAll(() => {
 | 
						|
            // enable feature_pinning setting
 | 
						|
            (SettingsStore.getValue as jest.Mock).mockImplementation(feature => feature === 'feature_pinning');
 | 
						|
        });
 | 
						|
 | 
						|
        it("mentions message when a single message was pinned, with no previously pinned messages", () => {
 | 
						|
            const event = mockPinnedEvent(['message-1']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("mentions message when a single message was pinned, with multiple previously pinned messages", () => {
 | 
						|
            const event = mockPinnedEvent(['message-1', 'message-2', 'message-3'], ['message-1', 'message-2']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("mentions message when a single message was unpinned, with a single message previously pinned", () => {
 | 
						|
            const event = mockPinnedEvent([], ['message-1']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("mentions message when a single message was unpinned, with multiple previously pinned messages", () => {
 | 
						|
            const event = mockPinnedEvent(['message-2'], ['message-1', 'message-2']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("shows generic text when multiple messages were pinned", () => {
 | 
						|
            const event = mockPinnedEvent(['message-1', 'message-2', 'message-3'], ['message-1']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com changed the pinned messages for the room.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("shows generic text when multiple messages were unpinned", () => {
 | 
						|
            const event = mockPinnedEvent(['message-3'], ['message-1', 'message-2', 'message-3']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com changed the pinned messages for the room.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("shows generic text when one message was pinned, and another unpinned", () => {
 | 
						|
            const event = mockPinnedEvent(['message-2'], ['message-1']);
 | 
						|
            const plainText = textForEvent(event);
 | 
						|
            const component = renderer.create(textForEvent(event, true));
 | 
						|
 | 
						|
            const expectedText = "@foo:example.com changed the pinned messages for the room.";
 | 
						|
            expect(plainText).toBe(expectedText);
 | 
						|
            expect(renderComponent(component)).toBe(expectedText);
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe("textForPowerEvent()", () => {
 | 
						|
        let mockClient;
 | 
						|
        const mockRoom = {
 | 
						|
            getMember: jest.fn(),
 | 
						|
        };
 | 
						|
 | 
						|
        const userA = {
 | 
						|
            id: '@a',
 | 
						|
            name: 'Alice',
 | 
						|
        };
 | 
						|
        const userB = {
 | 
						|
            id: '@b',
 | 
						|
            name: 'Bob',
 | 
						|
        };
 | 
						|
        const userC = {
 | 
						|
            id: '@c',
 | 
						|
            name: 'Carl',
 | 
						|
        };
 | 
						|
        interface PowerEventProps {
 | 
						|
            usersDefault?: number;
 | 
						|
            prevDefault?: number;
 | 
						|
            users: Record<string, number>;
 | 
						|
            prevUsers: Record<string, number>;
 | 
						|
        }
 | 
						|
        const mockPowerEvent = ({
 | 
						|
            usersDefault, prevDefault, users, prevUsers,
 | 
						|
        }: PowerEventProps): MatrixEvent => new MatrixEvent({
 | 
						|
            type: EventType.RoomPowerLevels,
 | 
						|
            sender: userA.id,
 | 
						|
            state_key: "",
 | 
						|
            content: {
 | 
						|
                users_default: usersDefault,
 | 
						|
                users,
 | 
						|
            },
 | 
						|
            prev_content: {
 | 
						|
                users: prevUsers,
 | 
						|
                users_default: prevDefault,
 | 
						|
            },
 | 
						|
        });
 | 
						|
 | 
						|
        beforeAll(() => {
 | 
						|
            mockClient = createTestClient();
 | 
						|
            MatrixClientPeg.get = () => mockClient;
 | 
						|
            mockClient.getRoom.mockClear().mockReturnValue(mockRoom);
 | 
						|
            mockRoom.getMember.mockClear().mockImplementation(
 | 
						|
                userId => [userA, userB, userC].find(u => u.id === userId),
 | 
						|
            );
 | 
						|
            (SettingsStore.getValue as jest.Mock).mockReturnValue(true);
 | 
						|
        });
 | 
						|
 | 
						|
        beforeEach(() => {
 | 
						|
            (UserIdentifierCustomisations.getDisplayUserIdentifier as jest.Mock)
 | 
						|
                .mockClear()
 | 
						|
                .mockImplementation(userId => userId);
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns falsy when no users have changed power level", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                users: {
 | 
						|
                    [userA.id]: 100,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userA.id]: 100,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            expect(textForEvent(event)).toBeFalsy();
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns false when users power levels have been changed by default settings", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                usersDefault: 100,
 | 
						|
                prevDefault: 50,
 | 
						|
                users: {
 | 
						|
                    [userA.id]: 100,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userA.id]: 50,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            expect(textForEvent(event)).toBeFalsy();
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns correct message for a single user with changed power level", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                users: {
 | 
						|
                    [userB.id]: 100,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userB.id]: 50,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            const expectedText = "@a changed the power level of @b from Moderator to Admin.";
 | 
						|
            expect(textForEvent(event)).toEqual(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns correct message for a single user with power level changed to the default", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                usersDefault: 20,
 | 
						|
                prevDefault: 101,
 | 
						|
                users: {
 | 
						|
                    [userB.id]: 20,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userB.id]: 50,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            const expectedText = "@a changed the power level of @b from Moderator to Default.";
 | 
						|
            expect(textForEvent(event)).toEqual(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns correct message for a single user with power level changed to a custom level", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                users: {
 | 
						|
                    [userB.id]: -1,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userB.id]: 50,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            const expectedText = "@a changed the power level of @b from Moderator to Custom (-1).";
 | 
						|
            expect(textForEvent(event)).toEqual(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("returns correct message for a multiple power level changes", () => {
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                users: {
 | 
						|
                    [userB.id]: 100,
 | 
						|
                    [userC.id]: 50,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userB.id]: 50,
 | 
						|
                    [userC.id]: 101,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            const expectedText =
 | 
						|
                "@a changed the power level of @b from Moderator to Admin, @c from Custom (101) to Moderator.";
 | 
						|
            expect(textForEvent(event)).toEqual(expectedText);
 | 
						|
        });
 | 
						|
 | 
						|
        it("uses userIdentifier customisation", () => {
 | 
						|
            (UserIdentifierCustomisations.getDisplayUserIdentifier as jest.Mock)
 | 
						|
                .mockImplementation(userId => 'customised ' + userId);
 | 
						|
            const event = mockPowerEvent({
 | 
						|
                users: {
 | 
						|
                    [userB.id]: 100,
 | 
						|
                },
 | 
						|
                prevUsers: {
 | 
						|
                    [userB.id]: 50,
 | 
						|
                },
 | 
						|
            });
 | 
						|
            // uses customised user id
 | 
						|
            const expectedText = "@a changed the power level of customised @b from Moderator to Admin.";
 | 
						|
            expect(textForEvent(event)).toEqual(expectedText);
 | 
						|
            expect(UserIdentifierCustomisations.getDisplayUserIdentifier)
 | 
						|
                .toHaveBeenCalledWith(userB.id, { roomId: event.getRoomId() });
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe("textForCanonicalAliasEvent()", () => {
 | 
						|
        const userA = {
 | 
						|
            id: '@a',
 | 
						|
            name: 'Alice',
 | 
						|
        };
 | 
						|
 | 
						|
        interface AliasEventProps {
 | 
						|
            alias?: string; prevAlias?: string; altAliases?: string[]; prevAltAliases?: string[];
 | 
						|
        }
 | 
						|
        const mockEvent = ({
 | 
						|
            alias, prevAlias, altAliases, prevAltAliases,
 | 
						|
        }: AliasEventProps): MatrixEvent => new MatrixEvent({
 | 
						|
            type: EventType.RoomCanonicalAlias,
 | 
						|
            sender: userA.id,
 | 
						|
            state_key: "",
 | 
						|
            content: {
 | 
						|
                alias, alt_aliases: altAliases,
 | 
						|
            },
 | 
						|
            prev_content: {
 | 
						|
                alias: prevAlias, alt_aliases: prevAltAliases,
 | 
						|
            },
 | 
						|
        });
 | 
						|
 | 
						|
        type TestCase = [string, AliasEventProps & { result: string }];
 | 
						|
        const testCases: TestCase[] = [
 | 
						|
            ["room alias didn't change", {
 | 
						|
                result: '@a changed the addresses for this room.',
 | 
						|
            }],
 | 
						|
            ["room alias changed", {
 | 
						|
                alias: 'banana',
 | 
						|
                prevAlias: 'apple',
 | 
						|
                result: '@a set the main address for this room to banana.',
 | 
						|
            }],
 | 
						|
            ["room alias was added", {
 | 
						|
                alias: 'banana',
 | 
						|
                result: '@a set the main address for this room to banana.',
 | 
						|
            }],
 | 
						|
            ["room alias was removed", {
 | 
						|
                prevAlias: 'apple',
 | 
						|
                result: '@a removed the main address for this room.',
 | 
						|
            }],
 | 
						|
            ["added an alt alias", {
 | 
						|
                altAliases: ['canteloupe'],
 | 
						|
                result: '@a added alternative address canteloupe for this room.',
 | 
						|
            }],
 | 
						|
            ["added multiple alt aliases", {
 | 
						|
                altAliases: ['canteloupe', 'date'],
 | 
						|
                result: '@a added the alternative addresses canteloupe, date for this room.',
 | 
						|
            }],
 | 
						|
            ["removed an alt alias", {
 | 
						|
                altAliases: ['canteloupe'],
 | 
						|
                prevAltAliases: ['canteloupe', 'date'],
 | 
						|
                result: '@a removed alternative address date for this room.',
 | 
						|
            }],
 | 
						|
            ["added and removed an alt aliases", {
 | 
						|
                altAliases: ['canteloupe', 'elderberry'],
 | 
						|
                prevAltAliases: ['canteloupe', 'date'],
 | 
						|
                result: '@a changed the alternative addresses for this room.',
 | 
						|
            }],
 | 
						|
            ["changed alias and added alt alias", {
 | 
						|
                alias: 'banana',
 | 
						|
                prevAlias: 'apple',
 | 
						|
                altAliases: ['canteloupe'],
 | 
						|
                result: '@a changed the main and alternative addresses for this room.',
 | 
						|
            }],
 | 
						|
        ];
 | 
						|
 | 
						|
        it.each(testCases)('returns correct message when %s', (_d, { result, ...eventProps }) => {
 | 
						|
            const event = mockEvent(eventProps);
 | 
						|
            expect(textForEvent(event)).toEqual(result);
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 |