1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-07-28 15:22:05 +03:00

Merge branch 'develop' into andybalaam/stas-demydiuk-membership-type3

This commit is contained in:
Andy Balaam
2024-03-20 17:25:23 +00:00
124 changed files with 2399 additions and 1052 deletions

View File

@ -18,7 +18,7 @@ exports[`SlashCommands /rainbow should make things rainbowy 1`] = `
{
"body": "this is a test message",
"format": "org.matrix.custom.html",
"formatted_body": "<font color="#ff00be">t</font><font color="#ff0080">h</font><font color="#ff0041">i</font><font color="#ff5f00">s</font> <font color="#faa900">i</font><font color="#c3bf00">s</font> <font color="#00d800">a</font> <font color="#00e371">t</font><font color="#00e6b6">e</font><font color="#00e7f8">s</font><font color="#00e7ff">t</font> <font color="#00deff">m</font><font color="#00d2ff">e</font><font color="#00c0ff">s</font><font color="#44a4ff">s</font><font color="#e87dff">a</font><font color="#ff42ff">g</font><font color="#ff00fe">e</font>",
"formatted_body": "<span data-mx-color="#ff00be">t</span><span data-mx-color="#ff0080">h</span><span data-mx-color="#ff0041">i</span><span data-mx-color="#ff5f00">s</span> <span data-mx-color="#faa900">i</span><span data-mx-color="#c3bf00">s</span> <span data-mx-color="#00d800">a</span> <span data-mx-color="#00e371">t</span><span data-mx-color="#00e6b6">e</span><span data-mx-color="#00e7f8">s</span><span data-mx-color="#00e7ff">t</span> <span data-mx-color="#00deff">m</span><span data-mx-color="#00d2ff">e</span><span data-mx-color="#00c0ff">s</span><span data-mx-color="#44a4ff">s</span><span data-mx-color="#e87dff">a</span><span data-mx-color="#ff42ff">g</span><span data-mx-color="#ff00fe">e</span>",
"msgtype": "m.text",
}
`;
@ -27,7 +27,7 @@ exports[`SlashCommands /rainbowme should make things rainbowy 1`] = `
{
"body": "this is a test message",
"format": "org.matrix.custom.html",
"formatted_body": "<font color="#ff00be">t</font><font color="#ff0080">h</font><font color="#ff0041">i</font><font color="#ff5f00">s</font> <font color="#faa900">i</font><font color="#c3bf00">s</font> <font color="#00d800">a</font> <font color="#00e371">t</font><font color="#00e6b6">e</font><font color="#00e7f8">s</font><font color="#00e7ff">t</font> <font color="#00deff">m</font><font color="#00d2ff">e</font><font color="#00c0ff">s</font><font color="#44a4ff">s</font><font color="#e87dff">a</font><font color="#ff42ff">g</font><font color="#ff00fe">e</font>",
"formatted_body": "<span data-mx-color="#ff00be">t</span><span data-mx-color="#ff0080">h</span><span data-mx-color="#ff0041">i</span><span data-mx-color="#ff5f00">s</span> <span data-mx-color="#faa900">i</span><span data-mx-color="#c3bf00">s</span> <span data-mx-color="#00d800">a</span> <span data-mx-color="#00e371">t</span><span data-mx-color="#00e6b6">e</span><span data-mx-color="#00e7f8">s</span><span data-mx-color="#00e7ff">t</span> <span data-mx-color="#00deff">m</span><span data-mx-color="#00d2ff">e</span><span data-mx-color="#00c0ff">s</span><span data-mx-color="#44a4ff">s</span><span data-mx-color="#e87dff">a</span><span data-mx-color="#ff42ff">g</span><span data-mx-color="#ff00fe">e</span>",
"msgtype": "m.emote",
}
`;

View File

