Skip to content

Commit 21d62c5

Browse files
authored
fix: ensure marker position has valid numeric coordinates. (#950)
Updated the condition to check if `lat` and `lng` are finite numbers before creating a `LatLng` object. This prevents potential runtime errors when lat and lng are 0.
1 parent b3915fb commit 21d62c5

File tree

2 files changed

+85
-16
lines changed

2 files changed

+85
-16
lines changed

src/marker-utils.test.ts

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe.each(markerClasses)(
3636
map = new google.maps.Map(document.createElement("div"));
3737
});
3838

39-
test("identifies AdvancedMarker instances", () => {
39+
test(`${markerClass.name}: identifies AdvancedMarker instances`, () => {
4040
const isAdvancedMarker = MarkerUtils.isAdvancedMarker(new markerClass());
4141
if (markerClass === google.maps.marker.AdvancedMarkerElement) {
4242
expect(isAdvancedMarker).toBeTruthy();
@@ -45,7 +45,7 @@ describe.each(markerClasses)(
4545
expect(isAdvancedMarker).toBeFalsy();
4646
});
4747

48-
test("sets the map", () => {
48+
test(`${markerClass.name}: sets the map`, () => {
4949
const marker = new markerClass();
5050
MarkerUtils.setMap(marker, map);
5151
if (markerClass === google.maps.marker.AdvancedMarkerElement) {
@@ -57,21 +57,48 @@ describe.each(markerClasses)(
5757
expect((marker as google.maps.Marker).setMap).toHaveBeenCalled();
5858
});
5959

60-
test("gets the marker position and returns a LatLng", () => {
61-
// test markers created with LatLng and LatLngLiteral
62-
[new google.maps.LatLng(1, 1), { lat: 1, lng: 1 }].forEach((position) => {
63-
const marker = new markerClass({ position: position });
64-
if (markerClass === google.maps.marker.AdvancedMarkerElement) {
65-
(marker as google.maps.marker.AdvancedMarkerElement).position =
66-
position;
60+
test.each([
61+
[{ lat: 0, lng: 0 }, false, 0, 0],
62+
[{ lat: 0, lng: 1 }, false, 0, 1],
63+
[{ lat: 1, lng: 0 }, false, 1, 0],
64+
[{ lat: 1, lng: 1 }, false, 1, 1],
65+
[{ lat: 2, lng: 2 }, true, 2, 2],
66+
])(
67+
`${markerClass.name}: gets the marker position and returns a LatLng (%p)`,
68+
(input, convertToLatLng, lat, lng) => {
69+
// this test needs the partial implementation of the LatLng class that can be found below
70+
overwriteLatLngMock();
71+
72+
const isAdvMarker =
73+
markerClass === google.maps.marker.AdvancedMarkerElement;
74+
let marker:
75+
| google.maps.Marker
76+
| google.maps.marker.AdvancedMarkerElement;
77+
78+
if (isAdvMarker) {
79+
const m = new google.maps.marker.AdvancedMarkerElement();
80+
// in some test-cases, the advanced-marker version returns a
81+
// LatLng instance as well
82+
m.position = convertToLatLng ? new google.maps.LatLng(input) : input;
83+
84+
marker = m;
85+
} else {
86+
const m = new google.maps.Marker();
87+
jest
88+
.mocked(m.getPosition)
89+
.mockReturnValue(new google.maps.LatLng(input));
90+
marker = m;
6791
}
68-
expect(MarkerUtils.getPosition(marker)).toBeInstanceOf(
69-
google.maps.LatLng
70-
);
71-
});
72-
});
7392

74-
test(`${markerClass.name}.getVisible`, () => {
93+
const res = MarkerUtils.getPosition(marker);
94+
95+
expect(res).toBeInstanceOf(google.maps.LatLng);
96+
expect(res.lat()).toBe(lat);
97+
expect(res.lng()).toBe(lng);
98+
}
99+
);
100+
101+
test(`${markerClass.name}: MarkerUtils.getVisible`, () => {
75102
const marker = new markerClass();
76103

77104
const res = MarkerUtils.getVisible(marker);
@@ -84,3 +111,42 @@ describe.each(markerClasses)(
84111
});
85112
}
86113
);
114+
115+
/* overwrites the google.maps.LatLng class with a partly functional version */
116+
function overwriteLatLngMock() {
117+
const LatLngMock = class extends google.maps.LatLng {
118+
constructor(
119+
latOrLatLngOrLatLngLiteral:
120+
| number
121+
| google.maps.LatLngLiteral
122+
| google.maps.LatLng,
123+
lngOrNoClampNoWrap?: number | boolean | null,
124+
noClampNoWrap?: boolean
125+
) {
126+
super(latOrLatLngOrLatLngLiteral, lngOrNoClampNoWrap, noClampNoWrap);
127+
128+
let lat: number;
129+
let lng: number;
130+
131+
if (typeof latOrLatLngOrLatLngLiteral === "object") {
132+
if (
133+
typeof latOrLatLngOrLatLngLiteral.lat === "function" &&
134+
typeof latOrLatLngOrLatLngLiteral.lng === "function"
135+
) {
136+
lat = latOrLatLngOrLatLngLiteral.lat();
137+
lng = latOrLatLngOrLatLngLiteral.lng();
138+
} else {
139+
lat = latOrLatLngOrLatLngLiteral.lat as number;
140+
lng = latOrLatLngOrLatLngLiteral.lng as number;
141+
}
142+
} else {
143+
lat = latOrLatLngOrLatLngLiteral as number;
144+
lng = lngOrNoClampNoWrap as number;
145+
}
146+
147+
this.lat = () => lat;
148+
this.lng = () => lng;
149+
}
150+
};
151+
google.maps.LatLng = LatLngMock;
152+
}

src/marker-utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ export class MarkerUtils {
5858
return marker.position;
5959
}
6060
// since we can't cast to LatLngLiteral for reasons =(
61-
if (marker.position.lat && marker.position.lng) {
61+
if (
62+
Number.isFinite(marker.position.lat) &&
63+
Number.isFinite(marker.position.lng)
64+
) {
6265
return new google.maps.LatLng(
6366
marker.position.lat,
6467
marker.position.lng

0 commit comments

Comments
 (0)