diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 603b95f7f7..641a4a0a41 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -92,6 +92,12 @@ jobs: # # Run 4 instances in Parallel # runner: [1, 2, 3, 4] steps: + - uses: tecolicom/actions-use-apt-tools@v1 + with: + # Our test suite includes some screenshot tests with unusual diacritics, which are + # supposed to be covered by STIXGeneral. + tools: fonts-stix + - uses: actions/checkout@v3 with: # XXX: We're checking out untrusted code in a secure context diff --git a/cypress/e2e/polls/polls.spec.ts b/cypress/e2e/polls/polls.spec.ts index ce0962410c..c092d4f647 100644 --- a/cypress/e2e/polls/polls.spec.ts +++ b/cypress/e2e/polls/polls.spec.ts @@ -115,7 +115,10 @@ describe("Polls", () => { const pollParams = { title: "Does the polls feature work?", - options: ["Yes", "No", "Maybe"], + // Since we're going to take a screenshot anyways, we include some + // non-ASCII characters here to stress test the app's font config + // while we're at it. + options: ["Yes", "Noo⃐o⃑o⃩o⃪o⃫o⃬o⃭o⃮o⃯", "のらねこ Maybe?"], }; createPoll(pollParams); diff --git a/res/themes/legacy-light/css/_legacy-light.pcss b/res/themes/legacy-light/css/_legacy-light.pcss index 41a4118576..ae52b078a7 100644 --- a/res/themes/legacy-light/css/_legacy-light.pcss +++ b/res/themes/legacy-light/css/_legacy-light.pcss @@ -4,17 +4,20 @@ Arial empirically gets it right, hence prioritising Arial here. We also include STIXGeneral explicitly to support a wider range of combining diacritics (Chrome fails without it, as per - https://bugs.chromium.org/p/chromium/issues/detail?id=1328898) */ + https://bugs.chromium.org/p/chromium/issues/detail?id=1328898). + We should never actively *prefer* STIXGeneral over the default font though, + since it looks pretty rough and implements some non-LGC scripts only + partially, making, for example, Japanese text look patchy and sad. */ /* We fall through to Twemoji for emoji rather than falling through to native Emoji fonts (if any) to ensure cross-browser consistency */ /* Noto Color Emoji contains digits, in fixed-width, therefore causing digits in flowed text to stand out. TODO: Consider putting all emoji fonts to the end rather than the front. */ -$font-family: "Nunito", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Arial", "Helvetica", - sans-serif, "Noto Color Emoji"; +$font-family: "Nunito", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Arial", "Helvetica", sans-serif, + "STIXGeneral", "Noto Color Emoji"; -$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Courier", - monospace, "Noto Color Emoji"; +$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Courier", monospace, + "STIXGeneral", "Noto Color Emoji"; /* unified palette */ /* try to use these colors when possible */ diff --git a/res/themes/light/css/_light.pcss b/res/themes/light/css/_light.pcss index 0b016e9c8d..4a1ae7e53a 100644 --- a/res/themes/light/css/_light.pcss +++ b/res/themes/light/css/_light.pcss @@ -4,17 +4,20 @@ Arial empirically gets it right, hence prioritising Arial here. We also include STIXGeneral explicitly to support a wider range of combining diacritics (Chrome fails without it, as per - https://bugs.chromium.org/p/chromium/issues/detail?id=1328898) */ + https://bugs.chromium.org/p/chromium/issues/detail?id=1328898). + We should never actively *prefer* STIXGeneral over the default font though, + since it looks pretty rough and implements some non-LGC scripts only + partially, making, for example, Japanese text look patchy and sad. */ /* We fall through to Twemoji for emoji rather than falling through to native Emoji fonts (if any) to ensure cross-browser consistency */ /* Noto Color Emoji contains digits, in fixed-width, therefore causing digits in flowed text to stand out. TODO: Consider putting all emoji fonts to the end rather than the front. */ -$font-family: "Inter", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Arial", "Helvetica", sans-serif, +$font-family: "Inter", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Arial", "Helvetica", sans-serif, "STIXGeneral", "Noto Color Emoji"; -$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "STIXGeneral", "Courier", - monospace, "Noto Color Emoji"; +$monospace-font-family: "Inconsolata", "Twemoji", "Apple Color Emoji", "Segoe UI Emoji", "Courier", monospace, + "STIXGeneral", "Noto Color Emoji"; /* Colors from Figma Compound https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=559%3A120 */ /* ******************** */ diff --git a/src/Notifier.ts b/src/Notifier.ts index dd624e2dc7..7e1f9eb0a4 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -50,6 +50,7 @@ import { localNotificationsAreSilenced, createLocalNotificationSettingsIfNeeded import { getIncomingCallToastKey, IncomingCallToast } from "./toasts/IncomingCallToast"; import ToastStore from "./stores/ToastStore"; import { ElementCall } from "./models/Call"; +import { VoiceBroadcastChunkEventType } from "./voice-broadcast"; /* * Dispatches: @@ -77,6 +78,13 @@ const msgTypeHandlers = { [M_LOCATION.altName]: (event: MatrixEvent) => { return TextForEvent.textForLocationEvent(event)(); }, + [MsgType.Audio]: (event: MatrixEvent): string | null => { + if (event.getContent()?.[VoiceBroadcastChunkEventType]) { + // mute broadcast chunks + return null; + } + return TextForEvent.textForEvent(event); + }, }; export const Notifier = { diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx index 383edd0792..770ea01fce 100644 --- a/src/components/views/avatars/RoomAvatar.tsx +++ b/src/components/views/avatars/RoomAvatar.tsx @@ -29,6 +29,7 @@ import * as Avatar from "../../../Avatar"; import DMRoomMap from "../../../utils/DMRoomMap"; import { mediaFromMxc } from "../../../customisations/Media"; import { IOOBData } from "../../../stores/ThreepidInviteStore"; +import { LocalRoom } from "../../../models/LocalRoom"; interface IProps extends Omit, "name" | "idName" | "url" | "onClick"> { // Room may be left unset here, but if it is, @@ -117,13 +118,26 @@ export default class RoomAvatar extends React.Component { Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); }; + private get roomIdName(): string | undefined { + const room = this.props.room; + + if (room) { + const dmMapUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + // If the room is a DM, we use the other user's ID for the color hash + // in order to match the room avatar with their avatar + if (dmMapUserId) return dmMapUserId; + + if (room instanceof LocalRoom && room.targets.length === 1) { + return room.targets[0].userId; + } + } + + return this.props.room?.roomId || this.props.oobData?.roomId; + } + public render() { const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props; - const roomName = room?.name ?? oobData.name; - // If the room is a DM, we use the other user's ID for the color hash - // in order to match the room avatar with their avatar - const idName = room ? DMRoomMap.shared().getUserIdForRoomId(room.roomId) ?? room.roomId : oobData.roomId; return ( { mx_RoomAvatar_isSpaceRoom: (room?.getType() ?? this.props.oobData?.roomType) === RoomType.Space, })} name={roomName} - idName={idName} + idName={this.roomIdName} urls={this.state.urls} onClick={viewAvatarOnClick && this.state.urls[0] ? this.onRoomAvatarClick : onClick} /> diff --git a/src/components/views/settings/Notifications.tsx b/src/components/views/settings/Notifications.tsx index 96269ae179..ca4a9480c0 100644 --- a/src/components/views/settings/Notifications.tsx +++ b/src/components/views/settings/Notifications.tsx @@ -642,7 +642,7 @@ export default class Notifications extends React.PureComponent { className="mx_UserNotifSettings_clearNotifsButton" data-testid="clear-notifications" > - {_t("Clear notifications")} + {_t("Mark all as read")} ); } diff --git a/src/events/getReferenceRelationsForEvent.ts b/src/events/getReferenceRelationsForEvent.ts deleted file mode 100644 index 3c258f8fe1..0000000000 --- a/src/events/getReferenceRelationsForEvent.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* -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 { EventType, MatrixClient, MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix"; -import { Relations } from "matrix-js-sdk/src/models/relations"; - -import { VoiceBroadcastInfoEventType } from "../voice-broadcast"; - -export const getReferenceRelationsForEvent = ( - event: MatrixEvent, - messageType: EventType | typeof VoiceBroadcastInfoEventType, - client: MatrixClient, -): Relations | undefined => { - const room = client.getRoom(event.getRoomId()); - return room - ?.getUnfilteredTimelineSet() - ?.relations?.getChildEventsForEvent(event.getId(), RelationType.Reference, messageType); -}; diff --git a/src/events/index.ts b/src/events/index.ts index 16349b0512..5b5be07ca8 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -16,4 +16,3 @@ limitations under the License. export { getForwardableEvent } from "./forward/getForwardableEvent"; export { getShareableLocationEvent } from "./location/getShareableLocationEvent"; -export * from "./getReferenceRelationsForEvent"; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e481dfca6f..952dba4590 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1419,7 +1419,7 @@ "Enable desktop notifications for this session": "Enable desktop notifications for this session", "Show message in desktop notification": "Show message in desktop notification", "Enable audible notifications for this session": "Enable audible notifications for this session", - "Clear notifications": "Clear notifications", + "Mark all as read": "Mark all as read", "Keyword": "Keyword", "New keyword": "New keyword", "On": "On", @@ -2126,7 +2126,6 @@ "%(count)s reply|one": "%(count)s reply", "Open thread": "Open thread", "Jump to first unread message.": "Jump to first unread message.", - "Mark all as read": "Mark all as read", "Unable to access your microphone": "Unable to access your microphone", "We were unable to access your microphone. Please check your browser settings and try again.": "We were unable to access your microphone. Please check your browser settings and try again.", "No microphone found": "No microphone found", diff --git a/test/Notifier-test.ts b/test/Notifier-test.ts index bfa1e1d1f3..a3b1f9b2db 100644 --- a/test/Notifier-test.ts +++ b/test/Notifier-test.ts @@ -20,6 +20,7 @@ import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { SyncState } from "matrix-js-sdk/src/sync"; import { waitFor } from "@testing-library/react"; +import { EventType, MsgType } from "matrix-js-sdk/src/matrix"; import BasePlatform from "../src/BasePlatform"; import { ElementCall } from "../src/models/Call"; @@ -39,6 +40,7 @@ import { mkThread } from "./test-utils/threads"; import dis from "../src/dispatcher/dispatcher"; import { ThreadPayload } from "../src/dispatcher/payloads/ThreadPayload"; import { Action } from "../src/dispatcher/actions"; +import { VoiceBroadcastChunkEventType } from "../src/voice-broadcast"; jest.mock("../src/utils/notifications", () => ({ // @ts-ignore @@ -73,6 +75,22 @@ describe("Notifier", () => { }); }; + const mkAudioEvent = (broadcastChunk = false): MatrixEvent => { + const chunkContent = broadcastChunk ? { [VoiceBroadcastChunkEventType]: {} } : {}; + + return mkEvent({ + event: true, + type: EventType.RoomMessage, + user: "@user:example.com", + room: "!room:example.com", + content: { + ...chunkContent, + msgtype: MsgType.Audio, + body: "test audio message", + }, + }); + }; + beforeEach(() => { accountDataStore = {}; mockClient = getMockClientWithEventEmitter({ @@ -94,11 +112,11 @@ describe("Notifier", () => { }); mockClient.pushRules = { - global: undefined, + global: {}, }; - accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId); + accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId!); - testRoom = new Room(roomId, mockClient, mockClient.getUserId()); + testRoom = new Room(roomId, mockClient, mockClient.getSafeUserId()); MockPlatform = mockPlatformPeg({ supportsNotifications: jest.fn().mockReturnValue(true), @@ -109,8 +127,10 @@ describe("Notifier", () => { Notifier.isBodyEnabled = jest.fn().mockReturnValue(true); - mockClient.getRoom.mockImplementation((id) => { - return id === roomId ? testRoom : new Room(id, mockClient, mockClient.getUserId()); + mockClient.getRoom.mockImplementation((id: string | undefined): Room | null => { + if (id === roomId) return testRoom; + if (id) return new Room(id, mockClient, mockClient.getSafeUserId()); + return null; }); }); @@ -256,6 +276,24 @@ describe("Notifier", () => { Notifier._displayPopupNotification(testEvent, testRoom); expect(MockPlatform.displayNotification).toHaveBeenCalledTimes(count); }); + + it("should display a notification for a voice message", () => { + const audioEvent = mkAudioEvent(); + Notifier._displayPopupNotification(audioEvent, testRoom); + expect(MockPlatform.displayNotification).toHaveBeenCalledWith( + "@user:example.com (!room1:server)", + "@user:example.com: test audio message", + "data:image/png;base64,00", + testRoom, + audioEvent, + ); + }); + + it("should not display a notification for a broadcast chunk", () => { + const audioEvent = mkAudioEvent(true); + Notifier._displayPopupNotification(audioEvent, testRoom); + expect(MockPlatform.displayNotification).not.toHaveBeenCalled(); + }); }); describe("getSoundForRoom", () => { @@ -448,7 +486,7 @@ describe("Notifier", () => { dis.dispatch({ action: Action.ViewThread, - thread_id: rootEvent.getId(), + thread_id: rootEvent.getId()!, }); await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId())); @@ -456,6 +494,11 @@ describe("Notifier", () => { Notifier._evaluateEvent(events[1]); expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1); }); + + it("should show a pop-up for an audio message", () => { + Notifier._evaluateEvent(mkAudioEvent()); + expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1); + }); }); describe("setPromptHidden", () => { diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx index 2c348f7efd..57eccec014 100644 --- a/test/components/structures/auth/ForgotPassword-test.tsx +++ b/test/components/structures/auth/ForgotPassword-test.tsx @@ -40,7 +40,6 @@ describe("", () => { let onComplete: () => void; let onLoginClick: () => void; let renderResult: RenderResult; - let restoreConsole: () => void; const typeIntoField = async (label: string, value: string): Promise => { await act(async () => { @@ -63,14 +62,14 @@ describe("", () => { }); }; - beforeEach(() => { - restoreConsole = filterConsole( - // not implemented by js-dom https://github.com/jsdom/jsdom/issues/1937 - "Not implemented: HTMLFormElement.prototype.requestSubmit", - // not of interested for this test - "Starting load of AsyncWrapper for modal", - ); + filterConsole( + // not implemented by js-dom https://github.com/jsdom/jsdom/issues/1937 + "Not implemented: HTMLFormElement.prototype.requestSubmit", + // not of interested for this test + "Starting load of AsyncWrapper for modal", + ); + beforeEach(() => { client = stubClient(); mocked(createClient).mockReturnValue(client); @@ -87,7 +86,6 @@ describe("", () => { afterEach(() => { // clean up modals Modal.closeCurrentModal("force"); - restoreConsole?.(); }); beforeAll(() => { diff --git a/test/components/views/avatars/RoomAvatar-test.tsx b/test/components/views/avatars/RoomAvatar-test.tsx new file mode 100644 index 0000000000..e23cd96f02 --- /dev/null +++ b/test/components/views/avatars/RoomAvatar-test.tsx @@ -0,0 +1,78 @@ +/* +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 React from "react"; +import { render } from "@testing-library/react"; +import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import { mocked } from "jest-mock"; + +import RoomAvatar from "../../../../src/components/views/avatars/RoomAvatar"; +import { filterConsole, stubClient } from "../../../test-utils"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; +import { LocalRoom } from "../../../../src/models/LocalRoom"; +import * as AvatarModule from "../../../../src/Avatar"; +import { DirectoryMember } from "../../../../src/utils/direct-messages"; + +describe("RoomAvatar", () => { + let client: MatrixClient; + + filterConsole( + // unrelated for this test + "Room !room:example.com does not have an m.room.create event", + ); + + beforeAll(() => { + client = stubClient(); + const dmRoomMap = new DMRoomMap(client); + jest.spyOn(dmRoomMap, "getUserIdForRoomId"); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); + jest.spyOn(AvatarModule, "defaultAvatarUrlForString"); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + afterEach(() => { + mocked(DMRoomMap.shared().getUserIdForRoomId).mockReset(); + mocked(AvatarModule.defaultAvatarUrlForString).mockClear(); + }); + + it("should render as expected for a Room", () => { + const room = new Room("!room:example.com", client, client.getSafeUserId()); + room.name = "test room"; + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(room.roomId); + }); + + it("should render as expected for a DM room", () => { + const userId = "@dm_user@example.com"; + const room = new Room("!room:example.com", client, client.getSafeUserId()); + room.name = "DM room"; + mocked(DMRoomMap.shared().getUserIdForRoomId).mockReturnValue(userId); + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(userId); + }); + + it("should render as expected for a LocalRoom", () => { + const userId = "@local_room_user@example.com"; + const localRoom = new LocalRoom("!room:example.com", client, client.getSafeUserId()); + localRoom.name = "local test room"; + localRoom.targets.push(new DirectoryMember({ user_id: userId })); + expect(render().container).toMatchSnapshot(); + expect(AvatarModule.defaultAvatarUrlForString).toHaveBeenCalledWith(userId); + }); +}); diff --git a/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap new file mode 100644 index 0000000000..6bffa157b6 --- /dev/null +++ b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RoomAvatar should render as expected for a DM room 1`] = ` +
+ + + + +
+`; + +exports[`RoomAvatar should render as expected for a LocalRoom 1`] = ` +
+ + + + +
+`; + +exports[`RoomAvatar should render as expected for a Room 1`] = ` +
+ + + + +
+`; diff --git a/test/components/views/rooms/RoomTile-test.tsx b/test/components/views/rooms/RoomTile-test.tsx index e02a72b716..b4bfde243c 100644 --- a/test/components/views/rooms/RoomTile-test.tsx +++ b/test/components/views/rooms/RoomTile-test.tsx @@ -75,20 +75,19 @@ describe("RoomTile", () => { }; let client: Mocked; - let restoreConsole: () => void; let voiceBroadcastInfoEvent: MatrixEvent; let room: Room; let renderResult: RenderResult; let sdkContext: TestSdkContext; + filterConsole( + // irrelevant for this test + "Room !1:example.org does not have an m.room.create event", + ); + beforeEach(() => { sdkContext = new TestSdkContext(); - restoreConsole = filterConsole( - // irrelevant for this test - "Room !1:example.org does not have an m.room.create event", - ); - client = mocked(stubClient()); sdkContext.client = client; DMRoomMap.makeShared(); @@ -105,7 +104,6 @@ describe("RoomTile", () => { }); afterEach(() => { - restoreConsole(); jest.clearAllMocks(); }); diff --git a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx index 0ff637e2c9..af94db6461 100644 --- a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx +++ b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx @@ -33,8 +33,6 @@ jest.mock("../../../../src/components/structures/HomePage", () => ({ })); describe("UserOnboardingPage", () => { - let restoreConsole: () => void; - const renderComponent = async (): Promise => { const renderResult = render(); await act(async () => { @@ -43,12 +41,10 @@ describe("UserOnboardingPage", () => { return renderResult; }; - beforeAll(() => { - restoreConsole = filterConsole( - // unrelated for this test - "could not update user onboarding context", - ); - }); + filterConsole( + // unrelated for this test + "could not update user onboarding context", + ); beforeEach(() => { stubClient(); @@ -60,10 +56,6 @@ describe("UserOnboardingPage", () => { jest.restoreAllMocks(); }); - afterAll(() => { - restoreConsole(); - }); - describe("when the user registered before the cutoff date", () => { beforeEach(() => { jest.spyOn(MatrixClientPeg, "userRegisteredAfter").mockReturnValue(false); diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index 47e037b8fd..47040dcb4b 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -92,6 +92,7 @@ export const unmockClientPeg = () => jest.spyOn(MatrixClientPeg, "get").mockRest */ export const mockClientMethodsUser = (userId = "@alice:domain") => ({ getUserId: jest.fn().mockReturnValue(userId), + getSafeUserId: jest.fn().mockReturnValue(userId), getUser: jest.fn().mockReturnValue(new User(userId)), isGuest: jest.fn().mockReturnValue(false), mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), diff --git a/test/test-utils/console.ts b/test/test-utils/console.ts index b4c4b98f91..468d2e3b8f 100644 --- a/test/test-utils/console.ts +++ b/test/test-utils/console.ts @@ -16,36 +16,39 @@ limitations under the License. type FilteredConsole = Pick; -const originalFunctions: FilteredConsole = { - log: console.log, - error: console.error, - info: console.info, - debug: console.debug, - warn: console.warn, -}; - /** * Allows to filter out specific messages in console.*. + * Call this from any describe block. + * Automagically restores the original function by implementing an afterAll hook. * * @param ignoreList Messages to be filtered - * @returns function to restore the console */ -export const filterConsole = (...ignoreList: string[]): (() => void) => { - for (const [key, originalFunction] of Object.entries(originalFunctions)) { - window.console[key as keyof FilteredConsole] = (...data: any[]) => { - const message = data?.[0]?.message || data?.[0]; +export const filterConsole = (...ignoreList: string[]): void => { + const originalFunctions: FilteredConsole = { + log: console.log, + error: console.error, + info: console.info, + debug: console.debug, + warn: console.warn, + }; - if (typeof message === "string" && ignoreList.some((i) => message.includes(i))) { - return; - } + beforeAll(() => { + for (const [key, originalFunction] of Object.entries(originalFunctions)) { + window.console[key as keyof FilteredConsole] = (...data: any[]) => { + const message = data?.[0]?.message || data?.[0]; - originalFunction(...data); - }; - } + if (typeof message === "string" && ignoreList.some((i) => message.includes(i))) { + return; + } - return () => { + originalFunction(...data); + }; + } + }); + + afterAll(() => { for (const [key, originalFunction] of Object.entries(originalFunctions)) { window.console[key as keyof FilteredConsole] = originalFunction; } - }; + }); }; diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx index 15b3348465..662d7b2619 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx @@ -62,7 +62,6 @@ describe("VoiceBroadcastRecordingPip", () => { let infoEvent: MatrixEvent; let recording: VoiceBroadcastRecording; let renderResult: RenderResult; - let restoreConsole: () => void; const renderPip = async (state: VoiceBroadcastInfoState) => { infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, state, client.getUserId() || "", client.getDeviceId() || ""); @@ -85,6 +84,8 @@ describe("VoiceBroadcastRecordingPip", () => { }); }; + filterConsole("Starting load of AsyncWrapper for modal"); + beforeAll(() => { client = stubClient(); mocked(requestMediaPermissions).mockResolvedValue({ @@ -105,11 +106,6 @@ describe("VoiceBroadcastRecordingPip", () => { [MediaDeviceKindEnum.VideoInput]: [], }); jest.spyOn(MediaDeviceHandler.instance, "setDevice").mockImplementation(); - restoreConsole = filterConsole("Starting load of AsyncWrapper for modal"); - }); - - afterAll(() => { - restoreConsole(); }); describe("when rendering a started recording", () => { diff --git a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx index e99605c7d3..2f3455dd0a 100644 --- a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx +++ b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.tsx @@ -35,10 +35,6 @@ import { flushPromises, stubClient } from "../../test-utils"; import { createTestPlayback } from "../../test-utils/audio"; import { mkVoiceBroadcastChunkEvent, mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils"; -jest.mock("../../../src/events/getReferenceRelationsForEvent", () => ({ - getReferenceRelationsForEvent: jest.fn(), -})); - jest.mock("../../../src/utils/MediaEventHelper", () => ({ MediaEventHelper: jest.fn(), }));