@ -141,10 +141,28 @@ describe("RoomGeneralContextMenu", () => {
const markAsReadBtn = getByLabelText(container, "Mark as read");
fireEvent.click(markAsReadBtn);
await new Promise(setImmediate);
expect(mockClient.sendReadReceipt).toHaveBeenCalledWith(event, ReceiptType.Read, true);
expect(onFinished).toHaveBeenCalled();
});
it("marks the room as unread", async () => {
room.updateMyMembership("join");
const { container } = getComponent({});
const markAsUnreadBtn = getByLabelText(container, "Mark as unread");
fireEvent.click(markAsUnreadBtn);
await new Promise(setImmediate);
expect(mockClient.setRoomAccountData).toHaveBeenCalledWith(ROOM_ID, "com.famedly.marked_unread", {
unread: true,
});
expect(onFinished).toHaveBeenCalled();
});
it("when developer mode is disabled, it should not render the developer tools option", () => {
getComponent();
expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();

View File

@ -15,22 +15,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { render } from "@testing-library/react";
import { render, waitFor } from "@testing-library/react";
import hljs, { type HighlightOptions } from "highlight.js";
import React from "react";
import SyntaxHighlight from "../../../../src/components/views/elements/SyntaxHighlight";
describe("<SyntaxHighlight />", () => {
it("renders", () => {
it("renders", async () => {
const { container } = render(<SyntaxHighlight>console.log("Hello, World!");</SyntaxHighlight>);
await waitFor(() => expect(container.querySelector(".language-arcade")).toBeTruthy());
expect(container).toMatchSnapshot();
});
it.each(["json", "javascript", "css"])("uses the provided language", (lang) => {
it.each(["json", "javascript", "css"])("uses the provided language", async (lang) => {
const mock = jest.spyOn(hljs, "highlight");
render(<SyntaxHighlight language={lang}>// Hello, World</SyntaxHighlight>);
const { container } = render(<SyntaxHighlight language={lang}>// Hello, World</SyntaxHighlight>);
await waitFor(() => expect(container.querySelector(`.language-${lang}`)).toBeTruthy());
const [_lang, opts] = mock.mock.lastCall!;
expect((opts as HighlightOptions)["language"]).toBe(lang);

View File

@ -24,6 +24,7 @@ import {
RoomStateEvent,
PendingEventOrdering,
ISearchResults,
IContent,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { CallType } from "matrix-js-sdk/src/webrtc/call";
@ -112,7 +113,7 @@ describe("LegacyRoomHeader", () => {
room: roomId,
user: alice.userId,
skey: stateKey,
content,
content: content as IContent,
});
room.addLiveEvents([event]);
return { event_id: event.getId()! };

View File

@ -20,8 +20,41 @@ import React from "react";
import { StatelessNotificationBadge } from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge";
import SettingsStore from "../../../../../src/settings/SettingsStore";
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
import NotificationBadge from "../../../../../src/components/views/rooms/NotificationBadge";
import { NotificationState } from "../../../../../src/stores/notifications/NotificationState";
class DummyNotificationState extends NotificationState {
constructor(level: NotificationLevel) {
super();
this._level = level;
}
}
describe("NotificationBadge", () => {
it("shows a dot if the level is activity", () => {
const notif = new DummyNotificationState(NotificationLevel.Activity);
const { container } = render(<NotificationBadge roomId="!foo:bar" notification={notif} />);
expect(container.querySelector(".mx_NotificationBadge_dot")).toBeInTheDocument();
expect(container.querySelector(".mx_NotificationBadge")).toBeInTheDocument();
});
it("does not show a dot if the level is activity and hideIfDot is true", () => {
const notif = new DummyNotificationState(NotificationLevel.Activity);
const { container } = render(<NotificationBadge roomId="!foo:bar" notification={notif} hideIfDot={true} />);
expect(container.querySelector(".mx_NotificationBadge_dot")).not.toBeInTheDocument();
expect(container.querySelector(".mx_NotificationBadge")).not.toBeInTheDocument();
});
it("still shows an empty badge if hideIfDot us true", () => {
const notif = new DummyNotificationState(NotificationLevel.Notification);
const { container } = render(<NotificationBadge roomId="!foo:bar" notification={notif} hideIfDot={true} />);
expect(container.querySelector(".mx_NotificationBadge_dot")).not.toBeInTheDocument();
expect(container.querySelector(".mx_NotificationBadge")).toBeInTheDocument();
});
describe("StatelessNotificationBadge", () => {
it("lets you click it", () => {
const cb = jest.fn();

View File

@ -36,6 +36,13 @@ describe("StatelessNotificationBadge", () => {
expect(container.querySelector(".mx_NotificationBadge_knocked")).toBeInTheDocument();
});
it("has dot style for activity", () => {
const { container } = render(
<StatelessNotificationBadge symbol={null} count={3} level={NotificationLevel.Activity} />,
);
expect(container.querySelector(".mx_NotificationBadge_dot")).toBeInTheDocument();
});
it("has badge style for notification", () => {
const { container } = render(
<StatelessNotificationBadge symbol={null} count={3} level={NotificationLevel.Notification} />,

View File

@ -0,0 +1,120 @@
/*
*
* Copyright 2024 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, screen } from "@testing-library/react";
import React, { ComponentProps } from "react";
import userEvent from "@testing-library/user-event";
import { PowerLevelSelector } from "../../../../src/components/views/settings/PowerLevelSelector";
import { stubClient } from "../../../test-utils";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
describe("PowerLevelSelector", () => {
const matrixClient = stubClient();
const currentUser = matrixClient.getUserId()!;
const userLevels = {
[currentUser]: 100,
"@alice:server.org": 50,
"@bob:server.org": 0,
};
const renderPLS = (props: Partial<ComponentProps<typeof PowerLevelSelector>>) =>
render(
<MatrixClientContext.Provider value={matrixClient}>
<PowerLevelSelector
userLevels={userLevels}
canChangeLevels={true}
currentUserLevel={userLevels[currentUser]}
title="title"
// filter nothing by default
filter={() => true}
onClick={jest.fn()}
{...props}
>
empty label
</PowerLevelSelector>
</MatrixClientContext.Provider>,
);
it("should render", () => {
renderPLS({});
expect(screen.getByRole("group")).toMatchSnapshot();
});
it("should display only the current user", async () => {
// Display only the current user
renderPLS({ filter: (user) => user === currentUser });
// Only alice should be displayed
const userSelects = screen.getAllByRole("combobox");
expect(userSelects).toHaveLength(1);
expect(userSelects[0]).toHaveAccessibleName(currentUser);
expect(screen.getByRole("group")).toMatchSnapshot();
});
it("should be able to change the power level of the current user", async () => {
const onClick = jest.fn();
renderPLS({ onClick });
// Until the power level is changed, the apply button should be disabled
// compound button is using aria-disabled instead of the disabled attribute, we can't toBeDisabled on it
expect(screen.getByRole("button", { name: "Apply" })).toHaveAttribute("aria-disabled", "true");
const select = screen.getByRole("combobox", { name: currentUser });
// Sanity check
expect(select).toHaveValue("100");
// Change current user power level to 50
await userEvent.selectOptions(select, "50");
expect(select).toHaveValue("50");
// After the user level changes, the apply button should be enabled
expect(screen.getByRole("button", { name: "Apply" })).toHaveAttribute("aria-disabled", "false");
// Click on Apply should call onClick with the new power level
await userEvent.click(screen.getByRole("button", { name: "Apply" }));
expect(onClick).toHaveBeenCalledWith(50, currentUser);
});
it("should not be able to change the power level if `canChangeLevels` is false", async () => {
renderPLS({ canChangeLevels: false });
// The selects should be disabled
const userSelects = screen.getAllByRole("combobox");
userSelects.forEach((select) => expect(select).toBeDisabled());
});
it("should be able to change only the level of someone with a lower level", async () => {
const userLevels = {
[currentUser]: 50,
"@alice:server.org": 100,
};
renderPLS({ userLevels });
expect(screen.getByRole("combobox", { name: currentUser })).toBeEnabled();
expect(screen.getByRole("combobox", { name: "@alice:server.org" })).toBeDisabled();
});
it("should display the children if there is no user to display", async () => {
// No user to display
renderPLS({ filter: () => false });
expect(screen.getByText("empty label")).toBeInTheDocument();
});
});

View File

@ -0,0 +1,235 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PowerLevelSelector should display only the current user 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
title
</legend>
<div
class="mx_SettingsFieldset_content"
>
<div
class="mx_PowerSelector"
>
<div
class="mx_Field mx_Field_select"
>
<select
data-testid="power-level-select-element"
id="mx_Field_4"
label="@userId:matrix.org"
placeholder="@userId:matrix.org"
type="text"
>
<option
data-testid="power-level-option-0"
value="0"
>
Default
</option>
<option
data-testid="power-level-option-50"
value="50"
>
Moderator
</option>
<option
data-testid="power-level-option-100"
value="100"
>
Admin
</option>
<option
data-testid="power-level-option-SELECT_VALUE_CUSTOM"
value="SELECT_VALUE_CUSTOM"
>
Custom level
</option>
</select>
<label
for="mx_Field_4"
>
@userId:matrix.org
</label>
</div>
</div>
<button
aria-disabled="true"
aria-label="Apply"
class="_button_dyfp8_17 mx_Dialog_nonDialogButton mx_PowerLevelSelector_Button"
data-kind="primary"
data-size="sm"
role="button"
tabindex="0"
>
Apply
</button>
</div>
</fieldset>
`;
exports[`PowerLevelSelector should render 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
title
</legend>
<div
class="mx_SettingsFieldset_content"
>
<div
class="mx_PowerSelector"
>
<div
class="mx_Field mx_Field_select"
>
<select
data-testid="power-level-select-element"
id="mx_Field_1"
label="@bob:server.org"
placeholder="@bob:server.org"
type="text"
>
<option
data-testid="power-level-option-0"
value="0"
>
Default
</option>
<option
data-testid="power-level-option-50"
value="50"
>
Moderator
</option>
<option
data-testid="power-level-option-100"
value="100"
>
Admin
</option>
<option
data-testid="power-level-option-SELECT_VALUE_CUSTOM"
value="SELECT_VALUE_CUSTOM"
>
Custom level
</option>
</select>
<label
for="mx_Field_1"
>
@bob:server.org
</label>
</div>
</div>
<div
class="mx_PowerSelector"
>
<div
class="mx_Field mx_Field_select"
>
<select
data-testid="power-level-select-element"
id="mx_Field_2"
label="@alice:server.org"
placeholder="@alice:server.org"
type="text"
>
<option
data-testid="power-level-option-0"
value="0"
>
Default
</option>
<option
data-testid="power-level-option-50"
value="50"
>
Moderator
</option>
<option
data-testid="power-level-option-100"
value="100"
>
Admin
</option>
<option
data-testid="power-level-option-SELECT_VALUE_CUSTOM"
value="SELECT_VALUE_CUSTOM"
>
Custom level
</option>
</select>
<label
for="mx_Field_2"
>
@alice:server.org
</label>
</div>
</div>
<div
class="mx_PowerSelector"
>
<div
class="mx_Field mx_Field_select"
>
<select
data-testid="power-level-select-element"
id="mx_Field_3"
label="@userId:matrix.org"
placeholder="@userId:matrix.org"
type="text"
>
<option
data-testid="power-level-option-0"
value="0"
>
Default
</option>
<option
data-testid="power-level-option-50"
value="50"
>
Moderator
</option>
<option
data-testid="power-level-option-100"
value="100"
>
Admin
</option>
<option
data-testid="power-level-option-SELECT_VALUE_CUSTOM"
value="SELECT_VALUE_CUSTOM"
>
Custom level
</option>
</select>
<label
for="mx_Field_3"
>
@userId:matrix.org
</label>
</div>
</div>
<button
aria-disabled="true"
aria-label="Apply"
class="_button_dyfp8_17 mx_Dialog_nonDialogButton mx_PowerLevelSelector_Button"
data-kind="primary"
data-size="sm"
role="button"
tabindex="0"
>
Apply
</button>
</div>
</fieldset>
`;

View File

@ -15,11 +15,12 @@ limitations under the License.
*/
import React from "react";
import { fireEvent, render, RenderResult, screen, waitFor } from "@testing-library/react";
import { fireEvent, getByRole, render, RenderResult, screen, waitFor } from "@testing-library/react";
import { MatrixClient, EventType, MatrixEvent, Room, RoomMember, ISendEventResponse } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { mocked } from "jest-mock";
import { defer } from "matrix-js-sdk/src/utils";
import userEvent from "@testing-library/user-event";
import RolesRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab";
import { mkStubRoom, withClientContextRenderOptions, stubClient } from "../../../../../test-utils";
@ -263,6 +264,11 @@ describe("RolesRoomSettingsTab", () => {
fireEvent.change(selector, { target: { value: "50" } });
expect(selector).toHaveValue("50");
// Get the apply button of the privileged user section and click on it
const privilegedUsersSection = screen.getByRole("group", { name: "Privileged Users" });
const applyButton = getByRole(privilegedUsersSection, "button", { name: "Apply" });
await userEvent.click(applyButton);
deferred.reject("Error");
await waitFor(() => expect(selector).toHaveValue("100"));
});

View File

@ -17,7 +17,15 @@ limitations under the License.
import EventEmitter from "events";
import { mocked } from "jest-mock";
import { waitFor } from "@testing-library/react";
import { RoomType, Room, RoomEvent, MatrixEvent, RoomStateEvent, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
import {
RoomType,
Room,
RoomEvent,
MatrixEvent,
RoomStateEvent,
PendingEventOrdering,
IContent,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { Widget } from "matrix-widget-api";
// eslint-disable-next-line no-restricted-imports
@ -117,7 +125,7 @@ const setUpClientRoomAndStores = (): {
room: roomId,
user: alice.userId,
skey: stateKey,
content,
content: content as IContent,
});
room.addLiveEvents([event]);
return { event_id: event.getId()! };

View File

@ -108,6 +108,7 @@ describe("RoomViewStore", function () {
relations: jest.fn(),
knockRoom: jest.fn(),
leave: jest.fn(),
setRoomAccountData: jest.fn(),
});
const room = new Room(roomId, mockClient, userId);
const room2 = new Room(roomId2, mockClient, userId);
@ -339,6 +340,17 @@ describe("RoomViewStore", function () {
expect(mocked(Modal).createDialog.mock.calls[0][1]).toMatchSnapshot();
});
it("clears the unread flag when viewing a room", async () => {
room.getAccountData = jest.fn().mockReturnValue({
getContent: jest.fn().mockReturnValue({ unread: true }),
});
dis.dispatch({ action: Action.ViewRoom, room_id: roomId });
await untilDispatch(Action.ActiveRoomChanged, dis);
expect(mockClient.setRoomAccountData).toHaveBeenCalledWith(roomId, "com.famedly.marked_unread", {
unread: false,
});
});
describe("when listening to a voice broadcast", () => {
let voiceBroadcastPlayback: VoiceBroadcastPlayback;

View File

@ -22,6 +22,7 @@ import {
NotificationCountType,
EventType,
MatrixEvent,
RoomEvent,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
@ -81,7 +82,7 @@ describe("RoomNotificationState", () => {
room.setUnreadNotificationCount(NotificationCountType.Total, greys);
}
it("Updates on event decryption", () => {
it("updates on event decryption", () => {
const roomNotifState = new RoomNotificationState(room, true);
const listener = jest.fn();
roomNotifState.addListener(NotificationStateEvents.Update, listener);
@ -93,6 +94,36 @@ describe("RoomNotificationState", () => {
expect(listener).toHaveBeenCalled();
});
it("emits an Update event on marked unread room account data", () => {
const roomNotifState = new RoomNotificationState(room, true);
const listener = jest.fn();
roomNotifState.addListener(NotificationStateEvents.Update, listener);
const accountDataEvent = {
getType: () => "com.famedly.marked_unread",
getContent: () => {
return { unread: true };
},
} as unknown as MatrixEvent;
room.getAccountData = jest.fn().mockReturnValue(accountDataEvent);
room.emit(RoomEvent.AccountData, accountDataEvent, room);
expect(listener).toHaveBeenCalled();
});
it("does not update on other account data", () => {
const roomNotifState = new RoomNotificationState(room, true);
const listener = jest.fn();
roomNotifState.addListener(NotificationStateEvents.Update, listener);
const accountDataEvent = {
getType: () => "else.something",
getContent: () => {
return {};
},
} as unknown as MatrixEvent;
room.getAccountData = jest.fn().mockReturnValue(accountDataEvent);
room.emit(RoomEvent.AccountData, accountDataEvent, room);
expect(listener).not.toHaveBeenCalled();
});
it("removes listeners", () => {
const roomNotifState = new RoomNotificationState(room, false);
expect(() => roomNotifState.destroy()).not.toThrow();

View File

@ -18,7 +18,7 @@ import { textToHtmlRainbow } from "../../src/utils/colour";
describe("textToHtmlRainbow", () => {
it("correctly transform text to html without splitting the emoji in two", () => {
expect(textToHtmlRainbow("🐻")).toBe('<font color="#ff00be">🐻</font>');
expect(textToHtmlRainbow("🐕‍🦺")).toBe('<font color="#ff00be">🐕‍🦺</font>');
expect(textToHtmlRainbow("🐻")).toBe('<span data-mx-color="#ff00be">🐻</span>');
expect(textToHtmlRainbow("🐕‍🦺")).toBe('<span data-mx-color="#ff00be">🐕‍🦺</span>');
});
});

View File

@ -26,6 +26,8 @@ import {
clearRoomNotification,
notificationLevelToIndicator,
getThreadNotificationLevel,
getMarkedUnreadState,
setMarkedUnreadState,
} from "../../src/utils/notifications";
import SettingsStore from "../../src/settings/SettingsStore";
import { getMockClientWithEventEmitter } from "../test-utils/client";
@ -135,8 +137,8 @@ describe("notifications", () => {
});
});
it("sends a request even if everything has been read", () => {
clearRoomNotification(room, client);
it("sends a request even if everything has been read", async () => {
await clearRoomNotification(room, client);
expect(sendReadReceiptSpy).toHaveBeenCalledWith(message, ReceiptType.Read, true);
});
@ -155,8 +157,8 @@ describe("notifications", () => {
sendReceiptsSetting = false;
});
it("should send a private read receipt", () => {
clearRoomNotification(room, client);
it("should send a private read receipt", async () => {
await clearRoomNotification(room, client);
expect(sendReadReceiptSpy).toHaveBeenCalledWith(message, ReceiptType.ReadPrivate, true);
});
});
@ -186,7 +188,7 @@ describe("notifications", () => {
expect(sendReadReceiptSpy).not.toHaveBeenCalled();
});
it("sends unthreaded receipt requests", () => {
it("sends unthreaded receipt requests", async () => {
const message = mkMessage({
event: true,
room: ROOM_ID,
@ -196,12 +198,12 @@ describe("notifications", () => {
room.addLiveEvents([message]);
room.setUnreadNotificationCount(NotificationCountType.Total, 1);
clearAllNotifications(client);
await clearAllNotifications(client);
expect(sendReadReceiptSpy).toHaveBeenCalledWith(message, ReceiptType.Read, true);
});
it("sends private read receipts", () => {
it("sends private read receipts", async () => {
const message = mkMessage({
event: true,
room: ROOM_ID,
@ -213,12 +215,121 @@ describe("notifications", () => {
jest.spyOn(SettingsStore, "getValue").mockReset().mockReturnValue(false);
clearAllNotifications(client);
await clearAllNotifications(client);
expect(sendReadReceiptSpy).toHaveBeenCalledWith(message, ReceiptType.ReadPrivate, true);
});
});
describe("getMarkedUnreadState", () => {
let client: MatrixClient;
let room: Room;
const ROOM_ID = "123";
const USER_ID = "@bob:example.org";
beforeEach(() => {
stubClient();
client = mocked(MatrixClientPeg.safeGet());
room = new Room(ROOM_ID, client, USER_ID);
});
it("reads from stable prefix", async () => {
room.getAccountData = jest.fn().mockImplementation((eventType: string) => {
if (eventType === "m.marked_unread") {
return { getContent: jest.fn().mockReturnValue({ unread: true }) };
}
return null;
});
expect(getMarkedUnreadState(room)).toBe(true);
});
it("reads from unstable prefix", async () => {
room.getAccountData = jest.fn().mockImplementation((eventType: string) => {
if (eventType === "com.famedly.marked_unread") {
return { getContent: jest.fn().mockReturnValue({ unread: true }) };
}
return null;
});
expect(getMarkedUnreadState(room)).toBe(true);
});
it("returns undefined if neither prefix is present", async () => {
room.getAccountData = jest.fn().mockImplementation((eventType: string) => {
return null;
});
expect(getMarkedUnreadState(room)).toBe(undefined);
});
});
describe("setUnreadMarker", () => {
let client: MatrixClient;
let room: Room;
const ROOM_ID = "123";
const USER_ID = "@bob:example.org";
beforeEach(() => {
stubClient();
client = mocked(MatrixClientPeg.safeGet());
room = new Room(ROOM_ID, client, USER_ID);
});
// set true, no existing event
it("sets unread flag if event doesn't exist", async () => {
await setMarkedUnreadState(room, client, true);
expect(client.setRoomAccountData).toHaveBeenCalledWith(ROOM_ID, "com.famedly.marked_unread", {
unread: true,
});
});
// set false, no existing event
it("does nothing when clearing if flag is false", async () => {
await setMarkedUnreadState(room, client, false);
expect(client.setRoomAccountData).not.toHaveBeenCalled();
});
// set true, existing event = false
it("sets unread flag to if existing event is false", async () => {
room.getAccountData = jest
.fn()
.mockReturnValue({ getContent: jest.fn().mockReturnValue({ unread: false }) });
await setMarkedUnreadState(room, client, true);
expect(client.setRoomAccountData).toHaveBeenCalledWith(ROOM_ID, "com.famedly.marked_unread", {
unread: true,
});
});
// set false, existing event = false
it("does nothing if set false and existing event is false", async () => {
room.getAccountData = jest
.fn()
.mockReturnValue({ getContent: jest.fn().mockReturnValue({ unread: false }) });
await setMarkedUnreadState(room, client, false);
expect(client.setRoomAccountData).not.toHaveBeenCalled();
});
// set true, existing event = true
it("does nothing if setting true and existing event is true", async () => {
room.getAccountData = jest
.fn()
.mockReturnValue({ getContent: jest.fn().mockReturnValue({ unread: true }) });
await setMarkedUnreadState(room, client, true);
expect(client.setRoomAccountData).not.toHaveBeenCalled();
});
// set false, existing event = true
it("sets flag if setting false and existing event is true", async () => {
room.getAccountData = jest
.fn()
.mockReturnValue({ getContent: jest.fn().mockReturnValue({ unread: true }) });
await setMarkedUnreadState(room, client, false);
expect(client.setRoomAccountData).toHaveBeenCalledWith(ROOM_ID, "com.famedly.marked_unread", {
unread: false,
});
});
});
describe("notificationLevelToIndicator", () => {
it("returns undefined if notification level is None", () => {
expect(notificationLevelToIndicator(NotificationLevel.None)).toBeUndefined();