From 275009f5148fc8df27a2e0b64f4afe2f0f5c9be2 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 31 May 2023 13:57:30 +1200 Subject: [PATCH 1/6] add aria-label to default homeserver checkbox --- src/components/views/dialogs/ServerPickerDialog.tsx | 1 + src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx index 4b6b17c01da..bcc3b737854 100644 --- a/src/components/views/dialogs/ServerPickerDialog.tsx +++ b/src/components/views/dialogs/ServerPickerDialog.tsx @@ -202,6 +202,7 @@ export default class ServerPickerDialog extends React.PureComponent {defaultServerName} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f4e329696e0..2cfc16958d9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3020,6 +3020,7 @@ "Matrix.org is the biggest public homeserver in the world, so it's a good place for many.": "Matrix.org is the biggest public homeserver in the world, so it's a good place for many.", "Sign into your homeserver": "Sign into your homeserver", "We call the places where you can host your account 'homeservers'.": "We call the places where you can host your account 'homeservers'.", + "Default homeserver": "Default homeserver", "Other homeserver": "Other homeserver", "Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.", "About homeservers": "About homeservers", From 79098c610836965720fbca198e34f9ddace6ffa7 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 31 May 2023 14:01:53 +1200 Subject: [PATCH 2/6] test ServerPickerDialog --- .../views/dialogs/ServerPickerDialog-test.tsx | 241 ++++++++++++++++++ .../ServerPickerDialog-test.tsx.snap | 142 +++++++++++ 2 files changed, 383 insertions(+) create mode 100644 test/components/views/dialogs/ServerPickerDialog-test.tsx create mode 100644 test/components/views/dialogs/__snapshots__/ServerPickerDialog-test.tsx.snap diff --git a/test/components/views/dialogs/ServerPickerDialog-test.tsx b/test/components/views/dialogs/ServerPickerDialog-test.tsx new file mode 100644 index 00000000000..0925d342de7 --- /dev/null +++ b/test/components/views/dialogs/ServerPickerDialog-test.tsx @@ -0,0 +1,241 @@ +/* +Copyright 2023 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 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 { ComponentProps } from "../../../../src/Modal"; +import { flushPromises } from "../../../test-utils"; + +// Fake random strings to give a predictable snapshot for IDs +jest.mock("matrix-js-sdk/src/randomstring", () => ({ + randomString: () => "abdefghi", +})); + +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> = {}) => + render(); + + beforeEach(() => { + SdkConfig.add({ + validated_server_config: defaultServerConfig, + }); + + fetchMock.resetHistory(); + + // stub error log, as lots of errors expected + // and make test results hard to see + // jest.spyOn(logger, 'error').mockClear().mockImplementation(() => {}); + }); + + 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.getByLabelText("Default homeserver")); + expect(screen.getByLabelText("Default homeserver")).toBeChecked(); + + fireEvent.click(screen.getByText("Continue")); + + // serverpicker still validates the 'other homeserver' field on submit + // when default is chosen + // so this throws a lot of errors into the console + // and is asynchronous while waiting for validation + await flushPromises(); + + // closed dialog with default server + expect(onFinished).toHaveBeenCalledWith(defaultServerConfig); + }); + + it("should submit successfully with a valid custom homeserver", async () => { + const homeserver = "https://myhomeserver.site"; + fetchMock.get(`${homeserver}/_matrix/client/versions`, { + unstable_features: {}, + 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: [], + }); + 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: ["1"] }); + 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, + }); + }); + }); + }); +}); diff --git a/test/components/views/dialogs/__snapshots__/ServerPickerDialog-test.tsx.snap b/test/components/views/dialogs/__snapshots__/ServerPickerDialog-test.tsx.snap new file mode 100644 index 00000000000..3c133bc4f75 --- /dev/null +++ b/test/components/views/dialogs/__snapshots__/ServerPickerDialog-test.tsx.snap @@ -0,0 +1,142 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render dialog 1`] = ` +
+
+