/*
Copyright 2024 New Vector Ltd.
Copyright 2023 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, screen } from "@testing-library/react";
import fetchMock from "fetch-mock-jest";
import ServerPickerDialog from "../../../../src/components/views/dialogs/ServerPickerDialog";
import SdkConfig from "../../../../src/SdkConfig";
import { flushPromises } from "../../../test-utils";
import { ValidatedServerConfig } from "../../../../src/utils/ValidatedServerConfig";
/** The matrix versions our mock server claims to support */
const SERVER_SUPPORTED_MATRIX_VERSIONS = ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9"];
describe("", () => {
const defaultServerConfig = {
hsUrl: "https://matrix.org",
hsName: "matrix.org",
hsNameIsDifferent: true,
isUrl: "https://is.org",
isDefault: true,
isNameResolvable: true,
warning: "",
};
const wkHsUrl = "https://hsbaseurlfrom.wk";
const wkIsUrl = "https://isbaseurlfrom.wk";
const validWellKnown = {
"m.homeserver": {
base_url: wkHsUrl,
},
"m.identity_server": {
base_url: wkIsUrl,
},
};
const defaultProps = {
serverConfig: defaultServerConfig,
onFinished: jest.fn(),
};
const getComponent = (
props: Partial<{
onFinished: any;
serverConfig: ValidatedServerConfig;
}> = {},
) => render();
beforeEach(() => {
SdkConfig.add({
validated_server_config: defaultServerConfig,
});
fetchMock.resetHistory();
fetchMock.catch({
status: 404,
body: '{"errcode": "M_UNRECOGNIZED", "error": "Unrecognized request"}',
headers: { "content-type": "application/json" },
});
});
it("should render dialog", () => {
const { container } = getComponent();
expect(container).toMatchSnapshot();
});
// checkbox and text input have the same aria-label
const getOtherHomeserverCheckBox = () =>
screen.getAllByLabelText("Other homeserver").find((node) => node.getAttribute("type") === "radio")!;
const getOtherHomeserverInput = () =>
screen.getAllByLabelText("Other homeserver").find((node) => node.getAttribute("type") === "text")!;
describe("when default server config is selected", () => {
it("should select other homeserver field on open", () => {
getComponent();
expect(getOtherHomeserverCheckBox()).toBeChecked();
// empty field
expect(getOtherHomeserverInput()).toHaveDisplayValue("");
});
it("should display an error when trying to continue with an empty homeserver field", async () => {
const onFinished = jest.fn();
const { container } = getComponent({ onFinished });
fireEvent.click(screen.getByText("Continue"));
await flushPromises();
// error on field
expect(container.querySelector(".mx_ServerPickerDialog_otherHomeserver.mx_Field_invalid")).toBeTruthy();
// didn't close dialog
expect(onFinished).not.toHaveBeenCalled();
});
it("should close when selecting default homeserver and clicking continue", async () => {
const onFinished = jest.fn();
getComponent({ onFinished });
fireEvent.click(screen.getByTestId("defaultHomeserver"));
expect(screen.getByTestId("defaultHomeserver")).toBeChecked();
fireEvent.click(screen.getByText("Continue"));
// closed dialog with default server
expect(onFinished).toHaveBeenCalledWith(defaultServerConfig);
});
it("should allow user to revert from a custom server to the default", async () => {
fetchMock.get(`https://custom.org/_matrix/client/versions`, {
unstable_features: {},
versions: SERVER_SUPPORTED_MATRIX_VERSIONS,
});
const onFinished = jest.fn();
const serverConfig = {
hsUrl: "https://custom.org",
hsName: "custom.org",
hsNameIsDifferent: true,
isUrl: "https://is.org",
isDefault: false,
isNameResolvable: true,
warning: "",
};
getComponent({ onFinished, serverConfig });
fireEvent.click(screen.getByTestId("defaultHomeserver"));
expect(screen.getByTestId("defaultHomeserver")).toBeChecked();
fireEvent.click(screen.getByText("Continue"));
await flushPromises();
// closed dialog with default server and nothing else
expect(onFinished).toHaveBeenCalledWith(defaultServerConfig);
expect(onFinished).toHaveBeenCalledTimes(1);
});
it("should submit successfully with a valid custom homeserver", async () => {
const homeserver = "https://myhomeserver.site";
fetchMock.get(`${homeserver}/_matrix/client/versions`, {
unstable_features: {},
versions: SERVER_SUPPORTED_MATRIX_VERSIONS,
});
const onFinished = jest.fn();
getComponent({ onFinished });
fireEvent.change(getOtherHomeserverInput(), { target: { value: homeserver } });
expect(getOtherHomeserverInput()).toHaveDisplayValue(homeserver);
fireEvent.click(screen.getByText("Continue"));
// validation on submit is async
await flushPromises();
// closed dialog with validated custom server
expect(onFinished).toHaveBeenCalledWith({
hsName: "myhomeserver.site",
hsUrl: homeserver,
hsNameIsDifferent: false,
warning: null,
isDefault: false,
isNameResolvable: false,
isUrl: defaultServerConfig.isUrl,
});
});
describe("validates custom homeserver", () => {
it("should lookup .well-known for homeserver without protocol", async () => {
const homeserver = "myhomeserver1.site";
const wellKnownUrl = `https://${homeserver}/.well-known/matrix/client`;
fetchMock.get(wellKnownUrl, {});
getComponent();
fireEvent.change(getOtherHomeserverInput(), { target: { value: homeserver } });
expect(getOtherHomeserverInput()).toHaveDisplayValue(homeserver);
// trigger validation
fireEvent.blur(getOtherHomeserverInput());
// validation on submit is async
await flushPromises();
expect(fetchMock).toHaveFetched(wellKnownUrl);
});
it("should submit using validated config from a valid .well-known", async () => {
const homeserver = "myhomeserver2.site";
const wellKnownUrl = `https://${homeserver}/.well-known/matrix/client`;
// urls from homeserver well-known
const versionsUrl = `${wkHsUrl}/_matrix/client/versions`;
const isWellKnownUrl = `${wkIsUrl}/_matrix/identity/v2`;
fetchMock.getOnce(wellKnownUrl, validWellKnown);
fetchMock.getOnce(versionsUrl, {
versions: SERVER_SUPPORTED_MATRIX_VERSIONS,
});
fetchMock.getOnce(isWellKnownUrl, {});
const onFinished = jest.fn();
getComponent({ onFinished });
fireEvent.change(getOtherHomeserverInput(), { target: { value: homeserver } });
fireEvent.click(screen.getByText("Continue"));
// validation on submit is async
await flushPromises();
expect(fetchMock).toHaveFetched(wellKnownUrl);
// fetched using urls from .well-known
expect(fetchMock).toHaveFetched(versionsUrl);
expect(fetchMock).toHaveFetched(isWellKnownUrl);
expect(onFinished).toHaveBeenCalledWith({
hsName: homeserver,
hsUrl: wkHsUrl,
hsNameIsDifferent: true,
warning: null,
isDefault: false,
isNameResolvable: true,
isUrl: wkIsUrl,
});
await flushPromises();
});
it("should fall back to static config when well-known lookup fails", async () => {
const homeserver = "myhomeserver3.site";
// this server returns 404 for well-known
const wellKnownUrl = `https://${homeserver}/.well-known/matrix/client`;
fetchMock.get(wellKnownUrl, { status: 404 });
// but is otherwise live (happy versions response)
fetchMock.get(`https://${homeserver}/_matrix/client/versions`, {
versions: SERVER_SUPPORTED_MATRIX_VERSIONS,
});
const onFinished = jest.fn();
getComponent({ onFinished });
fireEvent.change(getOtherHomeserverInput(), { target: { value: homeserver } });
fireEvent.click(screen.getByText("Continue"));
// validation on submit is async
await flushPromises();
expect(fetchMock).toHaveFetched(wellKnownUrl);
expect(fetchMock).toHaveFetched(`https://${homeserver}/_matrix/client/versions`);
expect(onFinished).toHaveBeenCalledWith({
hsName: homeserver,
hsUrl: "https://" + homeserver,
hsNameIsDifferent: false,
warning: null,
isDefault: false,
isNameResolvable: false,
isUrl: defaultServerConfig.isUrl,
});
});
});
});
});