Device manager - rename session (PSG-528) (#9282)

* split heading into component

* switch between editing and view

* style file

* basic tests

* style device rename component

* add loading state

* kind of handle missing current device in drilled props

* use local loading state, add basic error message

* integration-ish test rename

* tidy

* fussy import ordering

* strict errors
This commit is contained in:
Kerry 2022-09-15 16:34:50 +02:00 committed by GitHub
parent b8bb8f163a
commit 4fec436883
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 720 additions and 43 deletions

View file

@ -15,13 +15,14 @@ limitations under the License.
*/
import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { fireEvent, render, RenderResult } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { DeviceInfo } from 'matrix-js-sdk/src/crypto/deviceinfo';
import { logger } from 'matrix-js-sdk/src/logger';
import { DeviceTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning';
import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest';
import { sleep } from 'matrix-js-sdk/src/utils';
import { IMyDevice } from 'matrix-js-sdk/src/matrix';
import SessionManagerTab from '../../../../../../src/components/views/settings/tabs/user/SessionManagerTab';
import MatrixClientContext from '../../../../../../src/contexts/MatrixClientContext';
@ -40,6 +41,7 @@ describe('<SessionManagerTab />', () => {
const alicesDevice = {
device_id: deviceId,
display_name: 'Alices device',
};
const alicesMobileDevice = {
device_id: 'alices_mobile_device',
@ -64,6 +66,7 @@ describe('<SessionManagerTab />', () => {
requestVerification: jest.fn().mockResolvedValue(mockVerificationRequest),
deleteMultipleDevices: jest.fn(),
generateClientSecret: jest.fn(),
setDeviceDetails: jest.fn(),
});
const defaultProps = {};
@ -87,7 +90,6 @@ describe('<SessionManagerTab />', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(logger, 'error').mockRestore();
mockClient.getDevices.mockResolvedValue({ devices: [] });
mockClient.getStoredDevice.mockImplementation((_userId, id) => {
const device = [alicesDevice, alicesMobileDevice].find(device => device.device_id === id);
return device ? new DeviceInfo(device.device_id) : null;
@ -98,7 +100,7 @@ describe('<SessionManagerTab />', () => {
mockClient.getDevices
.mockReset()
.mockResolvedValue({ devices: [alicesMobileDevice] });
.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
});
it('renders spinner while devices load', () => {
@ -561,4 +563,106 @@ describe('<SessionManagerTab />', () => {
});
});
});
describe('Rename sessions', () => {
const updateDeviceName = async (
getByTestId: RenderResult['getByTestId'],
device: IMyDevice,
newDeviceName: string,
) => {
toggleDeviceDetails(getByTestId, device.device_id);
// start editing
fireEvent.click(getByTestId('device-heading-rename-cta'));
const input = getByTestId('device-rename-input');
fireEvent.change(input, { target: { value: newDeviceName } });
fireEvent.click(getByTestId('device-rename-submit-cta'));
await flushPromisesWithFakeTimers();
await flushPromisesWithFakeTimers();
};
it('renames current session', async () => {
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
const newDeviceName = 'new device name';
await updateDeviceName(getByTestId, alicesDevice, newDeviceName);
expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(
alicesDevice.device_id, { display_name: newDeviceName });
// devices refreshed
expect(mockClient.getDevices).toHaveBeenCalledTimes(2);
});
it('renames other session', async () => {
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
const newDeviceName = 'new device name';
await updateDeviceName(getByTestId, alicesMobileDevice, newDeviceName);
expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(
alicesMobileDevice.device_id, { display_name: newDeviceName });
// devices refreshed
expect(mockClient.getDevices).toHaveBeenCalledTimes(2);
});
it('does not rename session or refresh devices is session name is unchanged', async () => {
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
await updateDeviceName(getByTestId, alicesDevice, alicesDevice.display_name);
expect(mockClient.setDeviceDetails).not.toHaveBeenCalled();
// only called once on initial load
expect(mockClient.getDevices).toHaveBeenCalledTimes(1);
});
it('saves an empty session display name successfully', async () => {
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
await updateDeviceName(getByTestId, alicesDevice, '');
expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(
alicesDevice.device_id, { display_name: '' });
});
it('displays an error when session display name fails to save', async () => {
const logSpy = jest.spyOn(logger, 'error');
const error = new Error('oups');
mockClient.setDeviceDetails.mockRejectedValue(error);
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
const newDeviceName = 'new device name';
await updateDeviceName(getByTestId, alicesDevice, newDeviceName);
await flushPromisesWithFakeTimers();
expect(logSpy).toHaveBeenCalledWith("Error setting session display name", error);
// error displayed
expect(getByTestId('device-rename-error')).toBeTruthy();
});
});
});

View file

@ -85,11 +85,15 @@ exports[`<SessionManagerTab /> renders current session section with a verified s
<div
class="mx_DeviceTile_info"
>
<h4
class="mx_Heading_h4"
<div
tabindex="0"
>
alices_device
</h4>
<h4
class="mx_Heading_h4"
>
Alices device
</h4>
</div>
<div
class="mx_DeviceTile_metadata"
>
@ -181,11 +185,15 @@ exports[`<SessionManagerTab /> renders current session section with an unverifie
<div
class="mx_DeviceTile_info"
>
<h4
class="mx_Heading_h4"
<div
tabindex="0"
>
alices_device
</h4>
<h4
class="mx_Heading_h4"
>
Alices device
</h4>
</div>
<div
class="mx_DeviceTile_metadata"
>
@ -277,11 +285,15 @@ exports[`<SessionManagerTab /> sets device verification status correctly 1`] = `
<div
class="mx_DeviceTile_info"
>
<h4
class="mx_Heading_h4"
<div
tabindex="0"
>
alices_device
</h4>
<h4
class="mx_Heading_h4"
>
Alices device
</h4>
</div>
<div
class="mx_DeviceTile_metadata"
>