Device manager - display client information in device details (PSG-682) (#9315)
* record device client inforamtion events on app start * matrix-client-information -> matrix_client_information * fix types * remove another unused export * add docs link * display device client information in device details * update snapshots * integration-ish test client information in metadata * tests * fix tests * export helper * DeviceClientInformation type
This commit is contained in:
parent
c7a3209b84
commit
16c3efead5
11 changed files with 210 additions and 109 deletions
|
@ -26,10 +26,10 @@ import Spinner from '../../elements/Spinner';
|
||||||
import ToggleSwitch from '../../elements/ToggleSwitch';
|
import ToggleSwitch from '../../elements/ToggleSwitch';
|
||||||
import { DeviceDetailHeading } from './DeviceDetailHeading';
|
import { DeviceDetailHeading } from './DeviceDetailHeading';
|
||||||
import { DeviceVerificationStatusCard } from './DeviceVerificationStatusCard';
|
import { DeviceVerificationStatusCard } from './DeviceVerificationStatusCard';
|
||||||
import { DeviceWithVerification } from './types';
|
import { ExtendedDevice } from './types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
device: DeviceWithVerification;
|
device: ExtendedDevice;
|
||||||
pusher?: IPusher | undefined;
|
pusher?: IPusher | undefined;
|
||||||
localNotificationSettings?: LocalNotificationSettings | undefined;
|
localNotificationSettings?: LocalNotificationSettings | undefined;
|
||||||
isSigningOut: boolean;
|
isSigningOut: boolean;
|
||||||
|
@ -41,6 +41,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MetadataTable {
|
interface MetadataTable {
|
||||||
|
id: string;
|
||||||
heading?: string;
|
heading?: string;
|
||||||
values: { label: string, value?: string | React.ReactNode }[];
|
values: { label: string, value?: string | React.ReactNode }[];
|
||||||
}
|
}
|
||||||
|
@ -58,6 +59,7 @@ const DeviceDetails: React.FC<Props> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const metadata: MetadataTable[] = [
|
const metadata: MetadataTable[] = [
|
||||||
{
|
{
|
||||||
|
id: 'session',
|
||||||
values: [
|
values: [
|
||||||
{ label: _t('Session ID'), value: device.device_id },
|
{ label: _t('Session ID'), value: device.device_id },
|
||||||
{
|
{
|
||||||
|
@ -67,12 +69,28 @@ const DeviceDetails: React.FC<Props> = ({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'application',
|
||||||
|
heading: _t('Application'),
|
||||||
|
values: [
|
||||||
|
{ label: _t('Name'), value: device.clientName },
|
||||||
|
{ label: _t('Version'), value: device.clientVersion },
|
||||||
|
{ label: _t('URL'), value: device.url },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'device',
|
||||||
heading: _t('Device'),
|
heading: _t('Device'),
|
||||||
values: [
|
values: [
|
||||||
{ label: _t('IP address'), value: device.last_seen_ip },
|
{ label: _t('IP address'), value: device.last_seen_ip },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
].map(section =>
|
||||||
|
// filter out falsy values
|
||||||
|
({ ...section, values: section.values.filter(row => !!row.value) }))
|
||||||
|
.filter(section =>
|
||||||
|
// then filter out sections with no values
|
||||||
|
section.values.length,
|
||||||
|
);
|
||||||
|
|
||||||
const showPushNotificationSection = !!pusher || !!localNotificationSettings;
|
const showPushNotificationSection = !!pusher || !!localNotificationSettings;
|
||||||
|
|
||||||
|
@ -101,9 +119,10 @@ const DeviceDetails: React.FC<Props> = ({
|
||||||
</section>
|
</section>
|
||||||
<section className='mx_DeviceDetails_section'>
|
<section className='mx_DeviceDetails_section'>
|
||||||
<p className='mx_DeviceDetails_sectionHeading'>{ _t('Session details') }</p>
|
<p className='mx_DeviceDetails_sectionHeading'>{ _t('Session details') }</p>
|
||||||
{ metadata.map(({ heading, values }, index) => <table
|
{ metadata.map(({ heading, values, id }, index) => <table
|
||||||
className='mx_DeviceDetails_metadataTable'
|
className='mx_DeviceDetails_metadataTable'
|
||||||
key={index}
|
key={index}
|
||||||
|
data-testid={`device-detail-metadata-${id}`}
|
||||||
>
|
>
|
||||||
{ heading &&
|
{ heading &&
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -17,7 +17,13 @@ limitations under the License.
|
||||||
import { IMyDevice } from "matrix-js-sdk/src/matrix";
|
import { IMyDevice } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
export type DeviceWithVerification = IMyDevice & { isVerified: boolean | null };
|
export type DeviceWithVerification = IMyDevice & { isVerified: boolean | null };
|
||||||
export type DevicesDictionary = Record<DeviceWithVerification['device_id'], DeviceWithVerification>;
|
export type ExtendedDeviceInfo = {
|
||||||
|
clientName?: string;
|
||||||
|
clientVersion?: string;
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
export type ExtendedDevice = DeviceWithVerification & ExtendedDeviceInfo;
|
||||||
|
export type DevicesDictionary = Record<DeviceWithVerification['device_id'], ExtendedDevice>;
|
||||||
|
|
||||||
export enum DeviceSecurityVariation {
|
export enum DeviceSecurityVariation {
|
||||||
Verified = 'Verified',
|
Verified = 'Verified',
|
||||||
|
|
|
@ -33,7 +33,8 @@ import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifi
|
||||||
|
|
||||||
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import { DevicesDictionary, DeviceWithVerification } from "./types";
|
import { getDeviceClientInformation } from "../../../../utils/device/clientInformation";
|
||||||
|
import { DevicesDictionary, DeviceWithVerification, ExtendedDeviceInfo } from "./types";
|
||||||
import { useEventEmitter } from "../../../../hooks/useEventEmitter";
|
import { useEventEmitter } from "../../../../hooks/useEventEmitter";
|
||||||
|
|
||||||
const isDeviceVerified = (
|
const isDeviceVerified = (
|
||||||
|
@ -62,6 +63,16 @@ const isDeviceVerified = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseDeviceExtendedInformation = (matrixClient: MatrixClient, device: IMyDevice): ExtendedDeviceInfo => {
|
||||||
|
const { name, version, url } = getDeviceClientInformation(matrixClient, device.device_id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
clientName: name,
|
||||||
|
clientVersion: version,
|
||||||
|
url,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const fetchDevicesWithVerification = async (
|
const fetchDevicesWithVerification = async (
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
userId: string,
|
userId: string,
|
||||||
|
@ -75,6 +86,7 @@ const fetchDevicesWithVerification = async (
|
||||||
[device.device_id]: {
|
[device.device_id]: {
|
||||||
...device,
|
...device,
|
||||||
isVerified: isDeviceVerified(matrixClient, crossSigningInfo, device),
|
isVerified: isDeviceVerified(matrixClient, crossSigningInfo, device),
|
||||||
|
...parseDeviceExtendedInformation(matrixClient, device),
|
||||||
},
|
},
|
||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
|
|
|
@ -1717,6 +1717,9 @@
|
||||||
"Please be aware that session names are also visible to people you communicate with": "Please be aware that session names are also visible to people you communicate with",
|
"Please be aware that session names are also visible to people you communicate with": "Please be aware that session names are also visible to people you communicate with",
|
||||||
"Session ID": "Session ID",
|
"Session ID": "Session ID",
|
||||||
"Last activity": "Last activity",
|
"Last activity": "Last activity",
|
||||||
|
"Application": "Application",
|
||||||
|
"Version": "Version",
|
||||||
|
"URL": "URL",
|
||||||
"Device": "Device",
|
"Device": "Device",
|
||||||
"IP address": "IP address",
|
"IP address": "IP address",
|
||||||
"Session details": "Session details",
|
"Session details": "Session details",
|
||||||
|
|
|
@ -19,6 +19,12 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import BasePlatform from "../../BasePlatform";
|
import BasePlatform from "../../BasePlatform";
|
||||||
import { IConfigOptions } from "../../IConfigOptions";
|
import { IConfigOptions } from "../../IConfigOptions";
|
||||||
|
|
||||||
|
export type DeviceClientInformation = {
|
||||||
|
name?: string;
|
||||||
|
version?: string;
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const formatUrl = (): string | undefined => {
|
const formatUrl = (): string | undefined => {
|
||||||
// don't record url for electron clients
|
// don't record url for electron clients
|
||||||
if (window.electron) {
|
if (window.electron) {
|
||||||
|
@ -34,7 +40,7 @@ const formatUrl = (): string | undefined => {
|
||||||
].join("");
|
].join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getClientInformationEventType = (deviceId: string): string =>
|
export const getClientInformationEventType = (deviceId: string): string =>
|
||||||
`io.element.matrix_client_information.${deviceId}`;
|
`io.element.matrix_client_information.${deviceId}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,3 +64,23 @@ export const recordClientInformation = async (
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sanitizeContentString = (value: unknown): string | undefined =>
|
||||||
|
value && typeof value === 'string' ? value : undefined;
|
||||||
|
|
||||||
|
export const getDeviceClientInformation = (matrixClient: MatrixClient, deviceId: string): DeviceClientInformation => {
|
||||||
|
const event = matrixClient.getAccountData(getClientInformationEventType(deviceId));
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, version, url } = event.getContent();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: sanitizeContentString(name),
|
||||||
|
version: sanitizeContentString(version),
|
||||||
|
url: sanitizeContentString(url),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ import { isSecretStorageBeingAccessed } from "../src/SecurityManager";
|
||||||
import dis from "../src/dispatcher/dispatcher";
|
import dis from "../src/dispatcher/dispatcher";
|
||||||
import { Action } from "../src/dispatcher/actions";
|
import { Action } from "../src/dispatcher/actions";
|
||||||
import SettingsStore from "../src/settings/SettingsStore";
|
import SettingsStore from "../src/settings/SettingsStore";
|
||||||
import { mockPlatformPeg } from "./test-utils";
|
|
||||||
import { SettingLevel } from "../src/settings/SettingLevel";
|
import { SettingLevel } from "../src/settings/SettingLevel";
|
||||||
|
import { mockPlatformPeg } from "./test-utils";
|
||||||
|
|
||||||
// don't litter test console with logs
|
// don't litter test console with logs
|
||||||
jest.mock("matrix-js-sdk/src/logger");
|
jest.mock("matrix-js-sdk/src/logger");
|
||||||
|
|
|
@ -58,6 +58,7 @@ describe('<DeviceDetails />', () => {
|
||||||
display_name: 'My Device',
|
display_name: 'My Device',
|
||||||
last_seen_ip: '123.456.789',
|
last_seen_ip: '123.456.789',
|
||||||
last_seen_ts: now - 60000000,
|
last_seen_ts: now - 60000000,
|
||||||
|
clientName: 'Element Web',
|
||||||
};
|
};
|
||||||
const { container } = render(getComponent({ device }));
|
const { container } = render(getComponent({ device }));
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
|
|
|
@ -76,6 +76,7 @@ HTMLCollection [
|
||||||
</p>
|
</p>
|
||||||
<table
|
<table
|
||||||
class="mx_DeviceDetails_metadataTable"
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-session"
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -90,39 +91,6 @@ HTMLCollection [
|
||||||
alices_device
|
alices_device
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
Last activity
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table
|
|
||||||
class="mx_DeviceDetails_metadataTable"
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
Device
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
IP address
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -64,6 +64,7 @@ exports[`<DeviceDetails /> renders a verified device 1`] = `
|
||||||
</p>
|
</p>
|
||||||
<table
|
<table
|
||||||
class="mx_DeviceDetails_metadataTable"
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-session"
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -78,39 +79,6 @@ exports[`<DeviceDetails /> renders a verified device 1`] = `
|
||||||
my-device
|
my-device
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
Last activity
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table
|
|
||||||
class="mx_DeviceDetails_metadataTable"
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
Device
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
IP address
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
@ -198,6 +166,7 @@ exports[`<DeviceDetails /> renders device with metadata 1`] = `
|
||||||
</p>
|
</p>
|
||||||
<table
|
<table
|
||||||
class="mx_DeviceDetails_metadataTable"
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-session"
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -228,6 +197,33 @@ exports[`<DeviceDetails /> renders device with metadata 1`] = `
|
||||||
</table>
|
</table>
|
||||||
<table
|
<table
|
||||||
class="mx_DeviceDetails_metadataTable"
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-application"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Application
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
class="mxDeviceDetails_metadataLabel"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="mxDeviceDetails_metadataValue"
|
||||||
|
>
|
||||||
|
Element Web
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table
|
||||||
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-device"
|
||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -336,6 +332,7 @@ exports[`<DeviceDetails /> renders device without metadata 1`] = `
|
||||||
</p>
|
</p>
|
||||||
<table
|
<table
|
||||||
class="mx_DeviceDetails_metadataTable"
|
class="mx_DeviceDetails_metadataTable"
|
||||||
|
data-testid="device-detail-metadata-session"
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -350,39 +347,6 @@ exports[`<DeviceDetails /> renders device without metadata 1`] = `
|
||||||
my-device
|
my-device
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
Last activity
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table
|
|
||||||
class="mx_DeviceDetails_metadataTable"
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
Device
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataLabel"
|
|
||||||
>
|
|
||||||
IP address
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="mxDeviceDetails_metadataValue"
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -87,10 +87,10 @@ describe('<SessionManagerTab />', () => {
|
||||||
deleteMultipleDevices: jest.fn(),
|
deleteMultipleDevices: jest.fn(),
|
||||||
generateClientSecret: jest.fn(),
|
generateClientSecret: jest.fn(),
|
||||||
setDeviceDetails: jest.fn(),
|
setDeviceDetails: jest.fn(),
|
||||||
|
getAccountData: jest.fn(),
|
||||||
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(true),
|
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(true),
|
||||||
getPushers: jest.fn(),
|
getPushers: jest.fn(),
|
||||||
setPusher: jest.fn(),
|
setPusher: jest.fn(),
|
||||||
getAccountData: jest.fn(),
|
|
||||||
setLocalNotificationSettings: jest.fn(),
|
setLocalNotificationSettings: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -243,6 +243,48 @@ describe('<SessionManagerTab />', () => {
|
||||||
expect(getByTestId(`device-tile-${alicesDevice.device_id}`)).toMatchSnapshot();
|
expect(getByTestId(`device-tile-${alicesDevice.device_id}`)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('extends device with client information when available', async () => {
|
||||||
|
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
||||||
|
mockClient.getAccountData.mockImplementation((eventType: string) => {
|
||||||
|
const content = {
|
||||||
|
name: 'Element Web',
|
||||||
|
version: '1.2.3',
|
||||||
|
url: 'test.com',
|
||||||
|
};
|
||||||
|
return new MatrixEvent({
|
||||||
|
type: eventType,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// twice for each device
|
||||||
|
expect(mockClient.getAccountData).toHaveBeenCalledTimes(4);
|
||||||
|
|
||||||
|
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
||||||
|
// application metadata section rendered
|
||||||
|
expect(getByTestId('device-detail-metadata-application')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders devices without available client information without error', async () => {
|
||||||
|
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
||||||
|
|
||||||
|
const { getByTestId, queryByTestId } = render(getComponent());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
||||||
|
// application metadata section not rendered
|
||||||
|
expect(queryByTestId('device-detail-metadata-application')).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders current session section with an unverified session', async () => {
|
it('renders current session section with an unverified session', async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
|
@ -14,9 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import BasePlatform from "../../../src/BasePlatform";
|
import BasePlatform from "../../../src/BasePlatform";
|
||||||
import { IConfigOptions } from "../../../src/IConfigOptions";
|
import { IConfigOptions } from "../../../src/IConfigOptions";
|
||||||
import { recordClientInformation } from "../../../src/utils/device/clientInformation";
|
import {
|
||||||
|
getDeviceClientInformation,
|
||||||
|
recordClientInformation,
|
||||||
|
} from "../../../src/utils/device/clientInformation";
|
||||||
import { getMockClientWithEventEmitter } from "../../test-utils";
|
import { getMockClientWithEventEmitter } from "../../test-utils";
|
||||||
|
|
||||||
describe('recordClientInformation()', () => {
|
describe('recordClientInformation()', () => {
|
||||||
|
@ -84,3 +89,58 @@ describe('recordClientInformation()', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDeviceClientInformation()', () => {
|
||||||
|
const deviceId = 'my-device-id';
|
||||||
|
|
||||||
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
|
getAccountData: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty object when no event exists for the device', () => {
|
||||||
|
expect(getDeviceClientInformation(mockClient, deviceId)).toEqual({});
|
||||||
|
|
||||||
|
expect(mockClient.getAccountData).toHaveBeenCalledWith(
|
||||||
|
`io.element.matrix_client_information.${deviceId}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns client information for the device', () => {
|
||||||
|
const eventContent = {
|
||||||
|
name: 'Element Web',
|
||||||
|
version: '1.2.3',
|
||||||
|
url: 'test.com',
|
||||||
|
};
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
type: `io.element.matrix_client_information.${deviceId}`,
|
||||||
|
content: eventContent,
|
||||||
|
});
|
||||||
|
mockClient.getAccountData.mockReturnValue(event);
|
||||||
|
expect(getDeviceClientInformation(mockClient, deviceId)).toEqual(eventContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('excludes values with incorrect types', () => {
|
||||||
|
const eventContent = {
|
||||||
|
extraField: 'hello',
|
||||||
|
name: 'Element Web',
|
||||||
|
// wrong format
|
||||||
|
version: { value: '1.2.3' },
|
||||||
|
url: 'test.com',
|
||||||
|
};
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
type: `io.element.matrix_client_information.${deviceId}`,
|
||||||
|
content: eventContent,
|
||||||
|
});
|
||||||
|
mockClient.getAccountData.mockReturnValue(event);
|
||||||
|
// invalid fields excluded
|
||||||
|
expect(getDeviceClientInformation(mockClient, deviceId)).toEqual({
|
||||||
|
name: eventContent.name,
|
||||||
|
url: eventContent.url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue