/* Copyright 2024 New Vector Ltd. Copyright 2022 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React from "react"; import { fireEvent, render, RenderResult, screen, waitFor } from "jest-matrix-react"; import userEvent from "@testing-library/user-event"; import PreferencesUserSettingsTab from "../../../../../../../src/components/views/settings/tabs/user/PreferencesUserSettingsTab"; import { MatrixClientPeg } from "../../../../../../../src/MatrixClientPeg"; import { mockPlatformPeg, stubClient } from "../../../../../../test-utils"; import SettingsStore from "../../../../../../../src/settings/SettingsStore"; import { SettingLevel } from "../../../../../../../src/settings/SettingLevel"; import MatrixClientBackedController from "../../../../../../../src/settings/controllers/MatrixClientBackedController"; import PlatformPeg from "../../../../../../../src/PlatformPeg"; describe("PreferencesUserSettingsTab", () => { beforeEach(() => { mockPlatformPeg(); }); const renderTab = (): RenderResult => { return render( {}} />); }; it("should render", () => { const { asFragment } = renderTab(); expect(asFragment()).toMatchSnapshot(); }); it("should reload when changing language", async () => { const reloadStub = jest.fn(); PlatformPeg.get()!.reload = reloadStub; renderTab(); const languageDropdown = await screen.findByRole("button", { name: "Language Dropdown" }); expect(languageDropdown).toBeInTheDocument(); await userEvent.click(languageDropdown); const germanOption = await screen.findByText("Deutsch"); await userEvent.click(germanOption); expect(reloadStub).toHaveBeenCalled(); }); it("should search and select a user timezone", async () => { renderTab(); expect(await screen.findByText(/Browser default/)).toBeInTheDocument(); const timezoneDropdown = await screen.findByRole("button", { name: "Set timezone" }); await userEvent.click(timezoneDropdown); // Without filtering `expect(screen.queryByRole("option" ...` take over 1s. await fireEvent.change(screen.getByRole("combobox", { name: "Set timezone" }), { target: { value: "Africa/Abidjan" }, }); expect(screen.queryByRole("option", { name: "Africa/Abidjan" })).toBeInTheDocument(); expect(screen.queryByRole("option", { name: "Europe/Paris" })).not.toBeInTheDocument(); await fireEvent.change(screen.getByRole("combobox", { name: "Set timezone" }), { target: { value: "Europe/Paris" }, }); expect(screen.queryByRole("option", { name: "Africa/Abidjan" })).not.toBeInTheDocument(); const option = await screen.getByRole("option", { name: "Europe/Paris" }); await userEvent.click(option); expect(await screen.findByText("Europe/Paris")).toBeInTheDocument(); }); it("should not show spell check setting if unsupported", async () => { PlatformPeg.get()!.supportsSpellCheckSettings = jest.fn().mockReturnValue(false); renderTab(); expect(screen.queryByRole("switch", { name: "Allow spell check" })).not.toBeInTheDocument(); }); it("should enable spell check", async () => { const spellCheckEnableFn = jest.fn(); PlatformPeg.get()!.supportsSpellCheckSettings = jest.fn().mockReturnValue(true); PlatformPeg.get()!.getSpellCheckEnabled = jest.fn().mockReturnValue(false); PlatformPeg.get()!.setSpellCheckEnabled = spellCheckEnableFn; renderTab(); const toggle = await screen.findByRole("switch", { name: "Allow spell check" }); expect(toggle).toHaveAttribute("aria-checked", "false"); await userEvent.click(toggle); expect(spellCheckEnableFn).toHaveBeenCalledWith(true); }); describe("send read receipts", () => { beforeEach(() => { stubClient(); jest.spyOn(SettingsStore, "setValue"); jest.spyOn(window, "matchMedia").mockReturnValue({ matches: false } as MediaQueryList); }); afterEach(() => { jest.resetAllMocks(); }); const getToggle = () => renderTab().getByRole("switch", { name: "Send read receipts" }); const mockIsVersionSupported = (val: boolean) => { const client = MatrixClientPeg.safeGet(); jest.spyOn(client, "doesServerSupportUnstableFeature").mockResolvedValue(false); jest.spyOn(client, "isVersionSupported").mockImplementation(async (version: string) => { if (version === "v1.4") return val; return false; }); MatrixClientBackedController.matrixClient = client; }; const mockGetValue = (val: boolean) => { const copyOfGetValueAt = SettingsStore.getValueAt; SettingsStore.getValueAt = ( level: SettingLevel, name: string, roomId?: string, isExplicit?: boolean, ): T => { if (name === "sendReadReceipts") return val as T; return copyOfGetValueAt(level, name, roomId, isExplicit); }; }; const expectSetValueToHaveBeenCalled = ( name: string, roomId: string | null, level: SettingLevel, value: boolean, ) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value); describe("with server support", () => { beforeEach(() => { mockIsVersionSupported(true); }); it("can be enabled", async () => { mockGetValue(false); const toggle = getToggle(); await waitFor(() => expect(toggle).toHaveAttribute("aria-disabled", "false")); fireEvent.click(toggle); expectSetValueToHaveBeenCalled("sendReadReceipts", null, SettingLevel.ACCOUNT, true); }); it("can be disabled", async () => { mockGetValue(true); const toggle = getToggle(); await waitFor(() => expect(toggle).toHaveAttribute("aria-disabled", "false")); fireEvent.click(toggle); expectSetValueToHaveBeenCalled("sendReadReceipts", null, SettingLevel.ACCOUNT, false); }); }); describe("without server support", () => { beforeEach(() => { mockIsVersionSupported(false); }); it("is forcibly enabled", async () => { const toggle = getToggle(); await waitFor(() => { expect(toggle).toHaveAttribute("aria-checked", "true"); expect(toggle).toHaveAttribute("aria-disabled", "true"); }); }); it("cannot be disabled", async () => { mockGetValue(true); const toggle = getToggle(); await waitFor(() => expect(toggle).toHaveAttribute("aria-disabled", "true")); fireEvent.click(toggle); expect(SettingsStore.setValue).not.toHaveBeenCalled(); }); }); }); });