Remove all usages of UNSAFE_* React methods (#9583)
This commit is contained in:
parent
38dbe8ed33
commit
590b845f3f
33 changed files with 585 additions and 413 deletions
102
test/components/structures/auth/ForgotPassword-test.tsx
Normal file
102
test/components/structures/auth/ForgotPassword-test.tsx
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright 2022 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, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { createClient, MatrixClient } from 'matrix-js-sdk/src/matrix';
|
||||
import { mocked } from 'jest-mock';
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import SdkConfig, { DEFAULTS } from '../../../../src/SdkConfig';
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
import ForgotPassword from "../../../../src/components/structures/auth/ForgotPassword";
|
||||
import PasswordReset from "../../../../src/PasswordReset";
|
||||
|
||||
jest.mock('matrix-js-sdk/src/matrix');
|
||||
jest.mock("../../../../src/PasswordReset", () => (jest.fn().mockReturnValue({
|
||||
resetPassword: jest.fn().mockReturnValue(new Promise(() => {})),
|
||||
})));
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('<ForgotPassword/>', () => {
|
||||
const mockClient = mocked({
|
||||
doesServerSupportLogoutDevices: jest.fn().mockResolvedValue(true),
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
beforeEach(function() {
|
||||
SdkConfig.put({
|
||||
...DEFAULTS,
|
||||
disable_custom_urls: true,
|
||||
});
|
||||
mocked(createClient).mockImplementation(opts => {
|
||||
mockClient.idBaseUrl = opts.idBaseUrl;
|
||||
mockClient.baseUrl = opts.baseUrl;
|
||||
return mockClient;
|
||||
});
|
||||
fetchMock.get("https://matrix.org/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
mockPlatformPeg({
|
||||
startSingleSignOn: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
fetchMock.restore();
|
||||
SdkConfig.unset(); // we touch the config, so clean up
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
defaultDeviceDisplayName: 'test-device-display-name',
|
||||
onServerConfigChange: jest.fn(),
|
||||
onLoginClick: jest.fn(),
|
||||
onComplete: jest.fn(),
|
||||
};
|
||||
|
||||
function getRawComponent(hsUrl = "https://matrix.org", isUrl = "https://vector.im") {
|
||||
return <ForgotPassword
|
||||
{...defaultProps}
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
/>;
|
||||
}
|
||||
|
||||
it("should handle serverConfig updates correctly", async () => {
|
||||
const { container, rerender } = render(getRawComponent());
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
fetchMock.get("https://server2/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
fetchMock.get("https://vector.im/_matrix/identity/api/v1", {});
|
||||
rerender(getRawComponent("https://server2"));
|
||||
|
||||
const email = "email@addy.com";
|
||||
const pass = "thisIsAT0tallySecurePassword";
|
||||
|
||||
fireEvent.change(container.querySelector('[label=Email]'), { target: { value: email } });
|
||||
fireEvent.change(container.querySelector('[label="New Password"]'), { target: { value: pass } });
|
||||
fireEvent.change(container.querySelector('[label=Confirm]'), { target: { value: pass } });
|
||||
fireEvent.change(container.querySelector('[type=checkbox]')); // this allows us to bypass the modal
|
||||
fireEvent.submit(container.querySelector("form"));
|
||||
|
||||
await waitFor(() => {
|
||||
return expect(PasswordReset).toHaveBeenCalledWith("https://server2", expect.anything());
|
||||
}, { timeout: 5000 });
|
||||
});
|
||||
});
|
|
@ -14,25 +14,24 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactTestUtils from 'react-dom/test-utils';
|
||||
import { mocked } from 'jest-mock';
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { mocked, MockedObject } from 'jest-mock';
|
||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import SdkConfig from '../../../../src/SdkConfig';
|
||||
import { mkServerConfig } from "../../../test-utils";
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
import Login from "../../../../src/components/structures/auth/Login";
|
||||
import PasswordLogin from "../../../../src/components/views/auth/PasswordLogin";
|
||||
import BasePlatform from "../../../../src/BasePlatform";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/matrix");
|
||||
|
||||
const flushPromises = async () => await new Promise(process.nextTick);
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
describe('Login', function() {
|
||||
let parentDiv;
|
||||
let platform: MockedObject<BasePlatform>;
|
||||
|
||||
const mockClient = mocked({
|
||||
login: jest.fn().mockResolvedValue({}),
|
||||
loginFlows: jest.fn(),
|
||||
|
@ -45,25 +44,37 @@ describe('Login', function() {
|
|||
});
|
||||
mockClient.login.mockClear().mockResolvedValue({});
|
||||
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
|
||||
mocked(createClient).mockReturnValue(mockClient);
|
||||
|
||||
parentDiv = document.createElement('div');
|
||||
document.body.appendChild(parentDiv);
|
||||
mocked(createClient).mockImplementation(opts => {
|
||||
mockClient.idBaseUrl = opts.idBaseUrl;
|
||||
mockClient.baseUrl = opts.baseUrl;
|
||||
return mockClient;
|
||||
});
|
||||
fetchMock.get("https://matrix.org/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
platform = mockPlatformPeg({
|
||||
startSingleSignOn: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
fetchMock.restore();
|
||||
SdkConfig.unset(); // we touch the config, so clean up
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
function render() {
|
||||
return ReactDOM.render(<Login
|
||||
serverConfig={mkServerConfig("https://matrix.org", "https://vector.im")}
|
||||
function getRawComponent(hsUrl = "https://matrix.org", isUrl = "https://vector.im") {
|
||||
return <Login
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
onLoggedIn={() => { }}
|
||||
onRegisterClick={() => { }}
|
||||
onServerConfigChange={() => { }}
|
||||
/>, parentDiv) as unknown as Component<any, any, any>;
|
||||
/>;
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string) {
|
||||
return render(getRawComponent(hsUrl, isUrl));
|
||||
}
|
||||
|
||||
it('should show form with change server link', async () => {
|
||||
|
@ -71,54 +82,41 @@ describe('Login', function() {
|
|||
brand: "test-brand",
|
||||
disable_custom_urls: false,
|
||||
});
|
||||
const root = render();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
await flushPromises();
|
||||
expect(container.querySelector("form")).toBeTruthy();
|
||||
|
||||
const form = ReactTestUtils.findRenderedComponentWithType(
|
||||
root,
|
||||
PasswordLogin,
|
||||
);
|
||||
expect(form).toBeTruthy();
|
||||
|
||||
const changeServerLink = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_ServerPicker_change');
|
||||
expect(changeServerLink).toBeTruthy();
|
||||
expect(container.querySelector(".mx_ServerPicker_change")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show form without change server link when custom URLs disabled', async () => {
|
||||
const root = render();
|
||||
await flushPromises();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
const form = ReactTestUtils.findRenderedComponentWithType(
|
||||
root,
|
||||
PasswordLogin,
|
||||
);
|
||||
expect(form).toBeTruthy();
|
||||
|
||||
const changeServerLinks = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, 'mx_ServerPicker_change');
|
||||
expect(changeServerLinks).toHaveLength(0);
|
||||
expect(container.querySelector("form")).toBeTruthy();
|
||||
expect(container.querySelectorAll(".mx_ServerPicker_change")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should show SSO button if that flow is available", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({ flows: [{ type: "m.login.sso" }] });
|
||||
|
||||
const root = render();
|
||||
await flushPromises();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
const ssoButton = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_SSOButton");
|
||||
const ssoButton = container.querySelector(".mx_SSOButton");
|
||||
expect(ssoButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should show both SSO button and username+password if both are available", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({ flows: [{ type: "m.login.password" }, { type: "m.login.sso" }] });
|
||||
|
||||
const root = render();
|
||||
await flushPromises();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
const form = ReactTestUtils.findRenderedComponentWithType(root, PasswordLogin);
|
||||
expect(form).toBeTruthy();
|
||||
expect(container.querySelector("form")).toBeTruthy();
|
||||
|
||||
const ssoButton = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_SSOButton");
|
||||
const ssoButton = container.querySelector(".mx_SSOButton");
|
||||
expect(ssoButton).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -139,11 +137,10 @@ describe('Login', function() {
|
|||
}],
|
||||
});
|
||||
|
||||
const root = render();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
await flushPromises();
|
||||
|
||||
const ssoButtons = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, "mx_SSOButton");
|
||||
const ssoButtons = container.querySelectorAll(".mx_SSOButton");
|
||||
expect(ssoButtons.length).toBe(3);
|
||||
});
|
||||
|
||||
|
@ -154,11 +151,33 @@ describe('Login', function() {
|
|||
}],
|
||||
});
|
||||
|
||||
const root = render();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
await flushPromises();
|
||||
|
||||
const ssoButtons = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, "mx_SSOButton");
|
||||
const ssoButtons = container.querySelectorAll(".mx_SSOButton");
|
||||
expect(ssoButtons.length).toBe(1);
|
||||
});
|
||||
|
||||
it("should handle serverConfig updates correctly", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
}],
|
||||
});
|
||||
|
||||
const { container, rerender } = render(getRawComponent());
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
fireEvent.click(container.querySelector(".mx_SSOButton"));
|
||||
expect(platform.startSingleSignOn.mock.calls[0][0].baseUrl).toBe("https://matrix.org");
|
||||
|
||||
fetchMock.get("https://server2/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
rerender(getRawComponent("https://server2"));
|
||||
|
||||
fireEvent.click(container.querySelector(".mx_SSOButton"));
|
||||
expect(platform.startSingleSignOn.mock.calls[1][0].baseUrl).toBe("https://server2");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,93 +16,115 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactTestUtils from 'react-dom/test-utils';
|
||||
import { createClient } from 'matrix-js-sdk/src/matrix';
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { createClient, MatrixClient } from 'matrix-js-sdk/src/matrix';
|
||||
import { MatrixError } from 'matrix-js-sdk/src/http-api/errors';
|
||||
import { mocked } from 'jest-mock';
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import SdkConfig, { DEFAULTS } from '../../../../src/SdkConfig';
|
||||
import { createTestClient, mkServerConfig } from "../../../test-utils";
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../test-utils";
|
||||
import Registration from "../../../../src/components/structures/auth/Registration";
|
||||
import RegistrationForm from "../../../../src/components/views/auth/RegistrationForm";
|
||||
|
||||
jest.mock('matrix-js-sdk/src/matrix');
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe('Registration', function() {
|
||||
let parentDiv;
|
||||
const registerRequest = jest.fn();
|
||||
const mockClient = mocked({
|
||||
registerRequest,
|
||||
loginFlows: jest.fn(),
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
beforeEach(function() {
|
||||
SdkConfig.put({
|
||||
...DEFAULTS,
|
||||
disable_custom_urls: true,
|
||||
});
|
||||
parentDiv = document.createElement('div');
|
||||
document.body.appendChild(parentDiv);
|
||||
mocked(createClient).mockImplementation(() => createTestClient());
|
||||
mockClient.registerRequest.mockRejectedValueOnce(new MatrixError({
|
||||
flows: [{ stages: [] }],
|
||||
}, 401));
|
||||
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
|
||||
mocked(createClient).mockImplementation(opts => {
|
||||
mockClient.idBaseUrl = opts.idBaseUrl;
|
||||
mockClient.baseUrl = opts.baseUrl;
|
||||
return mockClient;
|
||||
});
|
||||
fetchMock.get("https://matrix.org/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
mockPlatformPeg({
|
||||
startSingleSignOn: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
fetchMock.restore();
|
||||
SdkConfig.unset(); // we touch the config, so clean up
|
||||
unmockPlatformPeg();
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
defaultDeviceDisplayName: 'test-device-display-name',
|
||||
serverConfig: mkServerConfig("https://matrix.org", "https://vector.im"),
|
||||
makeRegistrationUrl: jest.fn(),
|
||||
onLoggedIn: jest.fn(),
|
||||
onLoginClick: jest.fn(),
|
||||
onServerConfigChange: jest.fn(),
|
||||
};
|
||||
function render() {
|
||||
return ReactDOM.render<typeof Registration>(<Registration
|
||||
|
||||
function getRawComponent(hsUrl = "https://matrix.org", isUrl = "https://vector.im") {
|
||||
return <Registration
|
||||
{...defaultProps}
|
||||
/>, parentDiv) as React.Component<typeof Registration>;
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl)}
|
||||
/>;
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string) {
|
||||
return render(getRawComponent(hsUrl, isUrl));
|
||||
}
|
||||
|
||||
it('should show server picker', async function() {
|
||||
const root = render();
|
||||
const selector = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_ServerPicker");
|
||||
expect(selector).toBeTruthy();
|
||||
const { container } = getComponent();
|
||||
expect(container.querySelector(".mx_ServerPicker")).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show form when custom URLs disabled', async function() {
|
||||
const root = render();
|
||||
|
||||
// Set non-empty flows & matrixClient to get past the loading spinner
|
||||
root.setState({
|
||||
flows: [{
|
||||
stages: [],
|
||||
}],
|
||||
matrixClient: {},
|
||||
busy: false,
|
||||
});
|
||||
|
||||
const form = ReactTestUtils.findRenderedComponentWithType(
|
||||
root,
|
||||
RegistrationForm,
|
||||
);
|
||||
expect(form).toBeTruthy();
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
expect(container.querySelector("form")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should show SSO options if those are available", async () => {
|
||||
const root = render();
|
||||
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.sso" }] });
|
||||
const { container } = getComponent();
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
// Set non-empty flows & matrixClient to get past the loading spinner
|
||||
root.setState({
|
||||
flows: [{
|
||||
stages: [],
|
||||
}],
|
||||
ssoFlow: {
|
||||
type: "m.login.sso",
|
||||
},
|
||||
matrixClient: {},
|
||||
busy: false,
|
||||
});
|
||||
|
||||
const ssoButton = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_SSOButton");
|
||||
const ssoButton = container.querySelector(".mx_SSOButton");
|
||||
expect(ssoButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should handle serverConfig updates correctly", async () => {
|
||||
mockClient.loginFlows.mockResolvedValue({
|
||||
flows: [{
|
||||
"type": "m.login.sso",
|
||||
}],
|
||||
});
|
||||
|
||||
const { container, rerender } = render(getRawComponent());
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
fireEvent.click(container.querySelector(".mx_SSOButton"));
|
||||
expect(registerRequest.mock.instances[0].baseUrl).toBe("https://matrix.org");
|
||||
|
||||
fetchMock.get("https://server2/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: [],
|
||||
});
|
||||
rerender(getRawComponent("https://server2"));
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
|
||||
|
||||
fireEvent.click(container.querySelector(".mx_SSOButton"));
|
||||
expect(registerRequest.mock.instances[1].baseUrl).toBe("https://server2");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -189,30 +189,7 @@ exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
|
|||
>
|
||||
<BaseAvatar
|
||||
height={36}
|
||||
hideTitle={false}
|
||||
idName="@alice:server"
|
||||
member={
|
||||
RoomMember {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_isOutOfBand": false,
|
||||
"_maxListeners": undefined,
|
||||
"disambiguate": false,
|
||||
"events": {},
|
||||
"membership": undefined,
|
||||
"modified": 1647270879403,
|
||||
"name": "@alice:server",
|
||||
"powerLevel": 0,
|
||||
"powerLevelNorm": 0,
|
||||
"rawDisplayName": "@alice:server",
|
||||
"requestedProfileInfo": false,
|
||||
"roomId": "!room:server",
|
||||
"typing": false,
|
||||
"user": undefined,
|
||||
"userId": "@alice:server",
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
name="@alice:server"
|
||||
resizeMethod="crop"
|
||||
title="@alice:server"
|
||||
|
@ -220,29 +197,6 @@ exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
|
|||
>
|
||||
<span
|
||||
className="mx_BaseAvatar"
|
||||
hideTitle={false}
|
||||
member={
|
||||
RoomMember {
|
||||
"_events": {},
|
||||
"_eventsCount": 0,
|
||||
"_isOutOfBand": false,
|
||||
"_maxListeners": undefined,
|
||||
"disambiguate": false,
|
||||
"events": {},
|
||||
"membership": undefined,
|
||||
"modified": 1647270879403,
|
||||
"name": "@alice:server",
|
||||
"powerLevel": 0,
|
||||
"powerLevelNorm": 0,
|
||||
"rawDisplayName": "@alice:server",
|
||||
"requestedProfileInfo": false,
|
||||
"roomId": "!room:server",
|
||||
"typing": false,
|
||||
"user": undefined,
|
||||
"userId": "@alice:server",
|
||||
Symbol(kCapture): false,
|
||||
}
|
||||
}
|
||||
role="presentation"
|
||||
>
|
||||
<span
|
||||
|
|
|
@ -59,4 +59,20 @@ describe('<PowerSelector />', () => {
|
|||
await screen.findByDisplayValue(40);
|
||||
expect(fn).toHaveBeenCalledWith(40, "key");
|
||||
});
|
||||
|
||||
it("should reset when props get changed", async () => {
|
||||
const fn = jest.fn();
|
||||
const { rerender } = render(<PowerSelector value={50} maxValue={100} usersDefault={0} onChange={fn} />);
|
||||
|
||||
const select = screen.getByLabelText("Power level");
|
||||
fireEvent.change(select, { target: { value: "SELECT_VALUE_CUSTOM" } });
|
||||
|
||||
rerender(<PowerSelector value={51} maxValue={100} usersDefault={0} onChange={fn} />);
|
||||
await screen.findByDisplayValue(51);
|
||||
|
||||
rerender(<PowerSelector value={50} maxValue={100} usersDefault={0} onChange={fn} />);
|
||||
const option = await screen.findByText<HTMLOptionElement>("Moderator");
|
||||
expect(option.selected).toBeTruthy();
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,4 +14,4 @@ exports[`<TextualBody /> renders formatted m.text correctly pills do not appear
|
|||
</span>"
|
||||
`;
|
||||
|
||||
exports[`<TextualBody /> renders formatted m.text correctly pills get injected correctly into the DOM 1`] = `"<span class="mx_EventTile_body markdown-body" dir="auto">Hey <span><bdi><a class="mx_Pill mx_UserPill"><img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" style="width: 16px; height: 16px;" alt="" data-testid="avatar-img" member="[object Object]" aria-hidden="true"><span class="mx_Pill_linkText">Member</span></a></bdi></span></span>"`;
|
||||
exports[`<TextualBody /> renders formatted m.text correctly pills get injected correctly into the DOM 1`] = `"<span class="mx_EventTile_body markdown-body" dir="auto">Hey <span><bdi><a class="mx_Pill mx_UserPill"><img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" style="width: 16px; height: 16px;" alt="" data-testid="avatar-img" aria-hidden="true"><span class="mx_Pill_linkText">Member</span></a></bdi></span></span>"`;
|
||||
|
|
|
@ -38,7 +38,7 @@ import dis from "../../../../src/dispatcher/dispatcher";
|
|||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { SendMessageComposer } from "../../../../src/components/views/rooms/SendMessageComposer";
|
||||
import { E2EStatus } from "../../../../src/utils/ShieldUtils";
|
||||
import { addTextToComposer } from "../../../test-utils/composer";
|
||||
import { addTextToComposerEnzyme } from "../../../test-utils/composer";
|
||||
import UIStore, { UI_EVENTS } from "../../../../src/stores/UIStore";
|
||||
import { SendWysiwygComposer } from "../../../../src/components/views/rooms/wysiwyg_composer";
|
||||
|
||||
|
@ -176,7 +176,7 @@ describe("MessageComposer", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
wrapper = wrapAndRender({ room });
|
||||
addTextToComposer(wrapper, "Hello");
|
||||
addTextToComposerEnzyme(wrapper, "Hello");
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
|
|
|
@ -15,17 +15,13 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { MatrixClient, MsgType, RelationType } from "matrix-js-sdk/src/matrix";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import { mount } from 'enzyme';
|
||||
import { fireEvent, render, waitFor } from "@testing-library/react";
|
||||
import { MatrixClient, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import SendMessageComposer, {
|
||||
createMessageContent,
|
||||
isQuickReaction,
|
||||
SendMessageComposer as SendMessageComposerClass,
|
||||
} from "../../../../src/components/views/rooms/SendMessageComposer";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
|
||||
|
@ -46,15 +42,6 @@ jest.mock("../../../../src/utils/local-room", () => ({
|
|||
doMaybeLocalRoomAction: jest.fn(),
|
||||
}));
|
||||
|
||||
const WrapWithProviders: React.FC<{
|
||||
roomContext: IRoomState;
|
||||
client: MatrixClient;
|
||||
}> = ({ children, roomContext, client }) => <MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={roomContext}>
|
||||
{ children }
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>;
|
||||
|
||||
describe('<SendMessageComposer/>', () => {
|
||||
const defaultRoomContext: IRoomState = {
|
||||
roomLoading: true,
|
||||
|
@ -194,44 +181,48 @@ describe('<SendMessageComposer/>', () => {
|
|||
toggleStickerPickerOpen: jest.fn(),
|
||||
permalinkCreator: new RoomPermalinkCreator(mockRoom),
|
||||
};
|
||||
const getRawComponent = (props = {}, roomContext = defaultRoomContext, client = mockClient) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={roomContext}>
|
||||
<SendMessageComposer {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
const getComponent = (props = {}, roomContext = defaultRoomContext, client = mockClient) => {
|
||||
return mount(<SendMessageComposer {...defaultProps} {...props} />, {
|
||||
wrappingComponent: WrapWithProviders,
|
||||
wrappingComponentProps: { roomContext, client },
|
||||
});
|
||||
return render(getRawComponent(props, roomContext, client));
|
||||
};
|
||||
|
||||
it("renders text and placeholder correctly", () => {
|
||||
const wrapper = getComponent({ placeholder: "placeholder string" });
|
||||
const { container } = getComponent({ placeholder: "placeholder string" });
|
||||
|
||||
expect(wrapper.find('[aria-label="placeholder string"]')).toHaveLength(1);
|
||||
expect(container.querySelectorAll('[aria-label="placeholder string"]')).toHaveLength(1);
|
||||
|
||||
addTextToComposer(wrapper, "Test Text");
|
||||
addTextToComposer(container, "Test Text");
|
||||
|
||||
expect(wrapper.text()).toBe("Test Text");
|
||||
expect(container.textContent).toBe("Test Text");
|
||||
});
|
||||
|
||||
it("correctly persists state to and from localStorage", () => {
|
||||
const wrapper = getComponent({ replyToEvent: mockEvent });
|
||||
const props = { replyToEvent: mockEvent };
|
||||
const { container, unmount, rerender } = getComponent(props);
|
||||
|
||||
addTextToComposer(wrapper, "Test Text");
|
||||
addTextToComposer(container, "Test Text");
|
||||
|
||||
// @ts-ignore
|
||||
const key = wrapper.find(SendMessageComposerClass).instance().editorStateKey;
|
||||
const key = "mx_cider_state_myfakeroom";
|
||||
|
||||
expect(wrapper.text()).toBe("Test Text");
|
||||
expect(container.textContent).toBe("Test Text");
|
||||
expect(localStorage.getItem(key)).toBeNull();
|
||||
|
||||
// ensure the right state was persisted to localStorage
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({
|
||||
parts: [{ "type": "plain", "text": "Test Text" }],
|
||||
replyEventId: mockEvent.getId(),
|
||||
});
|
||||
|
||||
// ensure the correct model is re-loaded
|
||||
wrapper.mount();
|
||||
expect(wrapper.text()).toBe("Test Text");
|
||||
rerender(getRawComponent(props));
|
||||
expect(container.textContent).toBe("Test Text");
|
||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||
action: "reply_to_event",
|
||||
event: mockEvent,
|
||||
|
@ -239,21 +230,20 @@ describe('<SendMessageComposer/>', () => {
|
|||
});
|
||||
|
||||
// now try with localStorage wiped out
|
||||
wrapper.unmount();
|
||||
unmount();
|
||||
localStorage.removeItem(key);
|
||||
wrapper.mount();
|
||||
expect(wrapper.text()).toBe("");
|
||||
rerender(getRawComponent(props));
|
||||
expect(container.textContent).toBe("");
|
||||
});
|
||||
|
||||
it("persists state correctly without replyToEvent onbeforeunload", () => {
|
||||
const wrapper = getComponent();
|
||||
const { container } = getComponent();
|
||||
|
||||
addTextToComposer(wrapper, "Hello World");
|
||||
addTextToComposer(container, "Hello World");
|
||||
|
||||
// @ts-ignore
|
||||
const key = wrapper.find(SendMessageComposerClass).instance().editorStateKey;
|
||||
const key = "mx_cider_state_myfakeroom";
|
||||
|
||||
expect(wrapper.text()).toBe("Hello World");
|
||||
expect(container.textContent).toBe("Hello World");
|
||||
expect(localStorage.getItem(key)).toBeNull();
|
||||
|
||||
// ensure the right state was persisted to localStorage
|
||||
|
@ -266,22 +256,20 @@ describe('<SendMessageComposer/>', () => {
|
|||
it("persists to session history upon sending", async () => {
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
|
||||
const wrapper = getComponent({ replyToEvent: mockEvent });
|
||||
const { container } = getComponent({ replyToEvent: mockEvent });
|
||||
|
||||
addTextToComposer(wrapper, "This is a message");
|
||||
act(() => {
|
||||
wrapper.find(".mx_SendMessageComposer").simulate("keydown", { key: "Enter" });
|
||||
wrapper.update();
|
||||
});
|
||||
await sleep(10); // await the async _sendMessage
|
||||
wrapper.update();
|
||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||
action: "reply_to_event",
|
||||
event: null,
|
||||
context: TimelineRenderingType.Room,
|
||||
addTextToComposer(container, "This is a message");
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer"), { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||
action: "reply_to_event",
|
||||
event: null,
|
||||
context: TimelineRenderingType.Room,
|
||||
});
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toBe("");
|
||||
expect(container.textContent).toBe("");
|
||||
const str = sessionStorage.getItem(`mx_cider_history_${mockRoom.roomId}[0]`);
|
||||
expect(JSON.parse(str)).toStrictEqual({
|
||||
parts: [{ "type": "plain", "text": "This is a message" }],
|
||||
|
@ -289,19 +277,6 @@ describe('<SendMessageComposer/>', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('correctly sets the editorStateKey for threads', () => {
|
||||
const relation = {
|
||||
rel_type: RelationType.Thread,
|
||||
event_id: "myFakeThreadId",
|
||||
};
|
||||
const includeReplyLegacyFallback = false;
|
||||
const wrapper = getComponent({ relation, includeReplyLegacyFallback });
|
||||
const instance = wrapper.find(SendMessageComposerClass).instance();
|
||||
// @ts-ignore
|
||||
const key = instance.editorStateKey;
|
||||
expect(key).toEqual('mx_cider_state_myfakeroom_myFakeThreadId');
|
||||
});
|
||||
|
||||
it("correctly sends a message", () => {
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(<T extends {}>(
|
||||
roomId: string,
|
||||
|
@ -312,13 +287,10 @@ describe('<SendMessageComposer/>', () => {
|
|||
});
|
||||
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
const wrapper = getComponent();
|
||||
const { container } = getComponent();
|
||||
|
||||
addTextToComposer(wrapper, "test message");
|
||||
act(() => {
|
||||
wrapper.find(".mx_SendMessageComposer").simulate("keydown", { key: "Enter" });
|
||||
wrapper.update();
|
||||
});
|
||||
addTextToComposer(container, "test message");
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer"), { key: "Enter" });
|
||||
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith(
|
||||
"myfakeroom",
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright 2022 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 { render, screen } from "@testing-library/react";
|
||||
import { IThreepid, ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids';
|
||||
|
||||
import { EmailAddress } from '../../../../../src/components/views/settings/discovery/EmailAddresses';
|
||||
|
||||
describe("<EmailAddress/>", () => {
|
||||
it("should track props.email.bound changes", async () => {
|
||||
const email: IThreepid = {
|
||||
medium: ThreepidMedium.Email,
|
||||
address: "foo@bar.com",
|
||||
validated_at: 12345,
|
||||
added_at: 12342,
|
||||
bound: false,
|
||||
};
|
||||
|
||||
const { rerender } = render(<EmailAddress email={email} />);
|
||||
await screen.findByText("Share");
|
||||
|
||||
email.bound = true;
|
||||
rerender(<EmailAddress email={{ ...email }} />);
|
||||
await screen.findByText("Revoke");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright 2022 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 { render, screen } from "@testing-library/react";
|
||||
import { IThreepid, ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids';
|
||||
|
||||
import { PhoneNumber } from "../../../../../src/components/views/settings/discovery/PhoneNumbers";
|
||||
|
||||
describe("<PhoneNumber/>", () => {
|
||||
it("should track props.msisdn.bound changes", async () => {
|
||||
const msisdn: IThreepid = {
|
||||
medium: ThreepidMedium.Phone,
|
||||
address: "+441111111111",
|
||||
validated_at: 12345,
|
||||
added_at: 12342,
|
||||
bound: false,
|
||||
};
|
||||
|
||||
const { rerender } = render(<PhoneNumber msisdn={msisdn} />);
|
||||
await screen.findByText("Share");
|
||||
|
||||
msisdn.bound = true;
|
||||
rerender(<PhoneNumber msisdn={{ ...msisdn }} />);
|
||||
await screen.findByText("Revoke");
|
||||
});
|
||||
});
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
import { render, RenderResult, screen } from "@testing-library/react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
|
@ -24,6 +24,8 @@ import { mkStubRoom, stubClient } from "../../../../../test-utils";
|
|||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
import { EchoChamber } from "../../../../../../src/stores/local-echo/EchoChamber";
|
||||
import { RoomEchoChamber } from "../../../../../../src/stores/local-echo/RoomEchoChamber";
|
||||
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
|
||||
|
||||
describe("NotificatinSettingsTab", () => {
|
||||
const roomId = "!room:example.com";
|
||||
|
@ -55,4 +57,23 @@ describe("NotificatinSettingsTab", () => {
|
|||
|
||||
expect(roomProps.notificationVolume).not.toBe("mentions_only");
|
||||
});
|
||||
|
||||
it("should show the currently chosen custom notification sound", async () => {
|
||||
SettingsStore.setValue("notificationSound", roomId, SettingLevel.ACCOUNT, {
|
||||
url: "mxc://server/custom-sound-123",
|
||||
name: "custom-sound-123",
|
||||
});
|
||||
renderTab();
|
||||
|
||||
await screen.findByText("custom-sound-123");
|
||||
});
|
||||
|
||||
it("should show the currently chosen custom notification sound url if no name", async () => {
|
||||
SettingsStore.setValue("notificationSound", roomId, SettingLevel.ACCOUNT, {
|
||||
url: "mxc://server/custom-sound-123",
|
||||
});
|
||||
renderTab();
|
||||
|
||||
await screen.findByText("http://this.is.a.url/server/custom-sound-123");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue