You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-04 11:51:45 +03:00 
			
		
		
		
	* Delabs threads * remove threads reload when labs is toggled * Fix ts strict * fix rebase mistake * remove .only * fix pr comments * re-introduce backwards compat * Fix export test * Fix SearchREsultTile test * strict ts
		
			
				
	
	
		
			343 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/*
 | 
						|
Copyright 2021 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 { renderToString } from "react-dom/server";
 | 
						|
import {
 | 
						|
    IContent,
 | 
						|
    MatrixClient,
 | 
						|
    MatrixEvent,
 | 
						|
    Room,
 | 
						|
    RoomMember,
 | 
						|
    RelationType,
 | 
						|
    EventType,
 | 
						|
} from "matrix-js-sdk/src/matrix";
 | 
						|
 | 
						|
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
 | 
						|
import { IExportOptions, ExportType, ExportFormat } from "../../src/utils/exportUtils/exportUtils";
 | 
						|
import PlainTextExporter from "../../src/utils/exportUtils/PlainTextExport";
 | 
						|
import HTMLExporter from "../../src/utils/exportUtils/HtmlExport";
 | 
						|
import * as TestUtilsMatrix from "../test-utils";
 | 
						|
import { stubClient } from "../test-utils";
 | 
						|
 | 
						|
let client: MatrixClient;
 | 
						|
 | 
						|
const MY_USER_ID = "@me:here";
 | 
						|
 | 
						|
function generateRoomId() {
 | 
						|
    return "!" + Math.random().toString().slice(2, 10) + ":domain";
 | 
						|
}
 | 
						|
 | 
						|
interface ITestContent extends IContent {
 | 
						|
    expectedText: string;
 | 
						|
}
 | 
						|
 | 
						|
describe("export", function () {
 | 
						|
    let mockExportOptions: IExportOptions;
 | 
						|
    let mockRoom: Room;
 | 
						|
    let ts0: number;
 | 
						|
    let events: MatrixEvent[];
 | 
						|
    beforeEach(() => {
 | 
						|
        stubClient();
 | 
						|
        client = MatrixClientPeg.get();
 | 
						|
        client.getUserId = () => {
 | 
						|
            return MY_USER_ID;
 | 
						|
        };
 | 
						|
 | 
						|
        mockExportOptions = {
 | 
						|
            numberOfMessages: 5,
 | 
						|
            maxSize: 100 * 1024 * 1024,
 | 
						|
            attachmentsIncluded: false,
 | 
						|
        };
 | 
						|
 | 
						|
        function createRoom() {
 | 
						|
            const room = new Room(generateRoomId(), null, client.getUserId());
 | 
						|
            return room;
 | 
						|
        }
 | 
						|
        mockRoom = createRoom();
 | 
						|
        ts0 = Date.now();
 | 
						|
        events = mkEvents();
 | 
						|
        jest.spyOn(client, "getRoom").mockReturnValue(mockRoom);
 | 
						|
    });
 | 
						|
 | 
						|
    function mkRedactedEvent(i = 0) {
 | 
						|
        return new MatrixEvent({
 | 
						|
            type: "m.room.message",
 | 
						|
            sender: MY_USER_ID,
 | 
						|
            content: {},
 | 
						|
            unsigned: {
 | 
						|
                age: 72,
 | 
						|
                transaction_id: "m1212121212.23",
 | 
						|
                redacted_because: {
 | 
						|
                    content: {},
 | 
						|
                    origin_server_ts: ts0 + i * 1000,
 | 
						|
                    redacts: "$9999999999999999999999999999999999999999998",
 | 
						|
                    sender: "@me:here",
 | 
						|
                    type: EventType.RoomRedaction,
 | 
						|
                    unsigned: {
 | 
						|
                        age: 94,
 | 
						|
                        transaction_id: "m1111111111.1",
 | 
						|
                    },
 | 
						|
                    event_id: "$9999999999999999999999999999999999999999998",
 | 
						|
                    room_id: mockRoom.roomId,
 | 
						|
                },
 | 
						|
            },
 | 
						|
            event_id: "$9999999999999999999999999999999999999999999",
 | 
						|
            room_id: mockRoom.roomId,
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    function mkFileEvent() {
 | 
						|
        return new MatrixEvent({
 | 
						|
            content: {
 | 
						|
                body: "index.html",
 | 
						|
                info: {
 | 
						|
                    mimetype: "text/html",
 | 
						|
                    size: 31613,
 | 
						|
                },
 | 
						|
                msgtype: "m.file",
 | 
						|
                url: "mxc://test.org",
 | 
						|
            },
 | 
						|
            origin_server_ts: 1628872988364,
 | 
						|
            sender: MY_USER_ID,
 | 
						|
            type: "m.room.message",
 | 
						|
            unsigned: {
 | 
						|
                age: 266,
 | 
						|
                transaction_id: "m99999999.2",
 | 
						|
            },
 | 
						|
            event_id: "$99999999999999999999",
 | 
						|
            room_id: mockRoom.roomId,
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    function mkImageEvent() {
 | 
						|
        return new MatrixEvent({
 | 
						|
            content: {
 | 
						|
                body: "image.png",
 | 
						|
                info: {
 | 
						|
                    mimetype: "image/png",
 | 
						|
                    size: 31613,
 | 
						|
                },
 | 
						|
                msgtype: "m.image",
 | 
						|
                url: "mxc://test.org",
 | 
						|
            },
 | 
						|
            origin_server_ts: 1628872988364,
 | 
						|
            sender: MY_USER_ID,
 | 
						|
            type: "m.room.message",
 | 
						|
            unsigned: {
 | 
						|
                age: 266,
 | 
						|
                transaction_id: "m99999999.2",
 | 
						|
            },
 | 
						|
            event_id: "$99999999999999999999",
 | 
						|
            room_id: mockRoom.roomId,
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    function mkEvents() {
 | 
						|
        const matrixEvents = [];
 | 
						|
        let i: number;
 | 
						|
        // plain text
 | 
						|
        for (i = 0; i < 10; i++) {
 | 
						|
            matrixEvents.push(
 | 
						|
                TestUtilsMatrix.mkMessage({
 | 
						|
                    event: true,
 | 
						|
                    room: "!room:id",
 | 
						|
                    user: "@user:id",
 | 
						|
                    ts: ts0 + i * 1000,
 | 
						|
                }),
 | 
						|
            );
 | 
						|
        }
 | 
						|
        // reply events
 | 
						|
        for (i = 0; i < 10; i++) {
 | 
						|
            const eventId = "$" + Math.random() + "-" + Math.random();
 | 
						|
            matrixEvents.push(
 | 
						|
                TestUtilsMatrix.mkEvent({
 | 
						|
                    content: {
 | 
						|
                        "body": "> <@me:here> Hi\n\nTest",
 | 
						|
                        "format": "org.matrix.custom.html",
 | 
						|
                        "m.relates_to": {
 | 
						|
                            "rel_type": RelationType.Reference,
 | 
						|
                            "event_id": eventId,
 | 
						|
                            "m.in_reply_to": {
 | 
						|
                                event_id: eventId,
 | 
						|
                            },
 | 
						|
                        },
 | 
						|
                        "msgtype": "m.text",
 | 
						|
                    },
 | 
						|
                    user: "@me:here",
 | 
						|
                    type: "m.room.message",
 | 
						|
                    room: mockRoom.roomId,
 | 
						|
                    event: true,
 | 
						|
                }),
 | 
						|
            );
 | 
						|
        }
 | 
						|
        // membership events
 | 
						|
        for (i = 0; i < 10; i++) {
 | 
						|
            matrixEvents.push(
 | 
						|
                TestUtilsMatrix.mkMembership({
 | 
						|
                    event: true,
 | 
						|
                    room: "!room:id",
 | 
						|
                    user: "@user:id",
 | 
						|
                    target: {
 | 
						|
                        userId: "@user:id",
 | 
						|
                        name: "Bob",
 | 
						|
                        getAvatarUrl: () => {
 | 
						|
                            return "avatar.jpeg";
 | 
						|
                        },
 | 
						|
                        getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
 | 
						|
                    } as unknown as RoomMember,
 | 
						|
                    ts: ts0 + i * 1000,
 | 
						|
                    mship: "join",
 | 
						|
                    prevMship: "join",
 | 
						|
                    name: "A user",
 | 
						|
                }),
 | 
						|
            );
 | 
						|
        }
 | 
						|
        // emote
 | 
						|
        matrixEvents.push(
 | 
						|
            TestUtilsMatrix.mkEvent({
 | 
						|
                content: {
 | 
						|
                    body: "waves",
 | 
						|
                    msgtype: "m.emote",
 | 
						|
                },
 | 
						|
                user: "@me:here",
 | 
						|
                type: "m.room.message",
 | 
						|
                room: mockRoom.roomId,
 | 
						|
                event: true,
 | 
						|
            }),
 | 
						|
        );
 | 
						|
        // redacted events
 | 
						|
        for (i = 0; i < 10; i++) {
 | 
						|
            matrixEvents.push(mkRedactedEvent(i));
 | 
						|
        }
 | 
						|
        return matrixEvents;
 | 
						|
    }
 | 
						|
 | 
						|
    it("checks if the export format is valid", function () {
 | 
						|
        function isValidFormat(format: string): boolean {
 | 
						|
            const options: string[] = Object.values(ExportFormat);
 | 
						|
            return options.includes(format);
 | 
						|
        }
 | 
						|
        expect(isValidFormat("Html")).toBeTruthy();
 | 
						|
        expect(isValidFormat("Json")).toBeTruthy();
 | 
						|
        expect(isValidFormat("PlainText")).toBeTruthy();
 | 
						|
        expect(isValidFormat("Pdf")).toBeFalsy();
 | 
						|
    });
 | 
						|
 | 
						|
    it("checks if the icons' html corresponds to export regex", function () {
 | 
						|
        const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null);
 | 
						|
        const fileRegex = /<span class="mx_MFileBody_info_icon">.*?<\/span>/;
 | 
						|
        expect(fileRegex.test(renderToString(exporter.getEventTile(mkFileEvent(), true)))).toBeTruthy();
 | 
						|
    });
 | 
						|
 | 
						|
    it("should export images if attachments are enabled", () => {
 | 
						|
        const exporter = new HTMLExporter(
 | 
						|
            mockRoom,
 | 
						|
            ExportType.Beginning,
 | 
						|
            {
 | 
						|
                numberOfMessages: 5,
 | 
						|
                maxSize: 100 * 1024 * 1024,
 | 
						|
                attachmentsIncluded: true,
 | 
						|
            },
 | 
						|
            null,
 | 
						|
        );
 | 
						|
        const imageRegex = /<img.+ src="mxc:\/\/test.org" alt="image.png"\/>/;
 | 
						|
        expect(imageRegex.test(renderToString(exporter.getEventTile(mkImageEvent(), true)))).toBeTruthy();
 | 
						|
    });
 | 
						|
 | 
						|
    const invalidExportOptions: [string, IExportOptions][] = [
 | 
						|
        [
 | 
						|
            "numberOfMessages exceeds max",
 | 
						|
            {
 | 
						|
                numberOfMessages: 10 ** 9,
 | 
						|
                maxSize: 1024 * 1024 * 1024,
 | 
						|
                attachmentsIncluded: false,
 | 
						|
            },
 | 
						|
        ],
 | 
						|
        [
 | 
						|
            "maxSize exceeds 8GB",
 | 
						|
            {
 | 
						|
                numberOfMessages: -1,
 | 
						|
                maxSize: 8001 * 1024 * 1024,
 | 
						|
                attachmentsIncluded: false,
 | 
						|
            },
 | 
						|
        ],
 | 
						|
        [
 | 
						|
            "maxSize is less than 1mb",
 | 
						|
            {
 | 
						|
                numberOfMessages: 0,
 | 
						|
                maxSize: 0,
 | 
						|
                attachmentsIncluded: false,
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    ];
 | 
						|
    it.each(invalidExportOptions)("%s", (_d, options) => {
 | 
						|
        expect(() => new PlainTextExporter(mockRoom, ExportType.Beginning, options, null)).toThrowError(
 | 
						|
            "Invalid export options",
 | 
						|
        );
 | 
						|
    });
 | 
						|
 | 
						|
    it("tests the file extension splitter", function () {
 | 
						|
        const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null);
 | 
						|
        const fileNameWithExtensions = {
 | 
						|
            "": ["", ""],
 | 
						|
            "name": ["name", ""],
 | 
						|
            "name.txt": ["name", ".txt"],
 | 
						|
            ".htpasswd": ["", ".htpasswd"],
 | 
						|
            "name.with.many.dots.myext": ["name.with.many.dots", ".myext"],
 | 
						|
        };
 | 
						|
        for (const fileName in fileNameWithExtensions) {
 | 
						|
            expect(exporter.splitFileName(fileName)).toStrictEqual(fileNameWithExtensions[fileName]);
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    it("checks if the reply regex executes correctly", function () {
 | 
						|
        const eventContents: ITestContent[] = [
 | 
						|
            {
 | 
						|
                msgtype: "m.text",
 | 
						|
                body: "> <@me:here> Source\n\nReply",
 | 
						|
                expectedText: '<@me:here "Source"> Reply',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                msgtype: "m.text",
 | 
						|
                // if the reply format is invalid, then return the body
 | 
						|
                body: "Invalid reply format",
 | 
						|
                expectedText: "Invalid reply format",
 | 
						|
            },
 | 
						|
            {
 | 
						|
                msgtype: "m.text",
 | 
						|
                body: "> <@me:here> The source is more than 32 characters\n\nReply",
 | 
						|
                expectedText: '<@me:here "The source is more than 32 chara..."> Reply',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                msgtype: "m.text",
 | 
						|
                body: "> <@me:here> This\nsource\nhas\nnew\nlines\n\nReply",
 | 
						|
                expectedText: '<@me:here "This"> Reply',
 | 
						|
            },
 | 
						|
        ];
 | 
						|
        const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null);
 | 
						|
        for (const content of eventContents) {
 | 
						|
            expect(exporter.textForReplyEvent(content)).toBe(content.expectedText);
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    it("checks if the render to string doesn't throw any error for different types of events", function () {
 | 
						|
        const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null);
 | 
						|
        for (const event of events) {
 | 
						|
            expect(renderToString(exporter.getEventTile(event, false))).toBeTruthy();
 | 
						|
        }
 | 
						|
    });
 | 
						|
});
 |