From 6a4b0adc329b5d075bce26bbcd81bdb924b0a00c Mon Sep 17 00:00:00 2001 From: Clark Fischer Date: Mon, 23 Jan 2023 15:55:00 -0800 Subject: [PATCH] Fix stringified promises in HTML exports Fixes https://github.com/vector-im/element-web/issues/24272 HtmlExporter was coercing Promises into strings. This removes the unecessary async labels on the offending methods. Signed-off-by: Clark Fischer --- src/utils/exportUtils/HtmlExport.tsx | 6 +- src/utils/exportUtils/exportCSS.ts | 4 +- test/test-utils/test-utils.ts | 4 + test/utils/exportUtils/HTMLExport-test.ts | 62 ++++++++++--- .../__snapshots__/HTMLExport-test.ts.snap | 86 +++++++++++++++++++ 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index 407d340c224f..81448e3173f7 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -238,7 +238,7 @@ export default class HTMLExporter extends Exporter { } } - protected async getDateSeparator(event: MatrixEvent): Promise { + protected getDateSeparator(event: MatrixEvent): string { const ts = event.getTs(); const dateSeparator = (
  • @@ -248,7 +248,7 @@ export default class HTMLExporter extends Exporter { return renderToStaticMarkup(dateSeparator); } - protected async needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent | null): Promise { + protected needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent | null): boolean { if (!prevEvent) return true; return wantsDateSeparator(prevEvent.getDate() || undefined, event.getDate() || undefined); } @@ -400,7 +400,7 @@ export default class HTMLExporter extends Exporter { if (this.cancelled) return this.cleanUp(); if (!haveRendererForEvent(event, false)) continue; - content += (await this.needsDateSeparator(event, prevEvent)) ? this.getDateSeparator(event) : ""; + content += this.needsDateSeparator(event, prevEvent) ? this.getDateSeparator(event) : ""; const shouldBeJoined = !this.needsDateSeparator(event, prevEvent) && shouldFormContinuation(prevEvent, event, false, this.threadsEnabled); diff --git a/src/utils/exportUtils/exportCSS.ts b/src/utils/exportUtils/exportCSS.ts index f92e339b023f..2a6a098a14e4 100644 --- a/src/utils/exportUtils/exportCSS.ts +++ b/src/utils/exportUtils/exportCSS.ts @@ -58,8 +58,8 @@ const getExportCSS = async (usedClasses: Set): Promise => { // If the light theme isn't loaded we will have to fetch & parse it manually if (!stylesheets.some(isLightTheme)) { - const href = document.querySelector('link[rel="stylesheet"][href$="theme-light.css"]').href; - stylesheets.push(await getRulesFromCssFile(href)); + const href = document.querySelector('link[rel="stylesheet"][href$="theme-light.css"]')?.href; + if (href) stylesheets.push(await getRulesFromCssFile(href)); } let css = ""; diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 25a581ba6f77..9089fad731bb 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -207,6 +207,10 @@ export function createTestClient(): MatrixClient { setPassword: jest.fn().mockRejectedValue({}), groupCallEventHandler: { groupCalls: new Map() }, redactEvent: jest.fn(), + + createMessagesRequest: jest.fn().mockResolvedValue({ + chunk: [], + }), } as unknown as MatrixClient; client.reEmitter = new ReEmitter(client); diff --git a/test/utils/exportUtils/HTMLExport-test.ts b/test/utils/exportUtils/HTMLExport-test.ts index 062988f7f28b..89a0ddedf505 100644 --- a/test/utils/exportUtils/HTMLExport-test.ts +++ b/test/utils/exportUtils/HTMLExport-test.ts @@ -1,5 +1,5 @@ /* -Copyright 2022 The Matrix.org Foundation C.I.C. +Copyright 2022 - 2023 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. @@ -14,26 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { EventType, IRoomEvent, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; -import { createTestClient, mkStubRoom, REPEATABLE_DATE } from "../../test-utils"; +import { mkStubRoom, REPEATABLE_DATE, stubClient } from "../../test-utils"; import { ExportType, IExportOptions } from "../../../src/utils/exportUtils/exportUtils"; import SdkConfig from "../../../src/SdkConfig"; import HTMLExporter from "../../../src/utils/exportUtils/HtmlExport"; +import DMRoomMap from "../../../src/utils/DMRoomMap"; + +jest.mock("jszip"); describe("HTMLExport", () => { + let client: jest.Mocked; + beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(REPEATABLE_DATE); - }); - afterEach(() => { - mocked(SdkConfig.get).mockRestore(); + client = stubClient() as jest.Mocked; + DMRoomMap.makeShared(); }); + function getMessageFile(exporter: HTMLExporter): Blob { + //@ts-ignore private access + const files = exporter.files; + const file = files.find((f) => f.name == "messages.html")!; + return file.blob; + } + it("should have an SDK-branded destination file name", () => { const roomName = "My / Test / Room: Welcome"; - const client = createTestClient(); const stubOptions: IExportOptions = { attachmentsIncluded: false, maxSize: 50000000, @@ -43,10 +53,42 @@ describe("HTMLExport", () => { expect(exporter.destinationFileName).toMatchSnapshot(); - jest.spyOn(SdkConfig, "get").mockImplementation(() => { - return { brand: "BrandedChat/WithSlashes/ForFun" }; - }); + SdkConfig.put({ brand: "BrandedChat/WithSlashes/ForFun" }); expect(exporter.destinationFileName).toMatchSnapshot(); }); + + it("should export", async () => { + const room = new Room("!myroom:example.org", client, "@me:example.org"); + + const events = [...Array(50)].map((_, i) => ({ + event_id: "$1", + type: EventType.RoomMessage, + sender: `@user${i}:example.com`, + origin_server_ts: 5_000 + i * 1000, + content: { + msgtype: "m.text", + body: `Message #${i}`, + }, + })); + + client.getRoom.mockReturnValue(room); + client.createMessagesRequest.mockResolvedValue({ chunk: events }); + + const exporter = new HTMLExporter( + room, + ExportType.LastNMessages, + { + attachmentsIncluded: false, + maxSize: 1_024 * 1_024, + numberOfMessages: events.length, + }, + () => {}, + ); + + await exporter.export(); + + const file = getMessageFile(exporter); + expect(await file.text()).toMatchSnapshot(); + }); }); diff --git a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap index 5cfed9ef9023..af50c8d604d4 100644 --- a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap +++ b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap @@ -1,5 +1,91 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`HTMLExport should export 1`] = ` +" + + + + + + + + + Exported Data + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + !myroom:example.org +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
      +
      + +

      !myroom:example.org

      +

      created this room.

      This is the start of export of !myroom:example.org. Exported by @userId:matrix.org at 2022/11/17.

      +
      +

      +
      +
    1. @user49:example.com
      Message #49
    2. @user48:example.com
      Message #48
    3. @user47:example.com
      Message #47
    4. @user46:example.com
      Message #46
    5. @user45:example.com
      Message #45
    6. @user44:example.com
      Message #44
    7. @user43:example.com
      Message #43
    8. @user42:example.com
      Message #42
    9. @user41:example.com
      Message #41
    10. @user40:example.com
      Message #40
    11. @user39:example.com
      Message #39
    12. @user38:example.com
      Message #38
    13. @user37:example.com
      Message #37
    14. @user36:example.com
      Message #36
    15. @user35:example.com
      Message #35
    16. @user34:example.com
      Message #34
    17. @user33:example.com
      Message #33
    18. @user32:example.com
      Message #32
    19. @user31:example.com
      Message #31
    20. @user30:example.com
      Message #30
    21. @user29:example.com
      Message #29
    22. @user28:example.com
      Message #28
    23. @user27:example.com
      Message #27
    24. @user26:example.com
      Message #26
    25. @user25:example.com
      Message #25
    26. @user24:example.com
      Message #24
    27. @user23:example.com
      Message #23
    28. @user22:example.com
      Message #22
    29. @user21:example.com
      Message #21
    30. @user20:example.com
      Message #20
    31. @user19:example.com
      Message #19
    32. @user18:example.com
      Message #18
    33. @user17:example.com
      Message #17
    34. @user16:example.com
      Message #16
    35. @user15:example.com
      Message #15
    36. @user14:example.com
      Message #14
    37. @user13:example.com
      Message #13
    38. @user12:example.com
      Message #12
    39. @user11:example.com
      Message #11
    40. @user10:example.com
      Message #10
    41. @user9:example.com
      Message #9
    42. @user8:example.com
      Message #8
    43. @user7:example.com
      Message #7
    44. @user6:example.com
      Message #6
    45. @user5:example.com
      Message #5
    46. @user4:example.com
      Message #4
    47. @user3:example.com
      Message #3
    48. @user2:example.com
      Message #2
    49. @user1:example.com
      Message #1
    50. @user0:example.com
      Message #0
    51. +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + " +`; + exports[`HTMLExport should have an SDK-branded destination file name 1`] = `"Element - My Test Room Welcome - Chat Export - 2022-11-17T16-58-32.517Z.zip"`; exports[`HTMLExport should have an SDK-branded destination file name 2`] = `"BrandedChatWithSlashesForFun - My Test Room Welcome - Chat Export - 2022-11-17T16-58-32.517Z.zip"`;