Hide the names of banned users behind a spoiler tag (attempt 2) (#32636)

This commit is contained in:
Andy Balaam
2026-03-18 10:47:16 +00:00
committed by GitHub
parent cb9ff9b92b
commit 2128c1884c
18 changed files with 328 additions and 59 deletions
@@ -20,6 +20,7 @@ import { KnownMembership } from "matrix-js-sdk/src/types";
import { render } from "jest-matrix-react";
import { type ReactElement } from "react";
import { type Mocked, mocked } from "jest-mock";
import React from "react";
import { hasText, textForEvent } from "../../src/TextForEvent";
import SettingsStore from "../../src/settings/SettingsStore";
@@ -28,6 +29,7 @@ import { MatrixClientPeg } from "../../src/MatrixClientPeg";
import UserIdentifierCustomisations from "../../src/customisations/UserIdentifier";
import { getSenderName } from "../../src/utils/event/getSenderName";
import { ElementCallEventType } from "../../src/call-types";
import Spoiler from "../../src/components/views/elements/Spoiler";
jest.mock("../../src/settings/SettingsStore");
jest.mock("../../src/customisations/UserIdentifier", () => ({
@@ -562,6 +564,50 @@ describe("TextForEvent", () => {
),
).toMatchInlineSnapshot(`"Member rejected the invitation: I don't want to be in this room."`);
});
it("shows single-user bans with a spoiler on display name", () => {
mocked(mockClient.getRoom).mockReturnValue({
getMember: jest.fn().mockImplementation((userId) => {
return { rawDisplayName: userId === "@admin:example.com" ? "Admin" : "Bad User" };
}),
} as unknown as Mocked<Room>);
expect(textForEvent(banEventWithReason(), mockClient, true)).toEqual(
<span>
Admin banned <Spoiler>Bad User</Spoiler>: bad behaviour
</span>,
);
});
it("hides user name for single-user bans with reason when JSX is not allowed", () => {
mocked(mockClient.getRoom).mockReturnValue({
getMember: jest.fn().mockImplementation((userId) => {
return { rawDisplayName: userId === "@admin:example.com" ? "Admin" : "Bad User" };
}),
} as unknown as Mocked<Room>);
expect(textForEvent(banEventWithReason(), mockClient)).toEqual("Admin banned a user: bad behaviour");
});
it("shows single-user bans with a spoiler on user ID", () => {
mocked(mockClient.getRoom).mockReturnValue({
getMember: jest.fn().mockReturnValue({ rawDisplayName: undefined }),
} as unknown as Mocked<Room>);
expect(textForEvent(banEvent(), mockClient, true)).toEqual(
<span>
@admin:example.com banned <Spoiler>@bad_name:bad_server.co</Spoiler>
</span>,
);
});
it("hides user name for single-user bans when JSX is not allowed", () => {
mocked(mockClient.getRoom).mockReturnValue({
getMember: jest.fn().mockReturnValue({ rawDisplayName: undefined }),
} as unknown as Mocked<Room>);
expect(textForEvent(banEvent(), mockClient)).toEqual("@admin:example.com banned a user");
});
});
describe("textForJoinRulesEvent()", () => {
@@ -717,3 +763,26 @@ describe("TextForEvent", () => {
});
});
});
function banEvent(): MatrixEvent {
return new MatrixEvent({
type: "m.room.member",
sender: "@admin:example.com",
content: {
membership: KnownMembership.Ban,
},
state_key: "@bad_name:bad_server.co",
});
}
function banEventWithReason(): MatrixEvent {
return new MatrixEvent({
type: "m.room.member",
sender: "@admin:example.com",
content: {
membership: KnownMembership.Ban,
reason: "bad behaviour",
},
state_key: "@bad_name:bad_server.co",
});
}
@@ -120,7 +120,9 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
@user:id made no changes 100 times
<span>
@user:id made no changes 100 times
</span>
</span>
</div>
</div>
@@ -269,7 +269,12 @@ describe("EventListSummary", function () {
const { container } = renderComponent(props);
const summary = container.querySelector(".mx_GenericEventListSummary_summary");
// The sequence was summarised correctly
expect(summary).toHaveTextContent("user_1 was unbanned, joined and left 7 times and was invited");
// And there is no spoiler on the user's name since they were not banned
expect(summary).not.toContainHTML("mx_EventTile_spoiler_content");
expect(summary).toMatchSnapshot();
});
@@ -314,9 +319,14 @@ describe("EventListSummary", function () {
const { container } = renderComponent(props);
const summary = container.querySelector(".mx_GenericEventListSummary_summary");
// The sequence was summarised correctly
expect(summary).toHaveTextContent(
"user_1 was unbanned, joined and left 2 times, was banned, " + "joined and left 3 times and was invited",
"user_1 was unbanned, joined and left 2 times, was banned, joined and left 3 times and was invited",
);
// And the banned user's name is hidden within a spoiler
expect(summary).toContainHTML('<span class="mx_EventTile_spoiler_content">user_1</span>');
expect(summary).toMatchSnapshot();
});
@@ -367,9 +377,14 @@ describe("EventListSummary", function () {
const { container } = renderComponent(props);
const summary = container.querySelector(".mx_GenericEventListSummary_summary");
// The sequence was summarised correctly
expect(summary).toHaveTextContent(
"user_1 and one other were unbanned, joined and left 2 times and were banned",
);
// And the banned user's name is hidden within a spoiler
expect(summary).toContainHTML('<span class="mx_EventTile_spoiler_content">user_1</span>');
expect(summary).toMatchSnapshot();
});
@@ -400,9 +415,14 @@ describe("EventListSummary", function () {
const { container } = renderComponent(props);
const summary = container.querySelector(".mx_GenericEventListSummary_summary");
// The sequence was summarised correctly
expect(summary).toHaveTextContent(
"user_0 and 19 others were unbanned, joined and left 2 times and were banned",
);
// And the banned user's name is hidden within a spoiler
expect(summary).toContainHTML('<span class="mx_EventTile_spoiler_content">user_0</span>');
expect(summary).toMatchSnapshot();
});
@@ -4,7 +4,20 @@ exports[`EventListSummary correctly identifies transitions 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 was invited, was banned, joined, rejected their invitation, left, had their invitation withdrawn, was unbanned, was removed, left and was removed
<span>
<button
class="mx_EventTile_spoiler"
>
 
<span
class="mx_EventTile_spoiler_content"
>
user_1
</span>
</button>
was invited, was banned, joined, rejected their invitation, left, had their invitation withdrawn, was unbanned, was removed, left and was removed
</span>
</span>
`;
@@ -12,9 +25,26 @@ exports[`EventListSummary correctly orders sequences of transitions by the order
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_2 was unbanned and joined and left 2 times
<span>
user_2
was unbanned and joined and left 2 times
</span>
,
user_1 was unbanned, joined and left 2 times and was banned
<span>
<button
class="mx_EventTile_spoiler"
>
 
<span
class="mx_EventTile_spoiler_content"
>
user_1
</span>
</button>
was unbanned, joined and left 2 times and was banned
</span>
</span>
`;
@@ -22,7 +52,16 @@ exports[`EventListSummary handles a summary length = 2, with 1 "other" 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1, user_2 and one other joined
<span>
<span>
user_1
,
user_2
and one other
</span>
joined
</span>
</span>
`;
@@ -30,7 +69,16 @@ exports[`EventListSummary handles a summary length = 2, with many "others" 1`] =
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_0, user_1 and 18 others joined
<span>
<span>
user_0
,
user_1
and 18 others
</span>
joined
</span>
</span>
`;
@@ -38,7 +86,13 @@ exports[`EventListSummary handles a summary length = 2, with no "others" 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 and user_2 joined 2 times
<span>
user_1
and
user_2
joined 2 times
</span>
</span>
`;
@@ -46,7 +100,11 @@ exports[`EventListSummary handles invitation plurals correctly when there are mu
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 rejected their invitation 2 times
<span>
user_1
rejected their invitation 2 times
</span>
</span>
`;
@@ -54,7 +112,14 @@ exports[`EventListSummary handles invitation plurals correctly when there are mu
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 and one other rejected their invitations and had their invitations withdrawn
<span>
<span>
user_1
and one other
</span>
rejected their invitations and had their invitations withdrawn
</span>
</span>
`;
@@ -62,7 +127,23 @@ exports[`EventListSummary handles many users following the same sequence of memb
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_0 and 19 others were unbanned, joined and left 2 times and were banned
<span>
<span>
<button
class="mx_EventTile_spoiler"
>
 
<span
class="mx_EventTile_spoiler_content"
>
user_0
</span>
</button>
and 19 others
</span>
were unbanned, joined and left 2 times and were banned
</span>
</span>
`;
@@ -70,7 +151,23 @@ exports[`EventListSummary handles multiple users following the same sequence of
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 and one other were unbanned, joined and left 2 times and were banned
<span>
<span>
<button
class="mx_EventTile_spoiler"
>
 
<span
class="mx_EventTile_spoiler_content"
>
user_1
</span>
</button>
and one other
</span>
were unbanned, joined and left 2 times and were banned
</span>
</span>
`;
@@ -78,7 +175,11 @@ exports[`EventListSummary renders collapsed events if events.length = props.thre
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 joined and left and joined
<span>
user_1
joined and left and joined
</span>
</span>
`;
@@ -111,9 +212,17 @@ exports[`EventListSummary should not blindly group 3pid invites and treat them a
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
n...@d... was invited 2 times
<span>
n...@d...
was invited 2 times
</span>
,
d...@w... was invited
<span>
d...@w...
was invited
</span>
</span>
`;
@@ -121,7 +230,11 @@ exports[`EventListSummary truncates long join,leave repetitions 1`] = `
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 joined and left 7 times
<span>
user_1
joined and left 7 times
</span>
</span>
`;
@@ -129,7 +242,11 @@ exports[`EventListSummary truncates long join,leave repetitions between other ev
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 was unbanned, joined and left 7 times and was invited
<span>
user_1
was unbanned, joined and left 7 times and was invited
</span>
</span>
`;
@@ -137,6 +254,19 @@ exports[`EventListSummary truncates multiple sequences of repetitions with other
<span
class="mx_TextualEvent mx_GenericEventListSummary_summary"
>
user_1 was unbanned, joined and left 2 times, was banned, joined and left 3 times and was invited
<span>
<button
class="mx_EventTile_spoiler"
>
 
<span
class="mx_EventTile_spoiler_content"
>
user_1
</span>
</button>
was unbanned, joined and left 2 times, was banned, joined and left 3 times and was invited
</span>
</span>
`;