diff --git a/apps/web/playwright/e2e/timeline/event-list-summary.spec.ts b/apps/web/playwright/e2e/timeline/event-list-summary.spec.ts index cb92d87ffe..3a14753d58 100644 --- a/apps/web/playwright/e2e/timeline/event-list-summary.spec.ts +++ b/apps/web/playwright/e2e/timeline/event-list-summary.spec.ts @@ -40,7 +40,7 @@ test.describe("Event List Summary", () => { ); test( - "should display a single ban message on its own", + "should display a single ban message on its own, with the user hidden", { tag: "@screenshot" }, async ({ app, homeserver, page, user }) => { const { bot, roomId } = await setupRoom(app, homeserver, page, user); @@ -59,7 +59,7 @@ test.describe("Event List Summary", () => { // When we ban the bot await app.client.ban(roomId, bot.credentials.userId); - // Then we say that + // Then we say that, but the name is hidden await expect( page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", { hasText: "banned", @@ -106,7 +106,7 @@ test.describe("Event List Summary", () => { ); test( - "should display multiple messages as a group", + "should display multiple messages as a group, and hide banned names", { tag: "@screenshot" }, async ({ app, homeserver, page, user }) => { const { bot, roomId } = await setupRoom(app, homeserver, page, user); @@ -125,7 +125,7 @@ test.describe("Event List Summary", () => { await app.client.inviteUser(roomId, bot.credentials.userId); await bot.joinRoom(roomId); - // Then those actions are gathered into a single summary + // Then those actions are gathered into a single summary, with the name hidden await expect( page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", { hasText: "and joined", @@ -191,6 +191,9 @@ test.describe("Event List Summary", () => { // here. await page.getByRole("button", { name: "expand" }).nth(3).click(); + // Make sure the mouse is in a consistent position to avoid flaking + await page.getByRole("textbox").hover(); + // Then we see all the individual actions await expect( page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", { @@ -207,7 +210,7 @@ test.describe("Event List Summary", () => { ); test( - "should display join/ban messages for multiple people as a group", + "should display join/ban messages for multiple people as a group, with banned names hidden", { tag: "@screenshot" }, async ({ app, homeserver, page, user }) => { const { bot, roomId } = await setupRoom(app, homeserver, page, user); @@ -237,7 +240,7 @@ test.describe("Event List Summary", () => { await bot.joinRoom(roomId); await bot2.joinRoom(roomId); - // Then those actions are gathered into a single summary + // Then those actions are gathered into a single summary, with banned names hidden await expect( page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", { hasText: "was removed, was invited, and joined", @@ -258,7 +261,10 @@ test.describe("Event List Summary", () => { // here. await page.getByRole("button", { name: "expand" }).nth(3).click(); - // Then we see all the individual actions + // Make sure the mouse is in a consistent position to avoid flaking + await page.getByRole("textbox").hover(); + + // Then we see all the individual actions, with banned names hidden await expect( page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", { hasText: "removed MyBot2", @@ -321,9 +327,11 @@ async function replaceBotIds(page: Page, bot: Bot, bot2?: Bot) { await page.evaluate( ([bot1UserId, bot2UserId]) => { for (const el of document.querySelectorAll("div.mx_TextualEvent")) { - if ("innerText" in el) { - el.innerText = (el.innerText as any as string).replaceAll(bot1UserId, "<>"); - el.innerText = (el.innerText as any as string).replaceAll(bot2UserId, "<>"); + const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); + while (walker.nextNode()) { + const node = walker.currentNode; + node.textContent = node.textContent.replaceAll(bot1UserId, "<>"); + node.textContent = node.textContent.replaceAll(bot2UserId, "<>"); } } }, diff --git a/apps/web/playwright/e2e/timeline/timeline.spec.ts b/apps/web/playwright/e2e/timeline/timeline.spec.ts index 22d5742dfc..aaffffa76d 100644 --- a/apps/web/playwright/e2e/timeline/timeline.spec.ts +++ b/apps/web/playwright/e2e/timeline/timeline.spec.ts @@ -182,10 +182,10 @@ test.describe("Timeline", () => { await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-irc-layout.png", { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -218,10 +218,10 @@ test.describe("Timeline", () => { await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-modern-layout.png", { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -260,7 +260,7 @@ test.describe("Timeline", () => { await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-bubble-layout.png", { // Exclude timestamp from snapshot css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } `, @@ -280,7 +280,7 @@ test.describe("Timeline", () => { // Save snapshot of collapsed generic event list summary on bubble layout await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("collapsed-gels-bubble-layout.png", { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } `, @@ -325,10 +325,10 @@ test.describe("Timeline", () => { { // Exclude timestamp and read marker from snapshot css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -447,7 +447,7 @@ test.describe("Timeline", () => { { // Exclude timestamp from snapshot of mx_MainSplit css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } `, @@ -523,10 +523,10 @@ test.describe("Timeline", () => { const screenshotOptions = { // Hide because flaky - See https://github.com/vector-im/element-web/issues/24957 css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -651,10 +651,10 @@ test.describe("Timeline", () => { // Exclude timestamp and read marker from snapshot const screenshotOptions = { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -1131,10 +1131,10 @@ test.describe("Timeline", () => { // Exclude timestamp and read marker from snapshot const screenshotOptions = { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar,.mx_JumpToBottomButton { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -1259,10 +1259,10 @@ test.describe("Timeline", () => { // Exclude timestamp and read marker from snapshot const screenshotOptions = { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, @@ -1325,10 +1325,10 @@ test.describe("Timeline", () => { // Exclude timestamp and read marker from snapshot const screenshotOptions = { css: ` - .mx_MessageTimestamp { + .mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { + .mx_MessagePanel_myReadMarker { display: none !important; } `, diff --git a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/bot-was-banned-linux.png b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/bot-was-banned-linux.png index 7b3cb7a698..460e9bea73 100644 Binary files a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/bot-was-banned-linux.png and b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/bot-was-banned-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-join-ban-messages-linux.png b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-join-ban-messages-linux.png index 36d390596e..8706084d06 100644 Binary files a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-join-ban-messages-linux.png and b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-join-ban-messages-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-expanded-linux.png b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-expanded-linux.png index d610b63a9a..b66a1cefd1 100644 Binary files a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-expanded-linux.png and b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-expanded-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-linux.png b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-linux.png index 3b775c08f8..a4cbb01f03 100644 Binary files a/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-linux.png and b/apps/web/playwright/snapshots/timeline/event-list-summary.spec.ts/multiple-people-ban-messages-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/event-tile-reply-chains-irc-modern-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/event-tile-reply-chains-irc-modern-linux.png index 031f5aa419..49580a63d6 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/event-tile-reply-chains-irc-modern-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/event-tile-reply-chains-irc-modern-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png index 766a6fffa1..0ebb9196ed 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png index fa2d11b810..2896031331 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png index a2dc6d35bb..84f32debc2 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png differ diff --git a/apps/web/res/css/views/elements/_GenericEventListSummary.pcss b/apps/web/res/css/views/elements/_GenericEventListSummary.pcss index f4aa0e6f0f..68cc250a71 100644 --- a/apps/web/res/css/views/elements/_GenericEventListSummary.pcss +++ b/apps/web/res/css/views/elements/_GenericEventListSummary.pcss @@ -118,7 +118,7 @@ Please see LICENSE files in the repository root for full details. .mx_TextualEvent.mx_GenericEventListSummary_summary { font: var(--cpd-font-body-sm-regular); - display: inline-flex; + display: inline; color: $secondary-content; } diff --git a/apps/web/src/TextForEvent.tsx b/apps/web/src/TextForEvent.tsx index 1f326663b7..b9ea96dcd8 100644 --- a/apps/web/src/TextForEvent.tsx +++ b/apps/web/src/TextForEvent.tsx @@ -39,6 +39,7 @@ import { highlightEvent, isLocationEvent } from "./utils/EventUtils"; import { getSenderName } from "./utils/event/getSenderName"; import PosthogTrackers from "./PosthogTrackers.ts"; import { ElementCallEventType } from "./call-types.ts"; +import Spoiler from "./components/views/elements/Spoiler.tsx"; function getRoomMemberDisplayname(client: MatrixClient, event: MatrixEvent, userId = event.getSender()): string { const roomId = event.getRoomId(); @@ -107,7 +108,7 @@ function textForMemberEvent( client: MatrixClient, allowJSX: boolean, showHiddenEvents?: boolean, -): (() => string) | null { +): (() => Renderable) | null { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender?.name || getRoomMemberDisplayname(client, ev); const targetName = ev.target?.name || getRoomMemberDisplayname(client, ev, ev.getStateKey()); @@ -133,10 +134,26 @@ function textForMemberEvent( } } case KnownMembership.Ban: - return () => - reason - ? _t("timeline|m.room.member|ban_reason", { senderName, targetName, reason }) - : _t("timeline|m.room.member|ban", { senderName, targetName }); + if (allowJSX) { + return reason + ? () => + _t( + "timeline|m.room.member|ban_reason_spoiler", + { senderName, reason }, + { user: () => {targetName} }, + ) + : () => + _t( + "timeline|m.room.member|ban_spoiler", + { senderName }, + { user: () => {targetName} }, + ); + } + + return reason + ? () => _t("timeline|m.room.member|ban_reason", { senderName, reason }) + : () => _t("timeline|m.room.member|ban", { senderName }); + case KnownMembership.Join: if (prevContent && prevContent.membership === KnownMembership.Join) { const modDisplayname = getModification(prevContent.displayname, content.displayname); diff --git a/apps/web/src/components/views/elements/EventListSummary.tsx b/apps/web/src/components/views/elements/EventListSummary.tsx index 86a5ce5776..4400f19bd2 100644 --- a/apps/web/src/components/views/elements/EventListSummary.tsx +++ b/apps/web/src/components/views/elements/EventListSummary.tsx @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type ComponentProps, type ReactNode } from "react"; +import React, { type ReactElement, type ComponentProps, type ReactNode } from "react"; import { EventType, type MatrixEvent, MatrixEventEvent, type RoomMember } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { throttle } from "lodash"; @@ -25,6 +25,7 @@ import AccessibleButton from "./AccessibleButton"; import RoomContext from "../../../contexts/RoomContext"; import { arrayHasDiff } from "../../../utils/arrays.ts"; import { objectHasDiff } from "../../../utils/objects.ts"; +import Spoiler from "./Spoiler.tsx"; const onPinnedMessagesClick = (): void => { RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false); @@ -222,7 +223,15 @@ export default class EventListSummary extends React.Component { ): ReactNode { const summaries = orderedTransitionSequences.map((transitions) => { const userNames = eventAggregates[transitions]; - const nameList = this.renderNameList(userNames); + let spoileredUserNames: ReactElement[]; + + if (containsBanned(transitions)) { + spoileredUserNames = userNames.map((u) => {u}); + } else { + spoileredUserNames = userNames.map((u) => <>{u}); + } + + const nameList = this.renderNameList(spoileredUserNames); const splitTransitions = transitions.split(SEP) as TransitionType[]; @@ -234,7 +243,11 @@ export default class EventListSummary extends React.Component { const coalescedTransitions = EventListSummary.coalesceRepeatedTransitions(canonicalTransitions); const descs = coalescedTransitions.map((t) => { - return EventListSummary.getDescriptionForTransition(t.transitionType, userNames.length, t.repeats); + return EventListSummary.getDescriptionForTransition( + t.transitionType, + spoileredUserNames.length, + t.repeats, + ); }); const desc = formatList(descs); @@ -255,7 +268,7 @@ export default class EventListSummary extends React.Component { * more items in `users` than `this.props.summaryLength`, which is the number of names * included before "and [n] others". */ - private renderNameList(users: string[]): string { + private renderNameList(users: ReactElement[]): ReactElement { return formatList(users, this.props.summaryLength); } @@ -618,3 +631,11 @@ export default class EventListSummary extends React.Component { ); } } + +/** + * Returns true if the provided list of comma-separated list of transitions + * contains an item "banned". + */ +function containsBanned(transitions: string): boolean { + return transitions.startsWith(TransitionType.Banned) || transitions.includes(`,${TransitionType.Banned}`); +} diff --git a/apps/web/src/i18n/strings/en_EN.json b/apps/web/src/i18n/strings/en_EN.json index dbc3a45041..65fe70c55d 100644 --- a/apps/web/src/i18n/strings/en_EN.json +++ b/apps/web/src/i18n/strings/en_EN.json @@ -3488,8 +3488,10 @@ "m.room.member": { "accepted_3pid_invite": "%(targetName)s accepted the invitation for %(displayName)s", "accepted_invite": "%(targetName)s accepted an invitation", - "ban": "%(senderName)s banned %(targetName)s", - "ban_reason": "%(senderName)s banned %(targetName)s: %(reason)s", + "ban": "%(senderName)s banned a user", + "ban_reason": "%(senderName)s banned a user: %(reason)s", + "ban_reason_spoiler": "%(senderName)s banned : %(reason)s", + "ban_spoiler": "%(senderName)s banned ", "change_avatar": "%(senderName)s changed their profile picture", "change_name": "%(oldDisplayName)s changed their display name to %(displayName)s", "change_name_avatar": "%(oldDisplayName)s changed their display name and profile picture", diff --git a/apps/web/test/unit-tests/TextForEvent-test.tsx b/apps/web/test/unit-tests/TextForEvent-test.tsx index 076972e646..c6c23bffa2 100644 --- a/apps/web/test/unit-tests/TextForEvent-test.tsx +++ b/apps/web/test/unit-tests/TextForEvent-test.tsx @@ -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); + + expect(textForEvent(banEventWithReason(), mockClient, true)).toEqual( + + Admin banned Bad User: bad behaviour + , + ); + }); + + 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); + + 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); + + expect(textForEvent(banEvent(), mockClient, true)).toEqual( + + @admin:example.com banned @bad_name:bad_server.co + , + ); + }); + + 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); + + 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", + }); +} diff --git a/apps/web/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap b/apps/web/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap index af12993f99..2a319cec6b 100644 --- a/apps/web/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap +++ b/apps/web/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap @@ -120,7 +120,9 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = ` - @user:id made no changes 100 times + + @user:id made no changes 100 times + diff --git a/apps/web/test/unit-tests/components/views/elements/EventListSummary-test.tsx b/apps/web/test/unit-tests/components/views/elements/EventListSummary-test.tsx index 3254272d99..dec1e89484 100644 --- a/apps/web/test/unit-tests/components/views/elements/EventListSummary-test.tsx +++ b/apps/web/test/unit-tests/components/views/elements/EventListSummary-test.tsx @@ -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('user_1'); 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('user_1'); 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('user_0'); expect(summary).toMatchSnapshot(); }); diff --git a/apps/web/test/unit-tests/components/views/elements/__snapshots__/EventListSummary-test.tsx.snap b/apps/web/test/unit-tests/components/views/elements/__snapshots__/EventListSummary-test.tsx.snap index 88e137e0b1..6c30e0ce87 100644 --- a/apps/web/test/unit-tests/components/views/elements/__snapshots__/EventListSummary-test.tsx.snap +++ b/apps/web/test/unit-tests/components/views/elements/__snapshots__/EventListSummary-test.tsx.snap @@ -4,7 +4,20 @@ exports[`EventListSummary correctly identifies transitions 1`] = ` - user_1 was invited, was banned, joined, rejected their invitation, left, had their invitation withdrawn, was unbanned, was removed, left and was removed + + + + was invited, was banned, joined, rejected their invitation, left, had their invitation withdrawn, was unbanned, was removed, left and was removed + `; @@ -12,9 +25,26 @@ exports[`EventListSummary correctly orders sequences of transitions by the order - user_2 was unbanned and joined and left 2 times + + user_2 + + was unbanned and joined and left 2 times + , - user_1 was unbanned, joined and left 2 times and was banned + + + + was unbanned, joined and left 2 times and was banned + `; @@ -22,7 +52,16 @@ exports[`EventListSummary handles a summary length = 2, with 1 "other" 1`] = ` - user_1, user_2 and one other joined + + + user_1 + , + user_2 + and one other + + + joined + `; @@ -30,7 +69,16 @@ exports[`EventListSummary handles a summary length = 2, with many "others" 1`] = - user_0, user_1 and 18 others joined + + + user_0 + , + user_1 + and 18 others + + + joined + `; @@ -38,7 +86,13 @@ exports[`EventListSummary handles a summary length = 2, with no "others" 1`] = ` - user_1 and user_2 joined 2 times + + user_1 + and + user_2 + + joined 2 times + `; @@ -46,7 +100,11 @@ exports[`EventListSummary handles invitation plurals correctly when there are mu - user_1 rejected their invitation 2 times + + user_1 + + rejected their invitation 2 times + `; @@ -54,7 +112,14 @@ exports[`EventListSummary handles invitation plurals correctly when there are mu - user_1 and one other rejected their invitations and had their invitations withdrawn + + + user_1 + and one other + + + rejected their invitations and had their invitations withdrawn + `; @@ -62,7 +127,23 @@ exports[`EventListSummary handles many users following the same sequence of memb - user_0 and 19 others were unbanned, joined and left 2 times and were banned + + + + and 19 others + + + were unbanned, joined and left 2 times and were banned + `; @@ -70,7 +151,23 @@ exports[`EventListSummary handles multiple users following the same sequence of - user_1 and one other were unbanned, joined and left 2 times and were banned + + + + and one other + + + were unbanned, joined and left 2 times and were banned + `; @@ -78,7 +175,11 @@ exports[`EventListSummary renders collapsed events if events.length = props.thre - user_1 joined and left and joined + + user_1 + + joined and left and joined + `; @@ -111,9 +212,17 @@ exports[`EventListSummary should not blindly group 3pid invites and treat them a - n...@d... was invited 2 times + + n...@d... + + was invited 2 times + , - d...@w... was invited + + d...@w... + + was invited + `; @@ -121,7 +230,11 @@ exports[`EventListSummary truncates long join,leave repetitions 1`] = ` - user_1 joined and left 7 times + + user_1 + + joined and left 7 times + `; @@ -129,7 +242,11 @@ exports[`EventListSummary truncates long join,leave repetitions between other ev - user_1 was unbanned, joined and left 7 times and was invited + + user_1 + + was unbanned, joined and left 7 times and was invited + `; @@ -137,6 +254,19 @@ exports[`EventListSummary truncates multiple sequences of repetitions with other - user_1 was unbanned, joined and left 2 times, was banned, joined and left 3 times and was invited + + + + was unbanned, joined and left 2 times, was banned, joined and left 3 times and was invited + `;