Live location sharing - consolidate maps (#8236)

* extract location markers into generic Marker

Signed-off-by: Kerry Archibald <kerrya@element.io>

* wrap marker in smartmarker

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test smartmarker

Signed-off-by: Kerry Archibald <kerrya@element.io>

* working map in location body

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test Map

Signed-off-by: Kerry Archibald <kerrya@element.io>

* remove skinned sdk

Signed-off-by: Kerry Archibald <kerrya@element.io>

* update snaps with new mocks

Signed-off-by: Kerry Archibald <kerrya@element.io>

* use new ZoomButtons in MLocationBody

Signed-off-by: Kerry Archibald <kerrya@element.io>

* make LocationViewDialog map interactive

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test MLocationBody

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test LocationViewDialog

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add copyrights, shrink snapshot

Signed-off-by: Kerry Archibald <kerrya@element.io>

* update comment

Signed-off-by: Kerry Archibald <kerrya@element.io>

* lint

Signed-off-by: Kerry Archibald <kerrya@element.io>

* lint

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry
2022-04-11 18:40:06 +02:00
committed by GitHub
parent 944e11d7d6
commit 9ba55d1d14
16 changed files with 889 additions and 234 deletions
@@ -16,50 +16,52 @@ limitations under the License.
import React from 'react';
import { mount } from "enzyme";
import { mocked } from 'jest-mock';
import { LocationAssetType } from "matrix-js-sdk/src/@types/location";
import { RoomMember } from 'matrix-js-sdk/src/matrix';
import maplibregl from 'maplibre-gl';
import { logger } from 'matrix-js-sdk/src/logger';
import { act } from 'react-dom/test-utils';
import MLocationBody from "../../../../src/components/views/messages/MLocationBody";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";
import { getTileServerWellKnown } from "../../../../src/utils/WellKnownUtils";
import Modal from '../../../../src/Modal';
import SdkConfig from "../../../../src/SdkConfig";
import { makeLocationEvent } from "../../../test-utils/location";
jest.mock("../../../../src/utils/WellKnownUtils", () => ({
getTileServerWellKnown: jest.fn(),
}));
import { getMockClientWithEventEmitter } from '../../../test-utils';
describe("MLocationBody", () => {
describe('<MLocationBody>', () => {
const roomId = '!room:server';
const userId = '@user:server';
const mockClient = getMockClientWithEventEmitter({
getClientWellKnown: jest.fn().mockReturnValue({
"m.tile_server": { map_style_url: 'maps.com' },
}),
isGuest: jest.fn().mockReturnValue(false),
});
const defaultEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Pin);
const defaultProps = {
mxEvent: defaultEvent,
highlights: [],
highlightLink: '',
onHeightChanged: jest.fn(),
onMessageAllowed: jest.fn(),
permalinkCreator: {} as RoomPermalinkCreator,
mediaEventHelper: {} as MediaEventHelper,
};
const getComponent = (props = {}) => mount(<MLocationBody {...defaultProps} {...props} />, {
wrappingComponent: MatrixClientContext.Provider,
wrappingComponentProps: { value: mockClient },
});
describe('with error', () => {
const mockClient = {
on: jest.fn(),
off: jest.fn(),
};
const defaultEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Pin);
const defaultProps = {
mxEvent: defaultEvent,
highlights: [],
highlightLink: '',
onHeightChanged: jest.fn(),
onMessageAllowed: jest.fn(),
permalinkCreator: {} as RoomPermalinkCreator,
mediaEventHelper: {} as MediaEventHelper,
};
const getComponent = (props = {}) => mount(<MLocationBody {...defaultProps} {...props} />, {
wrappingComponent: MatrixClientContext.Provider,
wrappingComponentProps: { value: mockClient },
});
let sdkConfigSpy;
beforeEach(() => {
// eat expected errors to keep console clean
jest.spyOn(logger, 'error').mockImplementation(() => { });
mocked(getTileServerWellKnown).mockReturnValue({});
mockClient.getClientWellKnown.mockReturnValue({});
sdkConfigSpy = jest.spyOn(SdkConfig, 'get').mockReturnValue({});
});
@@ -75,7 +77,9 @@ describe("MLocationBody", () => {
it('displays correct fallback content when map_style_url is misconfigured', () => {
const mockMap = new maplibregl.Map();
mocked(getTileServerWellKnown).mockReturnValue({ map_style_url: 'bad-tile-server.com' });
mockClient.getClientWellKnown.mockReturnValue({
"m.tile_server": { map_style_url: 'bad-tile-server.com' },
});
const component = getComponent();
// simulate error initialising map in maplibregl
@@ -85,5 +89,69 @@ describe("MLocationBody", () => {
expect(component.find(".mx_EventTile_body")).toMatchSnapshot();
});
});
describe('without error', () => {
beforeEach(() => {
mockClient.getClientWellKnown.mockReturnValue({
"m.tile_server": { map_style_url: 'maps.com' },
});
// MLocationBody uses random number for map id
// stabilise for test
jest.spyOn(global.Math, 'random').mockReturnValue(0.123456);
});
afterAll(() => {
jest.spyOn(global.Math, 'random').mockRestore();
});
it('renders map correctly', () => {
const mockMap = new maplibregl.Map();
const component = getComponent();
expect(component).toMatchSnapshot();
// map was centered
expect(mockMap.setCenter).toHaveBeenCalledWith({
lat: 51.5076, lon: -0.1276,
});
});
it('opens map dialog on click', () => {
const modalSpy = jest.spyOn(Modal, 'createTrackedDialog').mockReturnValue(undefined);
const component = getComponent();
act(() => {
component.find('Map').at(0).simulate('click');
});
expect(modalSpy).toHaveBeenCalled();
});
it('renders marker correctly for a non-self share', () => {
const mockMap = new maplibregl.Map();
const component = getComponent();
expect(component.find('SmartMarker').at(0).props()).toEqual(
expect.objectContaining({
map: mockMap,
geoUri: 'geo:51.5076,-0.1276',
roomMember: undefined,
}),
);
});
it('renders marker correctly for a self share', () => {
const selfShareEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Self);
const member = new RoomMember(roomId, userId);
// @ts-ignore cheat assignment to property
selfShareEvent.sender = member;
const component = getComponent({ mxEvent: selfShareEvent });
// render self locations with user avatars
expect(component.find('SmartMarker').at(0).props()['roomMember']).toEqual(
member,
);
});
});
});
});
@@ -27,3 +27,159 @@ exports[`MLocationBody <MLocationBody> with error displays correct fallback cont
Shared a location: Found at geo:51.5076,-0.1276 at 2021-12-21T12:22+0000
</div>
`;
exports[`MLocationBody <MLocationBody> without error renders map correctly 1`] = `
<MLocationBody
highlightLink=""
highlights={Array []}
mediaEventHelper={Object {}}
mxEvent={
Object {
"content": Object {
"body": "Found at geo:51.5076,-0.1276 at 2021-12-21T12:22+0000",
"geo_uri": "geo:51.5076,-0.1276",
"msgtype": "m.location",
"org.matrix.msc1767.text": "Found at geo:51.5076,-0.1276 at 2021-12-21T12:22+0000",
"org.matrix.msc3488.asset": Object {
"type": "m.pin",
},
"org.matrix.msc3488.location": Object {
"description": "Human-readable label",
"uri": "geo:51.5076,-0.1276",
},
"org.matrix.msc3488.ts": 252523,
},
"event_id": "$2",
"type": "org.matrix.msc3488.location",
}
}
onHeightChanged={[MockFunction]}
onMessageAllowed={[MockFunction]}
permalinkCreator={Object {}}
>
<LocationBodyContent
mapId="mx_MLocationBody_$2_1f9acffa"
mxEvent={
Object {
"content": Object {
"body": "Found at geo:51.5076,-0.1276 at 2021-12-21T12:22+0000",
"geo_uri": "geo:51.5076,-0.1276",
"msgtype": "m.location",
"org.matrix.msc1767.text": "Found at geo:51.5076,-0.1276 at 2021-12-21T12:22+0000",
"org.matrix.msc3488.asset": Object {
"type": "m.pin",
},
"org.matrix.msc3488.location": Object {
"description": "Human-readable label",
"uri": "geo:51.5076,-0.1276",
},
"org.matrix.msc3488.ts": 252523,
},
"event_id": "$2",
"type": "org.matrix.msc3488.location",
}
}
onClick={[Function]}
onError={[Function]}
tooltip="Expand map"
>
<div
className="mx_MLocationBody"
>
<TooltipTarget
alignment={5}
label="Expand map"
maxParentWidth={450}
>
<div
onBlur={[Function]}
onFocus={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
tabIndex={0}
>
<Map
centerGeoUri="geo:51.5076,-0.1276"
className="mx_MLocationBody_map"
id="mx_MLocationBody_$2_1f9acffa"
onClick={[Function]}
onError={[Function]}
>
<div
className="mx_Map mx_MLocationBody_map"
id="mx_Map_mx_MLocationBody_$2_1f9acffa"
onClick={[Function]}
>
<SmartMarker
geoUri="geo:51.5076,-0.1276"
id="mx_MLocationBody_$2_1f9acffa-marker"
map={
MockMap {
"_events": Object {
"error": Array [
[Function],
[Function],
],
},
"_eventsCount": 1,
"_maxListeners": undefined,
"addControl": [MockFunction],
"removeControl": [MockFunction],
"setCenter": [MockFunction] {
"calls": Array [
Array [
Object {
"lat": 51.5076,
"lon": -0.1276,
},
],
Array [
Object {
"lat": 51.5076,
"lon": -0.1276,
},
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
Object {
"type": "return",
"value": undefined,
},
],
},
"setStyle": [MockFunction],
"zoomIn": [MockFunction],
"zoomOut": [MockFunction],
Symbol(kCapture): false,
}
}
>
<ForwardRef
id="mx_MLocationBody_$2_1f9acffa-marker"
>
<div
className="mx_Marker mx_Marker_defaultColor"
id="mx_MLocationBody_$2_1f9acffa-marker"
>
<div
className="mx_Marker_border"
>
<div
className="mx_Marker_icon"
/>
</div>
</div>
</ForwardRef>
</SmartMarker>
</div>
</Map>
</div>
</TooltipTarget>
</div>
</LocationBodyContent>
</MLocationBody>
`;