diff --git a/src/components/structures/ViewSource.tsx b/src/components/structures/ViewSource.tsx index b87194f251c..245c1587c58 100644 --- a/src/components/structures/ViewSource.tsx +++ b/src/components/structures/ViewSource.tsx @@ -1,7 +1,6 @@ /* -Copyright 2015, 2016 OpenMarket Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2015, 2016, 2019, 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. @@ -77,9 +76,13 @@ export default class ViewSource extends React.Component { {_t("Decrypted event source")} - - {stringify(decryptedEventSource)} - + {decryptedEventSource ? ( + + {stringify(decryptedEventSource)} + + ) : ( +
{_t("Decrypted source unavailable")}
+ )}
diff --git a/src/components/views/elements/SyntaxHighlight.tsx b/src/components/views/elements/SyntaxHighlight.tsx index 886c68c9165..3e8828d94ef 100644 --- a/src/components/views/elements/SyntaxHighlight.tsx +++ b/src/components/views/elements/SyntaxHighlight.tsx @@ -1,5 +1,6 @@ /* Copyright 2017 Michael Telatynski <7t3chguy@gmail.com> +Copyright 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. @@ -25,7 +26,7 @@ interface IProps { export default class SyntaxHighlight extends React.PureComponent { public render(): JSX.Element { const { children: content, language } = this.props; - const highlighted = language ? hljs.highlight(language, content) : hljs.highlightAuto(content); + const highlighted = language ? hljs.highlight(content, { language }) : hljs.highlightAuto(content); return (
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 4da12e366bd..54272b871cc 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -3452,6 +3452,7 @@
     "User menu": "User menu",
     "Could not load user profile": "Could not load user profile",
     "Decrypted event source": "Decrypted event source",
+    "Decrypted source unavailable": "Decrypted source unavailable",
     "Original event source": "Original event source",
     "Event ID: %(eventId)s": "Event ID: %(eventId)s",
     "Thread root ID: %(threadRootId)s": "Thread root ID: %(threadRootId)s",
diff --git a/test/components/structures/ViewSource-test.tsx b/test/components/structures/ViewSource-test.tsx
new file mode 100644
index 00000000000..a4bc8b1eca4
--- /dev/null
+++ b/test/components/structures/ViewSource-test.tsx
@@ -0,0 +1,59 @@
+/*
+Copyright 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.
+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 { render } from "@testing-library/react";
+import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix";
+import React from "react";
+
+import ViewSource from "../../../src/components/structures/ViewSource";
+import { mkEvent, stubClient } from "../../test-utils/test-utils";
+
+describe("ThreadView", () => {
+    const ROOM_ID = "!roomId:example.org";
+    const SENDER = "@alice:example.org";
+
+    let messageEvent: MatrixEvent;
+
+    const redactionEvent = mkEvent({
+        user: SENDER,
+        event: true,
+        type: EventType.RoomRedaction,
+        content: {},
+    });
+
+    beforeEach(() => {
+        messageEvent = new MatrixEvent({
+            type: EventType.RoomMessageEncrypted,
+            room_id: ROOM_ID,
+            sender: SENDER,
+            content: {},
+            state_key: undefined,
+        });
+        messageEvent.makeRedacted(redactionEvent);
+    });
+
+    beforeEach(stubClient);
+
+    // See https://github.com/vector-im/element-web/issues/24165
+    it("doesn't error when viewing redacted encrypted messages", () => {
+        // Sanity checks
+        expect(messageEvent.isEncrypted()).toBeTruthy();
+        // @ts-ignore clearEvent is private, but it's being used directly 
+        expect(messageEvent.clearEvent).toBe(undefined);
+
+        expect(() => render( {}} />)).not.toThrow();
+    });
+});
diff --git a/test/components/views/elements/SyntaxHighlight-test.tsx b/test/components/views/elements/SyntaxHighlight-test.tsx
new file mode 100644
index 00000000000..bdd3e50cf02
--- /dev/null
+++ b/test/components/views/elements/SyntaxHighlight-test.tsx
@@ -0,0 +1,38 @@
+/* eslint @typescript-eslint/no-unused-vars: ["error", { "varsIgnorePattern": "^_" }] */
+/*
+Copyright 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.
+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 { render } from "@testing-library/react";
+import hljs, { type HighlightOptions } from "highlight.js";
+import React from "react";
+
+import SyntaxHighlight from "../../../../src/components/views/elements/SyntaxHighlight";
+
+describe("", () => {
+    it("renders", () => {
+        const { container } = render(console.log("Hello, World!"););
+        expect(container).toMatchSnapshot();
+    });
+
+    it.each(["json", "javascript", "css"])("uses the provided language", (lang) => {
+        const mock = jest.spyOn(hljs, "highlight");
+
+        render(// Hello, World);
+
+        const [_lang, opts] = mock.mock.lastCall!;
+        expect((opts as HighlightOptions)["language"]).toBe(lang);
+    });
+});
diff --git a/test/components/views/elements/__snapshots__/SyntaxHighlight-test.tsx.snap b/test/components/views/elements/__snapshots__/SyntaxHighlight-test.tsx.snap
new file mode 100644
index 00000000000..e7ad9c057b0
--- /dev/null
+++ b/test/components/views/elements/__snapshots__/SyntaxHighlight-test.tsx.snap
@@ -0,0 +1,30 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders 1`] = `
+
+
+    
+      
+        console
+      
+      .
+      
+        log
+      
+      (
+      
+        "Hello, World!"
+      
+      );
+    
+  
+
+`;