You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-08-07 21:23:00 +03:00
Live location share - tiles without tile server (PSG-591) (#8962)
* live location without map POC * styles * force map tiles to show no map for test build * check latestlocationstate exists * just use loading style map fallback when cant display map * style map error for tile view * set pointer cursor when map error is clickable * test mbeaconbody with map display error, lint * lint more good * remove changes for first attempt tile * make maperror test id more accurate * fussy import ordering * PR tweaks
This commit is contained in:
@@ -99,7 +99,7 @@ describe("LocationPicker", () => {
|
||||
wrapper.setProps({});
|
||||
});
|
||||
|
||||
expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, "
|
||||
+ "or the configured map server may be unreachable.",
|
||||
);
|
||||
@@ -115,7 +115,7 @@ describe("LocationPicker", () => {
|
||||
const wrapper = getComponent();
|
||||
wrapper.setProps({});
|
||||
|
||||
expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
"This homeserver is not configured to display maps.",
|
||||
);
|
||||
});
|
||||
@@ -130,7 +130,7 @@ describe("LocationPicker", () => {
|
||||
const wrapper = getComponent();
|
||||
wrapper.setProps({});
|
||||
|
||||
expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
|
||||
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
|
||||
"This homeserver is not configured correctly to display maps, "
|
||||
+ "or the configured map server may be unreachable.",
|
||||
);
|
||||
|
@@ -15,28 +15,45 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render, RenderResult } from '@testing-library/react';
|
||||
|
||||
import { MapError } from '../../../../src/components/views/location/MapError';
|
||||
import { MapError, MapErrorProps } from '../../../../src/components/views/location/MapError';
|
||||
import { LocationShareError } from '../../../../src/utils/location';
|
||||
|
||||
describe('<MapError />', () => {
|
||||
const defaultProps = {
|
||||
onFinished: jest.fn(),
|
||||
error: LocationShareError.MapStyleUrlNotConfigured,
|
||||
className: 'test',
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
mount(<MapError {...defaultProps} {...props} />);
|
||||
const getComponent = (props: Partial<MapErrorProps> = {}): RenderResult =>
|
||||
render(<MapError {...defaultProps} {...props} />);
|
||||
|
||||
it('renders correctly for MapStyleUrlNotConfigured', () => {
|
||||
const component = getComponent();
|
||||
expect(component).toMatchSnapshot();
|
||||
const { container } = getComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly for MapStyleUrlNotReachable', () => {
|
||||
const component = getComponent({
|
||||
const { container } = getComponent({
|
||||
error: LocationShareError.MapStyleUrlNotReachable,
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not render button when onFinished falsy', () => {
|
||||
const { queryByText } = getComponent({
|
||||
error: LocationShareError.MapStyleUrlNotReachable,
|
||||
onFinished: undefined,
|
||||
});
|
||||
// no button
|
||||
expect(queryByText('OK')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('applies class when isMinimised is truthy', () => {
|
||||
const { container } = getComponent({
|
||||
isMinimised: true,
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@@ -1,95 +1,91 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<MapError /> renders correctly for MapStyleUrlNotConfigured 1`] = `
|
||||
<MapError
|
||||
error="MapStyleUrlNotConfigured"
|
||||
onFinished={[MockFunction]}
|
||||
>
|
||||
exports[`<MapError /> applies class when isMinimised is truthy 1`] = `
|
||||
<div>
|
||||
<div
|
||||
className="mx_MapError"
|
||||
data-test-id="location-picker-error"
|
||||
class="mx_MapError test mx_MapError_isMinimised"
|
||||
data-test-id="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
className="mx_MapError_icon"
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
<Heading
|
||||
className="mx_MapError_heading"
|
||||
size="h3"
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
<p
|
||||
class="mx_MapError_message"
|
||||
>
|
||||
<h3
|
||||
className="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
</Heading>
|
||||
<p>
|
||||
This homeserver is not configured to display maps.
|
||||
</p>
|
||||
<AccessibleButton
|
||||
element="button"
|
||||
kind="primary"
|
||||
onClick={[MockFunction]}
|
||||
<button
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
onClick={[MockFunction]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</AccessibleButton>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</MapError>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<MapError /> renders correctly for MapStyleUrlNotConfigured 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_MapError test"
|
||||
data-test-id="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
<p
|
||||
class="mx_MapError_message"
|
||||
>
|
||||
This homeserver is not configured to display maps.
|
||||
</p>
|
||||
<button
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<MapError /> renders correctly for MapStyleUrlNotReachable 1`] = `
|
||||
<MapError
|
||||
error="MapStyleUrlNotReachable"
|
||||
onFinished={[MockFunction]}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="mx_MapError"
|
||||
data-test-id="location-picker-error"
|
||||
class="mx_MapError test"
|
||||
data-test-id="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
className="mx_MapError_icon"
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
<Heading
|
||||
className="mx_MapError_heading"
|
||||
size="h3"
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
<p
|
||||
class="mx_MapError_message"
|
||||
>
|
||||
<h3
|
||||
className="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
</Heading>
|
||||
<p>
|
||||
This homeserver is not configured correctly to display maps, or the configured map server may be unreachable.
|
||||
</p>
|
||||
<AccessibleButton
|
||||
element="button"
|
||||
kind="primary"
|
||||
onClick={[MockFunction]}
|
||||
<button
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
onClick={[MockFunction]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</AccessibleButton>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</MapError>
|
||||
</div>
|
||||
`;
|
||||
|
@@ -33,6 +33,7 @@ import {
|
||||
getMockClientWithEventEmitter,
|
||||
makeBeaconEvent,
|
||||
makeBeaconInfoEvent,
|
||||
makeRoomWithBeacons,
|
||||
makeRoomWithStateEvents,
|
||||
} from '../../../test-utils';
|
||||
import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks';
|
||||
@@ -40,6 +41,9 @@ import { MediaEventHelper } from '../../../../src/utils/MediaEventHelper';
|
||||
import MatrixClientContext from '../../../../src/contexts/MatrixClientContext';
|
||||
import Modal from '../../../../src/Modal';
|
||||
import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils';
|
||||
import { MapError } from '../../../../src/components/views/location/MapError';
|
||||
import * as mapUtilHooks from '../../../../src/utils/location/useMap';
|
||||
import { LocationShareError } from '../../../../src/utils/location';
|
||||
|
||||
describe('<MBeaconBody />', () => {
|
||||
// 14.03.2022 16:15
|
||||
@@ -94,112 +98,116 @@ describe('<MBeaconBody />', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders stopped beacon UI for an explicitly stopped beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders stopped beacon UI for an expired beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders loading beacon UI for a beacon that has not started yet', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// puts this beacons start timestamp in the future
|
||||
{ isLive: true, timestamp: now + 60000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Loading live location...");
|
||||
});
|
||||
|
||||
it('does not open maximised map when on click when beacon is stopped', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
act(() => {
|
||||
component.find('.mx_MBeaconBody_map').at(0).simulate('click');
|
||||
const testBeaconStatuses = () => {
|
||||
it('renders stopped beacon UI for an explicitly stopped beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: false },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
expect(modalSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is not the latest beacon for a user', () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
|
||||
makeRoomWithStateEvents([aliceBeaconInfo1, aliceBeaconInfo2], { roomId, mockClient });
|
||||
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo1 });
|
||||
// beacon1 has been superceded by beacon2
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is replaced', () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo1], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo1 });
|
||||
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo1));
|
||||
// update alice's beacon with a new edition
|
||||
// beacon instance emits
|
||||
act(() => {
|
||||
beaconInstance.update(aliceBeaconInfo2);
|
||||
it('renders stopped beacon UI for an expired beacon', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
component.setProps({});
|
||||
it('renders loading beacon UI for a beacon that has not started yet', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// puts this beacons start timestamp in the future
|
||||
{ isLive: true, timestamp: now + 60000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.text()).toEqual("Loading live location...");
|
||||
});
|
||||
|
||||
// beacon1 has been superceded by beacon2
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
it('does not open maximised map when on click when beacon is stopped', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
// puts this beacons live period in the past
|
||||
{ isLive: true, timestamp: now - 600000, timeout: 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
act(() => {
|
||||
component.find('.mx_MBeaconBody_map').at(0).simulate('click');
|
||||
});
|
||||
|
||||
expect(modalSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is not the latest beacon for a user', () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
|
||||
makeRoomWithStateEvents([aliceBeaconInfo1, aliceBeaconInfo2], { roomId, mockClient });
|
||||
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo1 });
|
||||
// beacon1 has been superceded by beacon2
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
|
||||
it('renders stopped UI when a beacon event is replaced', () => {
|
||||
const aliceBeaconInfo1 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
// this one is a little older
|
||||
{ isLive: true, timestamp: now - 500 },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
aliceBeaconInfo1.event.origin_server_ts = now - 500;
|
||||
const aliceBeaconInfo2 = makeBeaconInfoEvent(
|
||||
aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-2',
|
||||
);
|
||||
|
||||
const room = makeRoomWithStateEvents([aliceBeaconInfo1], { roomId, mockClient });
|
||||
const component = getComponent({ mxEvent: aliceBeaconInfo1 });
|
||||
|
||||
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo1));
|
||||
// update alice's beacon with a new edition
|
||||
// beacon instance emits
|
||||
act(() => {
|
||||
beaconInstance.update(aliceBeaconInfo2);
|
||||
});
|
||||
|
||||
component.setProps({});
|
||||
|
||||
// beacon1 has been superceded by beacon2
|
||||
expect(component.text()).toEqual("Live location ended");
|
||||
});
|
||||
};
|
||||
|
||||
testBeaconStatuses();
|
||||
|
||||
describe('on liveness change', () => {
|
||||
it('renders stopped UI when a beacon stops being live', () => {
|
||||
@@ -458,4 +466,34 @@ describe('<MBeaconBody />', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when map display is not configured', () => {
|
||||
beforeEach(() => {
|
||||
// mock map utils to raise MapStyleUrlNotConfigured error
|
||||
jest.spyOn(mapUtilHooks, 'useMap').mockImplementation(
|
||||
({ onError }) => {
|
||||
onError(new Error(LocationShareError.MapStyleUrlNotConfigured));
|
||||
return mockMap;
|
||||
});
|
||||
});
|
||||
|
||||
it('renders maps unavailable error for a live beacon with location', () => {
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
|
||||
roomId,
|
||||
{ isLive: true },
|
||||
'$alice-room1-1',
|
||||
);
|
||||
const location1 = makeBeaconEvent(
|
||||
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
|
||||
);
|
||||
|
||||
makeRoomWithBeacons(roomId, mockClient, [beaconInfoEvent], [location1]);
|
||||
|
||||
const component = getComponent({ mxEvent: beaconInfoEvent });
|
||||
expect(component.find(MapError)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// test that statuses display as expected with a map display error
|
||||
testBeaconStatuses();
|
||||
});
|
||||
});
|
||||
|
@@ -0,0 +1,35 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<MBeaconBody /> when map display is not configured renders maps unavailable error for a live beacon with location 1`] = `
|
||||
<MapError
|
||||
className="mx_MBeaconBody_mapError mx_MBeaconBody_mapErrorInteractive"
|
||||
error="MapStyleUrlNotConfigured"
|
||||
isMinimised={true}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="mx_MapError mx_MBeaconBody_mapError mx_MBeaconBody_mapErrorInteractive mx_MapError_isMinimised"
|
||||
data-test-id="map-rendering-error"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="mx_MapError_icon"
|
||||
/>
|
||||
<Heading
|
||||
className="mx_MapError_heading"
|
||||
size="h3"
|
||||
>
|
||||
<h3
|
||||
className="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
Unable to load map
|
||||
</h3>
|
||||
</Heading>
|
||||
<p
|
||||
className="mx_MapError_message"
|
||||
>
|
||||
This homeserver is not configured to display maps.
|
||||
</p>
|
||||
</div>
|
||||
</MapError>
|
||||
`;
|
Reference in New Issue
Block a user