From 0e50760ab3e1f626300b4b467a118ab452364965 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Mon, 13 May 2024 14:35:01 -0500 Subject: [PATCH] Implemented core LatLng/LatLngBounds classes (#39) * Implemented core LatLng/LatLngBounds classes * Set lng to NaN when creating empty LatLng --- examples/basicMap/example.js | 2 +- src/directions.ts | 8 +- src/googleCommon.ts | 269 +++++++++++++++++++++++++------- src/index.ts | 3 + src/maps.ts | 33 ++-- src/markers.ts | 4 +- src/places.ts | 25 ++- test/googleCommon.test.ts | 286 ++++++++++++++++++++++++++++++++++ test/maps.test.ts | 288 ++++++++++++++++++++--------------- test/markers.test.ts | 14 +- test/places.test.ts | 12 +- 11 files changed, 718 insertions(+), 226 deletions(-) create mode 100644 test/googleCommon.test.ts diff --git a/examples/basicMap/example.js b/examples/basicMap/example.js index d3b1c76..4c70f82 100644 --- a/examples/basicMap/example.js +++ b/examples/basicMap/example.js @@ -7,7 +7,7 @@ // and retain the same functionality when using the migration adapter. function initMap() { - const austinCoords = { lat: 30.268193, lng: -97.7457518 }; // Austin, TX :) + const austinCoords = new google.maps.LatLng(30.268193, -97.7457518); // Austin, TX :) const map = new google.maps.Map(document.getElementById("map"), { center: austinCoords, diff --git a/src/directions.ts b/src/directions.ts index f183b06..f9f494e 100644 --- a/src/directions.ts +++ b/src/directions.ts @@ -3,7 +3,7 @@ import { CalculateRouteCommand, CalculateRouteRequest, LocationClient } from "@aws-sdk/client-location"; -import { DirectionsStatus, GoogleLatLng, GoogleLatLngBounds } from "./googleCommon"; +import { DirectionsStatus, MigrationLatLng, MigrationLatLngBounds } from "./googleCommon"; import { MigrationMap } from "./maps"; import { MigrationMarker } from "./markers"; import { MigrationPlacesService } from "./places"; @@ -68,8 +68,8 @@ class MigrationDirectionsService { text: step.DurationSeconds + " seconds", // TODO: Add conversion logic to make this seconds/minutes/hours value: step.DurationSeconds, }, - start_location: GoogleLatLng(step.StartPosition[1], step.StartPosition[0]), - end_location: GoogleLatLng(step.EndPosition[1], step.EndPosition[0]), + start_location: new MigrationLatLng(step.StartPosition[1], step.StartPosition[0]), + end_location: new MigrationLatLng(step.EndPosition[1], step.EndPosition[0]), }); }); @@ -82,7 +82,7 @@ class MigrationDirectionsService { }); const googleRoute = { - bounds: GoogleLatLngBounds( + bounds: new MigrationLatLngBounds( { lng: bounds[0], lat: bounds[1], diff --git a/src/googleCommon.ts b/src/googleCommon.ts index e8c7ba1..148de92 100644 --- a/src/googleCommon.ts +++ b/src/googleCommon.ts @@ -1,31 +1,93 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { LngLatBounds } from "maplibre-gl"; + +interface LatLngLiteral { + lat: number; + lng: number; +} + +type LatLngLike = LatLngLiteral | MigrationLatLng; + +interface LatLngBoundsLiteral { + east: number; + north: number; + south: number; + west: number; +} + // Migration version of google.maps.LatLng -// This is only used in adapter standalone mode and in unit tests -class MigrationLatLng { - lat: any; - lng: any; - - constructor(lat: number, lng: number, noWrap?: boolean) { - // TODO: Need to implement handling of noWrap - // TODO: Add support for handling LatLngLiteral - - // These are implemented as property functions instead of prototype functions - // to match the google.maps API - this.lat = function () { - return lat; - }; +export class MigrationLatLng { + #lat: number; + #lng: number; - this.lng = function () { - return lng; - }; + constructor( + latOrLatLngOrLatLngLiteral: number | LatLngLiteral | MigrationLatLng, + lngOrNoClampNoWrap?: number | boolean | null, + noClampNoWrap?: boolean, + ) { + if (latOrLatLngOrLatLngLiteral == null) { + this.#lat = NaN; + this.#lng = NaN; + } else if (typeof latOrLatLngOrLatLngLiteral === "number") { + this.#lat = latOrLatLngOrLatLngLiteral; + } else if (latOrLatLngOrLatLngLiteral.lat !== undefined && latOrLatLngOrLatLngLiteral.lat !== undefined) { + if (typeof latOrLatLngOrLatLngLiteral.lat === "number" && typeof latOrLatLngOrLatLngLiteral.lng === "number") { + this.#lat = latOrLatLngOrLatLngLiteral.lat; + this.#lng = latOrLatLngOrLatLngLiteral.lng; + } else if ( + typeof latOrLatLngOrLatLngLiteral.lat === "function" && + typeof latOrLatLngOrLatLngLiteral.lng === "function" + ) { + this.#lat = latOrLatLngOrLatLngLiteral.lat(); + this.#lng = latOrLatLngOrLatLngLiteral.lng(); + } + } + + let shouldClamp = true; + if (typeof lngOrNoClampNoWrap === "number") { + this.#lng = lngOrNoClampNoWrap; + } else if (typeof lngOrNoClampNoWrap === "boolean") { + shouldClamp = !lngOrNoClampNoWrap; + } + + if (typeof noClampNoWrap === "boolean") { + shouldClamp = !noClampNoWrap; + } + + if (shouldClamp && this.#lat != null && this.#lng != null) { + // Latitude should be clamped to [-90, 90] + if (this.#lat < -90) { + this.#lat = -90; + } else if (this.#lat > 90) { + this.#lat = 90; + } + + // Longitude should be wrapped to [-180, 180] + const minLongitude = -180; + const maxLongitude = 180; + if (this.#lng < minLongitude || this.#lng > maxLongitude) { + const range = maxLongitude - minLongitude; + const wrapped = ((((this.#lng - minLongitude) % range) + range) % range) + minLongitude; + + this.#lng = wrapped; + } + } } equals(other) { return other ? this.lat() == other.lat() && this.lng() == other.lng() : false; } + lat() { + return this.#lat; + } + + lng() { + return this.#lng; + } + toString() { return "(" + this.lat() + ", " + this.lng() + ")"; } @@ -37,62 +99,167 @@ class MigrationLatLng { }; } - toUrlValue() { - return this.lat() + "," + this.lng(); + // Rounded to 6 decimal places by default + toUrlValue(precision = 6) { + // Trim trailing 0's by using trick of dividing by 1 afterwards + const latDigits = this.lat().toPrecision(precision); + const latTrimmed = parseFloat(latDigits) / 1; + const lngDigits = this.lng().toPrecision(precision); + const lngTrimmed = parseFloat(lngDigits) / 1; + + return `${latTrimmed},${lngTrimmed}`; } } // Migration version of google.maps.LatLngBounds -// This is only used in adapter standalone mode and in unit tests -class MigrationLatLngBounds { - sw: MigrationLatLng; - ne: MigrationLatLng; +export class MigrationLatLngBounds { + #lngLatBounds: LngLatBounds; + + constructor( + swOrLatLngBounds?: MigrationLatLng | null | LatLngLiteral | MigrationLatLngBounds | LatLngBoundsLiteral, + ne?: MigrationLatLng | null | LatLngLiteral, + ) { + let west, south, east, north; + + if (!swOrLatLngBounds) { + // Inputs are empty, so create an empty LngLatBounds + this.#lngLatBounds = new LngLatBounds(); + return; + } else { + let southWest, northEast; + if (ne) { + southWest = new MigrationLatLng(swOrLatLngBounds as LatLngLike); + northEast = new MigrationLatLng(ne); + + west = southWest.lng(); + south = southWest.lat(); + east = northEast.lng(); + north = northEast.lat(); + } else if (swOrLatLngBounds instanceof MigrationLatLngBounds) { + southWest = swOrLatLngBounds.getSouthWest(); + northEast = swOrLatLngBounds.getNorthEast(); + + west = southWest.lng(); + south = southWest.lat(); + east = northEast.lng(); + north = northEast.lat(); + } /* LatLngBoundsLiteral */ else { + const boundsLiteral = swOrLatLngBounds as LatLngBoundsLiteral; + west = boundsLiteral.west; + south = boundsLiteral.south; + east = boundsLiteral.east; + north = boundsLiteral.north; + } + + // west, south, east, north + this.#lngLatBounds = new LngLatBounds([west, south, east, north]); + } + } + + contains(latLng) { + return this.#lngLatBounds.contains(LatLngToLngLat(latLng)); + } - constructor(swOrLatLngBounds, ne) { - // TODO: Handle LatLngBoundsLiteral + equals(other) { + const otherBounds = new MigrationLatLngBounds(other); + + return ( + this.getSouthWest().equals(otherBounds.getSouthWest()) && this.getNorthEast().equals(otherBounds.getNorthEast()) + ); + } + + extend(point) { + const lngLat = LatLngToLngLat(point); + + this.#lngLatBounds.extend(lngLat); - this.sw = swOrLatLngBounds; - this.ne = ne; + return this; + } + + getCenter() { + const lngLatCenter = this.#lngLatBounds.getCenter(); + return new MigrationLatLng(lngLatCenter.lat, lngLatCenter.lng); } getNorthEast() { - return this.ne; + const northEast = this.#lngLatBounds.getNorthEast(); + return new MigrationLatLng(northEast.lat, northEast.lng); } getSouthWest() { - return this.sw; + const southWest = this.#lngLatBounds.getSouthWest(); + return new MigrationLatLng(southWest.lat, southWest.lng); } - // TODO: Add methods to match Google LatLngBounds -} + isEmpty() { + return this.#lngLatBounds.isEmpty(); + } -// Dynamic function to create a LatLng instance. It will first try google.maps.LatLng -// and if it's not found, our migration version will be used. -export const GoogleLatLng = function (lat, lng, noWrap = false) { - return typeof google !== "undefined" - ? new google.maps.LatLng(lat, lng, noWrap) - : new MigrationLatLng(lat, lng, noWrap); -}; + toJSON() { + return { + east: this.#lngLatBounds.getEast(), + north: this.#lngLatBounds.getNorth(), + west: this.#lngLatBounds.getWest(), + south: this.#lngLatBounds.getSouth(), + }; + } -// Dynamic function to create a LatLngBounds instance. It will first try google.maps.LatLngBounds -// and if it's not found, our migration version will be used. -export const GoogleLatLngBounds = function (swOrLatLngBounds, ne) { - return typeof google !== "undefined" - ? new google.maps.LatLngBounds(swOrLatLngBounds, ne) - : new MigrationLatLngBounds(swOrLatLngBounds, ne); -}; + toSpan() { + const latSpan = this.#lngLatBounds.getNorth() - this.#lngLatBounds.getSouth(); + const lngSpan = this.#lngLatBounds.getEast() - this.#lngLatBounds.getWest(); + + return new MigrationLatLng(latSpan, lngSpan); + } + + toString() { + const south = this.#lngLatBounds.getSouth(); + const west = this.#lngLatBounds.getWest(); + const north = this.#lngLatBounds.getNorth(); + const east = this.#lngLatBounds.getEast(); + + return `((${south}, ${west}), (${north}, ${east}))`; + } + + // Rounded to 6 decimal places by default + toUrlValue(precision = 6) { + // Trim trailing 0's by using trick of dividing by 1 afterwards + const southDigits = this.#lngLatBounds.getSouth().toPrecision(precision); + const southTrimmed = parseFloat(southDigits) / 1; + const westDigits = this.#lngLatBounds.getWest().toPrecision(precision); + const westTrimmed = parseFloat(westDigits) / 1; + const northDigits = this.#lngLatBounds.getNorth().toPrecision(precision); + const northTrimmed = parseFloat(northDigits) / 1; + const eastDigits = this.#lngLatBounds.getEast().toPrecision(precision); + const eastTrimmed = parseFloat(eastDigits) / 1; + + return `${southTrimmed},${westTrimmed},${northTrimmed},${eastTrimmed}`; + } + + union(other) { + const bounds = new MigrationLatLngBounds(other); + + this.#lngLatBounds.extend(bounds._getBounds()); + + return this; + } + + // Internal method for migration logic that needs to access the underlying MapLibre LngLatBounds + _getBounds() { + return this.#lngLatBounds; + } +} // function that takes in a Google LatLng or LatLngLiteral and returns array containing a // longitude and latitude (valid MapLibre input), returns 'null' if 'coord' parameter // is not a Google LatLng or LatLngLiteral export const LatLngToLngLat = function (coord): [number, number] { - if (coord.lng !== undefined && coord.lat !== undefined) { - if (typeof coord.lng === "number" && typeof coord.lat === "number") { - return [coord.lng, coord.lat]; - } else if (typeof coord.lng === "function" && typeof coord.lat === "function") { - return [coord.lng(), coord.lat()]; - } + const latLng = new MigrationLatLng(coord); + const lat = latLng.lat(); + const lng = latLng.lng(); + if (isFinite(lat) && isFinite(lng)) { + return [lng, lat]; } + return null; }; diff --git a/src/index.ts b/src/index.ts index dd924c0..83a2ad5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { withAPIKey } from "@aws/amazon-location-utilities-auth-helper"; import { LocationClient } from "@aws-sdk/client-location"; import { MigrationDirectionsRenderer, MigrationDirectionsService } from "./directions"; +import { MigrationLatLng, MigrationLatLngBounds } from "./googleCommon"; import { MigrationMap } from "./maps"; import { MigrationMarker } from "./markers"; import { MigrationAutocompleteService, MigrationPlacesService } from "./places"; @@ -79,6 +80,8 @@ anyWindow.migrationInit = async function () { MigrationDirectionsService.prototype._placesService = new MigrationPlacesService(); // Replace the Google Maps classes with our migration classes + anyWindow.google.maps.LatLng = MigrationLatLng; + anyWindow.google.maps.LatLngBounds = MigrationLatLngBounds; anyWindow.google.maps.Map = MigrationMap; anyWindow.google.maps.Marker = MigrationMarker; if (anyWindow.google.maps.marker) { diff --git a/src/maps.ts b/src/maps.ts index a34c8af..7437699 100644 --- a/src/maps.ts +++ b/src/maps.ts @@ -3,13 +3,13 @@ import { CameraOptions, IControl, Map, MapOptions, NavigationControl } from "maplibre-gl"; import { - GoogleLatLng, - GoogleLatLngBounds, GoogleMapEvent, GoogleMapMouseEvent, GoogleToMaplibreControlPosition, GoogleToMaplibreEvent, LatLngToLngLat, + MigrationLatLng, + MigrationLatLngBounds, } from "./googleCommon"; /* @@ -77,7 +77,7 @@ class MigrationMap { this.#map.on(GoogleToMaplibreEvent[eventName], (mapLibreMapMouseEvent) => { const googleMapMouseEvent = { domEvent: mapLibreMapMouseEvent.originalEvent, - latLng: GoogleLatLng(mapLibreMapMouseEvent.lngLat.lat, mapLibreMapMouseEvent.lngLat.lng), + latLng: new MigrationLatLng(mapLibreMapMouseEvent.lngLat.lat, mapLibreMapMouseEvent.lngLat.lng), }; handler(googleMapMouseEvent); }); @@ -90,13 +90,19 @@ class MigrationMap { getBounds() { const bounds = this.#map.getBounds(); - return GoogleLatLngBounds(bounds.getSouthWest(), bounds.getNorthEast()); + + return new MigrationLatLngBounds({ + west: bounds.getWest(), + south: bounds.getSouth(), + east: bounds.getEast(), + north: bounds.getNorth(), + }); } getCenter() { const center = this.#map.getCenter(); - return GoogleLatLng(center?.lat, center?.lng); + return new MigrationLatLng(center?.lat, center?.lng); } getDiv() { @@ -222,17 +228,14 @@ class MigrationMap { } fitBounds(bounds, padding?) { - const northEast = bounds.getNorthEast(); - const southWest = bounds.getSouthWest(); - const bbox: [[number, number], [number, number]] = [ - [northEast.lng(), northEast.lat()], - [southWest.lng(), southWest.lat()], - ]; + // This will handle both LatLngBounds | LatLngBoundsLiteral input for us + const latLngBounds = new MigrationLatLngBounds(bounds); + if (padding !== undefined) { if (typeof padding === "number") { - this.#map.fitBounds(bbox, { padding: padding }); + this.#map.fitBounds(latLngBounds._getBounds(), { padding: padding }); } else if (typeof padding === "object") { - this.#map.fitBounds(bbox, { + this.#map.fitBounds(latLngBounds._getBounds(), { padding: { top: padding.top && typeof padding.top === "number" ? padding.top : 0, bottom: padding.bottom && typeof padding.bottom === "number" ? padding.bottom : 0, @@ -242,10 +245,10 @@ class MigrationMap { }); } else { // google does not error out on invalid padding parameter - this.#map.fitBounds(bbox); + this.#map.fitBounds(latLngBounds._getBounds()); } } else { - this.#map.fitBounds(bbox); + this.#map.fitBounds(latLngBounds._getBounds()); } } diff --git a/src/markers.ts b/src/markers.ts index e780b19..cd103d6 100644 --- a/src/markers.ts +++ b/src/markers.ts @@ -3,11 +3,11 @@ import { Marker, MarkerOptions } from "maplibre-gl"; import { - GoogleLatLng, GoogleMarkerMouseDOMEvent, GoogleMarkerMouseEvent, LatLngToLngLat, MigrationEvent, + MigrationLatLng, } from "./googleCommon"; class MigrationMarker { @@ -222,7 +222,7 @@ class MigrationMarker { getPosition() { const position = this.#marker.getLngLat(); - return GoogleLatLng(position?.lat, position?.lng); + return new MigrationLatLng(position?.lat, position?.lng); } getVisible() { diff --git a/src/places.ts b/src/places.ts index a8af645..ddda71b 100644 --- a/src/places.ts +++ b/src/places.ts @@ -11,7 +11,13 @@ import { SearchPlaceIndexForTextRequest, } from "@aws-sdk/client-location"; -import { GoogleLatLng, LatLngToLngLat, PlacesServiceStatus, QueryAutocompletePrediction } from "./googleCommon"; +import { + LatLngToLngLat, + MigrationLatLng, + MigrationLatLngBounds, + PlacesServiceStatus, + QueryAutocompletePrediction, +} from "./googleCommon"; const convertAmazonPlaceToGoogle = (placeObject, fields, includeDetailFields) => { const place = placeObject.Place; @@ -32,7 +38,7 @@ const convertAmazonPlaceToGoogle = (placeObject, fields, includeDetailFields) => if (includeAllFields || fields.includes("geometry") || fields.includes("geometry.location")) { const point = place.Geometry.Point; googlePlace["geometry"] = { - location: GoogleLatLng(point[1], point[0]), + location: new MigrationLatLng(point[1], point[0]), }; } @@ -168,18 +174,9 @@ class MigrationPlacesService { // If bounds is specified, then location bias is ignored if (bounds) { - // TODO: Change this to use GoogleLatLngBounds once MigrationLatLngBounds has - // been updated to handle all the constructor variants, which will handle converting - // either bounds from both LatLngBounds|LatLngBoundsLiteral for us - let southWest; - let northEast; - if (bounds.getSouthWest !== undefined) { - southWest = bounds.getSouthWest(); - northEast = bounds.getNorthEast(); - } else { - southWest = GoogleLatLng(bounds.south, bounds.west); - northEast = GoogleLatLng(bounds.north, bounds.east); - } + const latLngBounds = new MigrationLatLngBounds(bounds); + const southWest = latLngBounds.getSouthWest(); + const northEast = latLngBounds.getNorthEast(); input.FilterBBox = [southWest.lng(), southWest.lat(), northEast.lng(), northEast.lat()]; } else if (locationBias) { diff --git a/test/googleCommon.test.ts b/test/googleCommon.test.ts new file mode 100644 index 0000000..dfbc1c4 --- /dev/null +++ b/test/googleCommon.test.ts @@ -0,0 +1,286 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { MigrationLatLng, MigrationLatLngBounds } from "../src/googleCommon"; + +afterEach(() => { + jest.clearAllMocks(); +}); + +test("should construct LatLng with two numbers", () => { + const position = new MigrationLatLng(1, 2); + + expect(position.lat()).toStrictEqual(1); + expect(position.lng()).toStrictEqual(2); +}); + +test("should clamp latitude to -90 by default", () => { + const position = new MigrationLatLng(-100, 2); + + expect(position.lat()).toStrictEqual(-90); + expect(position.lng()).toStrictEqual(2); +}); + +test("should clamp latitude to 90 by default", () => { + const position = new MigrationLatLng(100, 2); + + expect(position.lat()).toStrictEqual(90); + expect(position.lng()).toStrictEqual(2); +}); + +test("should wrap longitude when given a value less than -180", () => { + const position = new MigrationLatLng(-100, -200); + + expect(position.lat()).toStrictEqual(-90); + expect(position.lng()).toStrictEqual(160); +}); + +test("should wrap longitude when given a value greater than 180", () => { + const position = new MigrationLatLng(110, 220); + + expect(position.lat()).toStrictEqual(90); + expect(position.lng()).toStrictEqual(-140); +}); + +test("shouldn't clamp or wrap if specified", () => { + const position = new MigrationLatLng(110, 220, true); + + expect(position.lat()).toStrictEqual(110); + expect(position.lng()).toStrictEqual(220); +}); + +test("should construct LatLng from LatLngLiteral", () => { + const position = new MigrationLatLng({ lat: 1, lng: 2 }); + + expect(position.lat()).toStrictEqual(1); + expect(position.lng()).toStrictEqual(2); +}); + +test("should construct LatLng from LatLng", () => { + const initialPosition = new MigrationLatLng(3, 4); + const position = new MigrationLatLng(initialPosition); + + expect(position.lat()).toStrictEqual(3); + expect(position.lng()).toStrictEqual(4); +}); + +test("should construct LatLng from LatLngLiteral with no clamp", () => { + const position = new MigrationLatLng({ lat: 120, lng: 230 }, true); + + expect(position.lat()).toStrictEqual(120); + expect(position.lng()).toStrictEqual(230); +}); + +test("should construct LatLng from LatLng with no clamp", () => { + const initialPosition = new MigrationLatLng(130, 240, true); + const position = new MigrationLatLng(initialPosition, true); + + expect(position.lat()).toStrictEqual(130); + expect(position.lng()).toStrictEqual(240); +}); + +test("should compare LatLng to LatLng", () => { + const p1 = new MigrationLatLng(1, 2); + const p2 = new MigrationLatLng(1, 2); + const p3 = new MigrationLatLng(1, 3); + const p4 = new MigrationLatLng(3, 2); + const p5 = new MigrationLatLng(5, 5); + + expect(p1.equals(p2)).toStrictEqual(true); + expect(p1.equals(p3)).toStrictEqual(false); + expect(p1.equals(p4)).toStrictEqual(false); + expect(p1.equals(p5)).toStrictEqual(false); + expect(p1.equals(undefined)).toStrictEqual(false); +}); + +test("should return toString in expected format for LatLng", () => { + const position = new MigrationLatLng(1, 2); + + expect(position.toString()).toStrictEqual("(1, 2)"); +}); + +test("should return toJSON in expected format for LatLng", () => { + const position = new MigrationLatLng(1, 2); + + expect(position.toJSON()).toStrictEqual({ lat: 1, lng: 2 }); +}); + +test("should return toUrlValue with decimal precision of 6 digits by default for LatLng", () => { + const position = new MigrationLatLng(0.1234567, 2); + + expect(position.toUrlValue()).toStrictEqual("0.123457,2"); +}); + +test("should return toUrlValue with specified decimal precision for LatLng", () => { + const position = new MigrationLatLng(0.1234567, 2); + + expect(position.toUrlValue(4)).toStrictEqual("0.1235,2"); +}); + +test("can construct an empty LatLngBounds", () => { + const bounds = new MigrationLatLngBounds(); + + expect(bounds.isEmpty()).toStrictEqual(true); +}); + +test("should construct LatLngBounds with two LatLngs", () => { + const sw = new MigrationLatLng(1, 2); + const ne = new MigrationLatLng(3, 4); + const bounds = new MigrationLatLngBounds(sw, ne); + + expect(bounds.getSouthWest().lat()).toStrictEqual(1); + expect(bounds.getSouthWest().lng()).toStrictEqual(2); + expect(bounds.getNorthEast().lat()).toStrictEqual(3); + expect(bounds.getNorthEast().lng()).toStrictEqual(4); +}); + +test("should construct LatLngBounds from LatLngBounds", () => { + const sw = new MigrationLatLng(1, 2); + const ne = new MigrationLatLng(3, 4); + const bounds = new MigrationLatLngBounds(sw, ne); + const anotherBounds = new MigrationLatLngBounds(bounds); + + expect(anotherBounds.getSouthWest().lat()).toStrictEqual(1); + expect(anotherBounds.getSouthWest().lng()).toStrictEqual(2); + expect(anotherBounds.getNorthEast().lat()).toStrictEqual(3); + expect(anotherBounds.getNorthEast().lng()).toStrictEqual(4); +}); + +test("should construct LatLngBounds from LatLngBoundsLiteral", () => { + const west = 1; + const south = 2; + const east = 3; + const north = 4; + const bounds = new MigrationLatLngBounds({ west, south, east, north }); + + expect(bounds.getSouthWest().lat()).toStrictEqual(south); + expect(bounds.getSouthWest().lng()).toStrictEqual(west); + expect(bounds.getNorthEast().lat()).toStrictEqual(north); + expect(bounds.getNorthEast().lng()).toStrictEqual(east); +}); + +test("should return true if point is in LatLngBounds", () => { + const sw = new MigrationLatLng(0, 0); + const ne = new MigrationLatLng(5, 5); + const bounds = new MigrationLatLngBounds(sw, ne); + + expect(bounds.contains(new MigrationLatLng(3, 3))).toStrictEqual(true); +}); + +test("should return true if bounds are equal", () => { + const sw = new MigrationLatLng(0, 0); + const ne = new MigrationLatLng(5, 5); + const bounds = new MigrationLatLngBounds(sw, ne); + const anotherBounds = new MigrationLatLngBounds(bounds); + + expect(bounds.equals(anotherBounds)).toStrictEqual(true); +}); + +test("bounds should extend to include LatLng", () => { + const sw = new MigrationLatLng(0, 0); + const ne = new MigrationLatLng(5, 5); + const bounds = new MigrationLatLngBounds(sw, ne); + + const newBounds = bounds.extend(new MigrationLatLng(10, 11)); + + expect(bounds.getSouthWest().lat()).toStrictEqual(0); + expect(bounds.getSouthWest().lng()).toStrictEqual(0); + expect(bounds.getNorthEast().lat()).toStrictEqual(10); + expect(bounds.getNorthEast().lng()).toStrictEqual(11); + + // extend should also return an updated bounds as well + expect(newBounds.getSouthWest().lat()).toStrictEqual(0); + expect(newBounds.getSouthWest().lng()).toStrictEqual(0); + expect(newBounds.getNorthEast().lat()).toStrictEqual(10); + expect(newBounds.getNorthEast().lng()).toStrictEqual(11); +}); + +test("should return center of bounds", () => { + const sw = new MigrationLatLng(0, 0); + const ne = new MigrationLatLng(5, 6); + const bounds = new MigrationLatLngBounds(sw, ne); + + const center = bounds.getCenter(); + + expect(center.lat()).toStrictEqual(2.5); + expect(center.lng()).toStrictEqual(3); +}); + +test("should return toJSON in expected format for LatLngBounds", () => { + const sw = new MigrationLatLng(0, 1); + const ne = new MigrationLatLng(2, 3); + const bounds = new MigrationLatLngBounds(sw, ne); + + const boundsLiteral = bounds.toJSON(); + + expect(boundsLiteral.south).toStrictEqual(0); + expect(boundsLiteral.west).toStrictEqual(1); + expect(boundsLiteral.north).toStrictEqual(2); + expect(boundsLiteral.east).toStrictEqual(3); +}); + +test("should calculate span of LatLngBounds", () => { + const west = 0; + const south = 1; + const east = 3; + const north = 8; + const bounds = new MigrationLatLngBounds({ west, south, east, north }); + + const span = bounds.toSpan(); + + expect(span.lat()).toStrictEqual(north - south); + expect(span.lng()).toStrictEqual(east - west); +}); + +test("should return toString in expected format for LatLngBounds", () => { + const west = 1; + const south = 2; + const east = 3; + const north = 4; + const bounds = new MigrationLatLngBounds({ west, south, east, north }); + + expect(bounds.toString()).toStrictEqual("((2, 1), (4, 3))"); +}); + +test("should return toUrlValue with decimal precision of 6 digits by default for LatLngBounds", () => { + const west = 1; + const south = 2.028348934; + const east = 3.984853201; + const north = 4.32; + const bounds = new MigrationLatLngBounds({ west, south, east, north }); + + expect(bounds.toUrlValue()).toStrictEqual("2.02835,1,4.32,3.98485"); +}); + +test("should return toUrlValue with specified decimal precision for LatLngBounds", () => { + const west = 1; + const south = 2.028348934; + const east = 3.984853201; + const north = 4.32; + const bounds = new MigrationLatLngBounds({ west, south, east, north }); + + expect(bounds.toUrlValue(3)).toStrictEqual("2.03,1,4.32,3.98"); +}); + +test("bounds should extend to include LatLngBounds", () => { + const sw = new MigrationLatLng(1, 1); + const ne = new MigrationLatLng(4, 5); + const bounds = new MigrationLatLngBounds(sw, ne); + + const otherSw = new MigrationLatLng(0, 0); + const otherNe = new MigrationLatLng(5, 5); + const otherBounds = new MigrationLatLngBounds(otherSw, otherNe); + + const newBounds = bounds.union(otherBounds); + + expect(bounds.getSouthWest().lat()).toStrictEqual(0); + expect(bounds.getSouthWest().lng()).toStrictEqual(0); + expect(bounds.getNorthEast().lat()).toStrictEqual(5); + expect(bounds.getNorthEast().lng()).toStrictEqual(5); + + // union should also return an updated bounds as well + expect(newBounds.getSouthWest().lat()).toStrictEqual(0); + expect(newBounds.getSouthWest().lng()).toStrictEqual(0); + expect(newBounds.getNorthEast().lat()).toStrictEqual(5); + expect(newBounds.getNorthEast().lng()).toStrictEqual(5); +}); diff --git a/test/maps.test.ts b/test/maps.test.ts index d4169b9..68c9c77 100644 --- a/test/maps.test.ts +++ b/test/maps.test.ts @@ -2,14 +2,66 @@ // SPDX-License-Identifier: Apache-2.0 import { MigrationMap } from "../src/maps"; -import { GoogleLatLng, GoogleLatLngBounds, MigrationControlPosition } from "../src/googleCommon"; +import { MigrationControlPosition, MigrationLatLng, MigrationLatLngBounds } from "../src/googleCommon"; // Mock maplibre because it requires a valid DOM container to create a Map // We don't need to verify maplibre itself, we just need to verify that // the values we pass to our google migration classes get transformed // correctly and our called -jest.mock("maplibre-gl"); -import { Map, MapOptions, NavigationControl } from "maplibre-gl"; +const mockAddControl = jest.fn(); +const mockRemoveControl = jest.fn(); +const mockFitBounds = jest.fn(); +const mockGetBounds = jest.fn(); +const mockGetCenter = jest.fn(); +const mockSetCenter = jest.fn(); +const mockGetContainer = jest.fn(); +const mockJumpTo = jest.fn(); +const mockOn = jest.fn(); +const mockPanBy = jest.fn(); +const mockPanTo = jest.fn(); +const mockGetBearing = jest.fn(); +const mockSetBearing = jest.fn(); +const mockGetPitch = jest.fn(); +const mockSetPitch = jest.fn(); +const mockGetZoom = jest.fn(); +const mockSetZoom = jest.fn(); +const mockSetMinZoom = jest.fn(); +const mockSetMaxZoom = jest.fn(); + +jest.mock("maplibre-gl", () => ({ + ...jest.requireActual("maplibre-gl"), + Map: jest.fn().mockImplementation(() => { + return { + addControl: mockAddControl, + removeControl: mockRemoveControl, + + fitBounds: mockFitBounds, + getBounds: mockGetBounds, + + getCenter: mockGetCenter, + setCenter: mockSetCenter, + + getContainer: mockGetContainer, + + jumpTo: mockJumpTo, + on: mockOn, + + panBy: mockPanBy, + panTo: mockPanTo, + + getBearing: mockGetBearing, + setBearing: mockSetBearing, + getPitch: mockGetPitch, + setPitch: mockSetPitch, + getZoom: mockGetZoom, + setZoom: mockSetZoom, + setMinZoom: mockSetMinZoom, + setMaxZoom: mockSetMaxZoom, + }; + }), +})); + +import { LngLatBounds, Map, MapOptions, NavigationControl } from "maplibre-gl"; const testLat = 30.268193; // Austin, TX :) const testLng = -97.7457518; @@ -47,8 +99,8 @@ test("should set migration map options", () => { expect(testMap).not.toBeNull(); expect(Map).toHaveBeenCalledTimes(1); expect(Map).toHaveBeenCalledWith(expectedMaplibreOptions); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-left"); + expect(mockAddControl).toHaveBeenCalledTimes(1); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-left"); }); test("should set migration map options with control position not available in MapLibre", () => { @@ -69,8 +121,8 @@ test("should set migration map options with control position not available in Ma expect(testMap).not.toBeNull(); expect(Map).toHaveBeenCalledTimes(1); expect(Map).toHaveBeenCalledWith(expectedMaplibreOptions); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); + expect(mockAddControl).toHaveBeenCalledTimes(1); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); }); test("should log error with invalid map option center", () => { @@ -88,18 +140,18 @@ test("should call setZoom from migration map", () => { testMap.setZoom(3); - expect(Map.prototype.setZoom).toHaveBeenCalledTimes(1); - expect(Map.prototype.setZoom).toHaveBeenCalledWith(3); + expect(mockSetZoom).toHaveBeenCalledTimes(1); + expect(mockSetZoom).toHaveBeenCalledWith(3); }); test("should call setCenter from migration map with LatLng", () => { const testMap = new MigrationMap(null, {}); - const testCenter = GoogleLatLng(1, 2); + const testCenter = new MigrationLatLng(1, 2); testMap.setCenter(testCenter); - expect(Map.prototype.setCenter).toHaveBeenCalledTimes(1); - expect(Map.prototype.setCenter).toHaveBeenCalledWith([2, 1]); + expect(mockSetCenter).toHaveBeenCalledTimes(1); + expect(mockSetCenter).toHaveBeenCalledWith([2, 1]); }); test("should call setCenter from migration map with LatLngLiteral", () => { @@ -108,8 +160,8 @@ test("should call setCenter from migration map with LatLngLiteral", () => { testMap.setCenter(testCenter); - expect(Map.prototype.setCenter).toHaveBeenCalledTimes(1); - expect(Map.prototype.setCenter).toHaveBeenCalledWith([4, 3]); + expect(mockSetCenter).toHaveBeenCalledTimes(1); + expect(mockSetCenter).toHaveBeenCalledWith([4, 3]); }); test("should call setHeading from migration map", () => { @@ -117,8 +169,8 @@ test("should call setHeading from migration map", () => { testMap.setHeading(45); - expect(Map.prototype.setBearing).toHaveBeenCalledTimes(1); - expect(Map.prototype.setBearing).toHaveBeenCalledWith(45); + expect(mockSetBearing).toHaveBeenCalledTimes(1); + expect(mockSetBearing).toHaveBeenCalledWith(45); }); test("should call setOptions from migration map", () => { @@ -137,21 +189,21 @@ test("should call setOptions from migration map", () => { }, }); - expect(Map.prototype.setCenter).toHaveBeenCalledTimes(1); - expect(Map.prototype.setCenter).toHaveBeenCalledWith([testLng, testLat]); - expect(Map.prototype.setZoom).toHaveBeenCalledTimes(1); - expect(Map.prototype.setZoom).toHaveBeenCalledWith(9); - expect(Map.prototype.setMaxZoom).toHaveBeenCalledTimes(1); - expect(Map.prototype.setMaxZoom).toHaveBeenCalledWith(18); - expect(Map.prototype.setMinZoom).toHaveBeenCalledTimes(1); - expect(Map.prototype.setMinZoom).toHaveBeenCalledWith(2); - expect(Map.prototype.setPitch).toHaveBeenCalledTimes(1); - expect(Map.prototype.setPitch).toHaveBeenCalledWith(45); - expect(Map.prototype.setBearing).toHaveBeenCalledTimes(1); - expect(Map.prototype.setBearing).toHaveBeenCalledWith(90); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(2); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-left"); + expect(mockSetCenter).toHaveBeenCalledTimes(1); + expect(mockSetCenter).toHaveBeenCalledWith([testLng, testLat]); + expect(mockSetZoom).toHaveBeenCalledTimes(1); + expect(mockSetZoom).toHaveBeenCalledWith(9); + expect(mockSetMaxZoom).toHaveBeenCalledTimes(1); + expect(mockSetMaxZoom).toHaveBeenCalledWith(18); + expect(mockSetMinZoom).toHaveBeenCalledTimes(1); + expect(mockSetMinZoom).toHaveBeenCalledWith(2); + expect(mockSetPitch).toHaveBeenCalledTimes(1); + expect(mockSetPitch).toHaveBeenCalledWith(45); + expect(mockSetBearing).toHaveBeenCalledTimes(1); + expect(mockSetBearing).toHaveBeenCalledWith(90); + expect(mockAddControl).toHaveBeenCalledTimes(2); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-left"); }); test("should call setOptions from migration map and remove NavigationControl", () => { @@ -161,10 +213,10 @@ test("should call setOptions from migration map and remove NavigationControl", ( zoomControl: false, }); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); - expect(Map.prototype.removeControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.removeControl).toHaveBeenCalledWith(expect.any(NavigationControl)); + expect(mockAddControl).toHaveBeenCalledTimes(1); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); + expect(mockRemoveControl).toHaveBeenCalledTimes(1); + expect(mockRemoveControl).toHaveBeenCalledWith(expect.any(NavigationControl)); }); test("should call setOptions from migration map and add new NavigationControl", () => { @@ -176,8 +228,8 @@ test("should call setOptions from migration map and add new NavigationControl", zoomControl: true, }); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); + expect(mockAddControl).toHaveBeenCalledTimes(1); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); }); test("should call setOptions from migration map and add new NavigationControl with zoomControlOptions", () => { @@ -189,11 +241,11 @@ test("should call setOptions from migration map and add new NavigationControl wi }, }); - expect(Map.prototype.addControl).toHaveBeenCalledTimes(2); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); - expect(Map.prototype.addControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-right"); - expect(Map.prototype.removeControl).toHaveBeenCalledTimes(1); - expect(Map.prototype.removeControl).toHaveBeenCalledWith(expect.any(NavigationControl)); + expect(mockAddControl).toHaveBeenCalledTimes(2); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "bottom-right"); + expect(mockAddControl).toHaveBeenCalledWith(expect.any(NavigationControl), "top-right"); + expect(mockRemoveControl).toHaveBeenCalledTimes(1); + expect(mockRemoveControl).toHaveBeenCalledWith(expect.any(NavigationControl)); }); test("should log error when setOptions is called with invalid center", () => { @@ -212,8 +264,8 @@ test("should call setTilt from migration map", () => { testMap.setTilt(30); - expect(Map.prototype.setPitch).toHaveBeenCalledTimes(1); - expect(Map.prototype.setPitch).toHaveBeenCalledWith(30); + expect(mockSetPitch).toHaveBeenCalledTimes(1); + expect(mockSetPitch).toHaveBeenCalledWith(30); }); test("should call get methods from migration map", () => { @@ -225,40 +277,48 @@ test("should call get methods from migration map", () => { testMap.getTilt(); testMap.getZoom(); - expect(Map.prototype.getCenter).toHaveBeenCalledTimes(1); - expect(Map.prototype.getContainer).toHaveBeenCalledTimes(1); - expect(Map.prototype.getBearing).toHaveBeenCalledTimes(1); - expect(Map.prototype.getPitch).toHaveBeenCalledTimes(1); - expect(Map.prototype.getZoom).toHaveBeenCalledTimes(1); + expect(mockGetCenter).toHaveBeenCalledTimes(1); + expect(mockGetContainer).toHaveBeenCalledTimes(1); + expect(mockGetBearing).toHaveBeenCalledTimes(1); + expect(mockGetPitch).toHaveBeenCalledTimes(1); + expect(mockGetZoom).toHaveBeenCalledTimes(1); }); test("should call getBounds from migration map", () => { // need to mock #map.getBounds() because if we do not, getBounds() will return undefined and when // we try to call bounds.getSouthWest() and bounds.getNorthEast() on undefined in the line after, // a null pointer exception is thrown and the test fails + const west = 1; + const south = 2; + const east = 3; + const north = 4; const mockMap = { - getBounds: jest.fn().mockReturnValue(GoogleLatLngBounds({}, {})), + getBounds: jest.fn().mockReturnValue(new LngLatBounds([west, south, east, north])), }; const testMap = new MigrationMap(null, {}); testMap._setMap(mockMap); - testMap.getBounds(); + const bounds = testMap.getBounds(); expect(mockMap.getBounds).toHaveBeenCalledTimes(1); + expect(bounds.getSouthWest().lat()).toStrictEqual(south); + expect(bounds.getSouthWest().lng()).toStrictEqual(west); + expect(bounds.getNorthEast().lat()).toStrictEqual(north); + expect(bounds.getNorthEast().lng()).toStrictEqual(east); }); test("should call moveCamera from migration map", () => { const testMap = new MigrationMap(null, {}); testMap.moveCamera({ - center: GoogleLatLng(testLat, testLng), + center: new MigrationLatLng(testLat, testLng), zoom: 16, heading: 90, tilt: 45, }); - expect(Map.prototype.jumpTo).toHaveBeenCalledTimes(1); - expect(Map.prototype.jumpTo).toHaveBeenCalledWith({ + expect(mockJumpTo).toHaveBeenCalledTimes(1); + expect(mockJumpTo).toHaveBeenCalledWith({ center: [testLng, testLat], zoom: 16, bearing: 90, @@ -285,8 +345,8 @@ test("should call panBy from migration map", () => { testMap.panBy(50, 60); - expect(Map.prototype.panBy).toHaveBeenCalledTimes(1); - expect(Map.prototype.panBy).toHaveBeenCalledWith([50, 60]); + expect(mockPanBy).toHaveBeenCalledTimes(1); + expect(mockPanBy).toHaveBeenCalledWith([50, 60]); }); test("should call panTo from migration map", () => { @@ -294,99 +354,75 @@ test("should call panTo from migration map", () => { testMap.panTo({ lat: testLat, lng: testLng }); - expect(Map.prototype.panTo).toHaveBeenCalledTimes(1); - expect(Map.prototype.panTo).toHaveBeenCalledWith([testLng, testLat]); + expect(mockPanTo).toHaveBeenCalledTimes(1); + expect(mockPanTo).toHaveBeenCalledWith([testLng, testLat]); }); test("should call fitBounds from migration map", () => { const testMap = new MigrationMap(null, {}); - const testSouthWest = GoogleLatLng(1, 2); - const testNorthEast = GoogleLatLng(3, 4); - const testBounds = GoogleLatLngBounds(testSouthWest, testNorthEast); + const testSouthWest = new MigrationLatLng(1, 2); + const testNorthEast = new MigrationLatLng(3, 4); + const testBounds = new MigrationLatLngBounds(testSouthWest, testNorthEast); testMap.fitBounds(testBounds); - expect(Map.prototype.fitBounds).toHaveBeenCalledTimes(1); - expect(Map.prototype.fitBounds).toHaveBeenCalledWith([ - [testNorthEast.lng(), testNorthEast.lat()], - [testSouthWest.lng(), testSouthWest.lat()], - ]); + expect(mockFitBounds).toHaveBeenCalledTimes(1); + expect(mockFitBounds).toHaveBeenCalledWith(testBounds._getBounds()); }); test("should call fitBounds from migration map with valid padding", () => { const testMap = new MigrationMap(null, {}); - const testSouthWest = GoogleLatLng(1, 2); - const testNorthEast = GoogleLatLng(3, 4); - const testBounds = GoogleLatLngBounds(testSouthWest, testNorthEast); + const testSouthWest = new MigrationLatLng(1, 2); + const testNorthEast = new MigrationLatLng(3, 4); + const testBounds = new MigrationLatLngBounds(testSouthWest, testNorthEast); testMap.fitBounds(testBounds, 100); - expect(Map.prototype.fitBounds).toHaveBeenCalledTimes(1); - expect(Map.prototype.fitBounds).toHaveBeenCalledWith( - [ - [testNorthEast.lng(), testNorthEast.lat()], - [testSouthWest.lng(), testSouthWest.lat()], - ], - { - padding: 100, - }, - ); + expect(mockFitBounds).toHaveBeenCalledTimes(1); + expect(mockFitBounds).toHaveBeenCalledWith(testBounds._getBounds(), { + padding: 100, + }); }); test("should call fitBounds from migration map with valid padding specifying all four sides", () => { const testMap = new MigrationMap(null, {}); - const testSouthWest = GoogleLatLng(1, 2); - const testNorthEast = GoogleLatLng(3, 4); - const testBounds = GoogleLatLngBounds(testSouthWest, testNorthEast); + const testSouthWest = new MigrationLatLng(1, 2); + const testNorthEast = new MigrationLatLng(3, 4); + const testBounds = new MigrationLatLngBounds(testSouthWest, testNorthEast); testMap.fitBounds(testBounds, { left: 10, right: 20, top: 30, bottom: 40 }); - expect(Map.prototype.fitBounds).toHaveBeenCalledTimes(1); - expect(Map.prototype.fitBounds).toHaveBeenCalledWith( - [ - [testNorthEast.lng(), testNorthEast.lat()], - [testSouthWest.lng(), testSouthWest.lat()], - ], - { - padding: { left: 10, right: 20, top: 30, bottom: 40 }, - }, - ); + expect(mockFitBounds).toHaveBeenCalledTimes(1); + expect(mockFitBounds).toHaveBeenCalledWith(testBounds._getBounds(), { + padding: { left: 10, right: 20, top: 30, bottom: 40 }, + }); }); test("should call fitBounds from migration map with valid padding specifying no sides", () => { const testMap = new MigrationMap(null, {}); - const testSouthWest = GoogleLatLng(1, 2); - const testNorthEast = GoogleLatLng(3, 4); - const testBounds = GoogleLatLngBounds(testSouthWest, testNorthEast); + const testSouthWest = new MigrationLatLng(1, 2); + const testNorthEast = new MigrationLatLng(3, 4); + const testBounds = new MigrationLatLngBounds(testSouthWest, testNorthEast); testMap.fitBounds(testBounds, {}); - expect(Map.prototype.fitBounds).toHaveBeenCalledTimes(1); - expect(Map.prototype.fitBounds).toHaveBeenCalledWith( - [ - [testNorthEast.lng(), testNorthEast.lat()], - [testSouthWest.lng(), testSouthWest.lat()], - ], - { - padding: { left: 0, right: 0, top: 0, bottom: 0 }, - }, - ); + expect(mockFitBounds).toHaveBeenCalledTimes(1); + expect(mockFitBounds).toHaveBeenCalledWith(testBounds._getBounds(), { + padding: { left: 0, right: 0, top: 0, bottom: 0 }, + }); }); test("should call fitBounds from migration map with invalid padding", () => { const testMap = new MigrationMap(null, {}); - const testSouthWest = GoogleLatLng(1, 2); - const testNorthEast = GoogleLatLng(3, 4); - const testBounds = GoogleLatLngBounds(testSouthWest, testNorthEast); + const testSouthWest = new MigrationLatLng(1, 2); + const testNorthEast = new MigrationLatLng(3, 4); + const testBounds = new MigrationLatLngBounds(testSouthWest, testNorthEast); testMap.fitBounds(testBounds, "bad bounds"); - expect(Map.prototype.fitBounds).toHaveBeenCalledTimes(1); + expect(mockFitBounds).toHaveBeenCalledTimes(1); // still calls fitBounds, but with no padding - expect(Map.prototype.fitBounds).toHaveBeenCalledWith([ - [testNorthEast.lng(), testNorthEast.lat()], - [testSouthWest.lng(), testSouthWest.lat()], - ]); + expect(mockFitBounds).toHaveBeenCalledWith(testBounds._getBounds()); }); test("should call addListener from migration map", () => { @@ -405,19 +441,19 @@ test("should call addListener from migration map", () => { testMap.addListener("dragend", () => {}); testMap.addListener("dragstart", () => {}); - expect(Map.prototype.on).toHaveBeenCalledTimes(12); - expect(Map.prototype.on).toHaveBeenCalledWith("click", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("dblclick", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("contextmenu", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("mousemove", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("mouseout", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("mouseover", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("load", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("pitch", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("zoom", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("drag", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("dragend", expect.any(Function)); - expect(Map.prototype.on).toHaveBeenCalledWith("dragstart", expect.any(Function)); + expect(mockOn).toHaveBeenCalledTimes(12); + expect(mockOn).toHaveBeenCalledWith("click", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("dblclick", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("contextmenu", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("mousemove", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("mouseout", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("mouseover", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("load", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("pitch", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("zoom", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("drag", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("dragend", expect.any(Function)); + expect(mockOn).toHaveBeenCalledWith("dragstart", expect.any(Function)); }); test("should call handler with translated MapMouseEvent after click", () => { diff --git a/test/markers.test.ts b/test/markers.test.ts index 7259c04..c10408a 100644 --- a/test/markers.test.ts +++ b/test/markers.test.ts @@ -3,7 +3,7 @@ import { MigrationMap } from "../src/maps"; import { MigrationMarker } from "../src/markers"; -import { GoogleLatLng } from "../src/googleCommon"; +import { MigrationLatLng } from "../src/googleCommon"; // Mock maplibre because it requires a valid DOM container to create a Map // We don't need to verify maplibre itself, we just need to verify that @@ -371,7 +371,7 @@ test("should call handler with translated MouseEvent after drag", () => { // mock marker so that we can mock on so that we can mock drag const mockMarker = { on: jest.fn(), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); @@ -407,7 +407,7 @@ test("should call handler with translated MouseEvent after dragstart", () => { // mock marker so that we can mock on so that we can mock dragstart const mockMarker = { on: jest.fn(), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); @@ -443,7 +443,7 @@ test("should call handler with translated MouseEvent after dragend", () => { // mock marker so that we can mock on so that we can mock dragend const mockMarker = { on: jest.fn(), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); @@ -484,7 +484,7 @@ test("should call handler with translated MouseEvent after click", () => { // mock marker to return mockElement when getElement is called const mockMarker = { getElement: jest.fn().mockReturnValue(mockElement), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); @@ -527,7 +527,7 @@ test("should call handler with translated MouseEvent after dblclick", () => { // mock marker to return mockElement when getElement is called const mockMarker = { getElement: jest.fn().mockReturnValue(mockElement), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); @@ -570,7 +570,7 @@ test("should call handler with translated MouseEvent after contextmenu", () => { // mock marker to return mockElement when getElement is called const mockMarker = { getElement: jest.fn().mockReturnValue(mockElement), - getLngLat: jest.fn().mockReturnValue(GoogleLatLng(1, 2)), + getLngLat: jest.fn().mockReturnValue(new MigrationLatLng(1, 2)), }; const migrationMarker = new MigrationMarker({}); migrationMarker._setMarker(mockMarker); diff --git a/test/places.test.ts b/test/places.test.ts index cbd133d..a11b3f0 100644 --- a/test/places.test.ts +++ b/test/places.test.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { MigrationPlacesService } from "../src/places"; -import { GoogleLatLng, GoogleLatLngBounds, PlacesServiceStatus } from "../src/googleCommon"; +import { MigrationLatLng, MigrationLatLngBounds, PlacesServiceStatus } from "../src/googleCommon"; // Spy on console.error so we can verify it gets called in error cases jest.spyOn(console, "error").mockImplementation(() => {}); @@ -257,8 +257,8 @@ test("textSearch should only use bounds if location was also specified", (done) const west = 3; const request = { query: "cool places in austin", - bounds: GoogleLatLngBounds(GoogleLatLng(south, west), GoogleLatLng(east, north)), - location: GoogleLatLng(4, 5), + bounds: new MigrationLatLngBounds(new MigrationLatLng(south, west), new MigrationLatLng(east, north)), + location: new MigrationLatLng(4, 5), }; placesService.textSearch(request, (results, status) => { @@ -312,7 +312,7 @@ test("textSearch should accept bounds as a literal", (done) => { test("textSearch should accept location bias if there is no bounds specified", (done) => { const request = { query: "cool places in austin", - location: GoogleLatLng(testLat, testLng), + location: new MigrationLatLng(testLat, testLng), }; placesService.textSearch(request, (results, status) => { @@ -337,7 +337,7 @@ test("textSearch should accept location bias if there is no bounds specified", ( test("textSearch should accept language", (done) => { const request = { query: "cool places in austin", - location: GoogleLatLng(testLat, testLng), + location: new MigrationLatLng(testLat, testLng), language: "en", }; @@ -363,7 +363,7 @@ test("textSearch should accept language", (done) => { test("textSearch should convert region to countries filter", (done) => { const request = { query: "cool places in austin", - location: GoogleLatLng(testLat, testLng), + location: new MigrationLatLng(testLat, testLng), region: "us", };