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

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

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"));
});