DigitalConsumer.ISS/src/iss-components/google-map/google-map.spec.js
2026-01-16 17:40:16 -05:00

573 lines
20 KiB
JavaScript

import { shallowMount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import googleMap from '@/iss-components/google-map/google-map.vue';
// Supporting Files
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
const mockFitBounds = jest.fn();
const mockMapInstance = {
fitBounds: mockFitBounds
};
const mockMap = jest.fn(() => mockMapInstance);
const mockGeocode = jest.fn();
const mockGeocoderInstance1 = {
geocode: mockGeocode
};
const mockGeocoderInstance2 = {
geocode: mockGeocode
};
const mockGeocoder = jest.fn(() => (mockGeocoderInstance1));
const mockAdvancedMarkerInstance = {
addListener: jest.fn()
};
const mockAdvancedMarkerElement = jest.fn(() => (mockAdvancedMarkerInstance));
const mockPinElement = jest.fn();
const mockInfoWindow = jest.fn();
const mockImportLibrary = jest.fn().mockImplementation(() => (Promise.resolve({
Map: mockMap,
Geocoder: mockGeocoder,
AdvancedMarkerElement: mockAdvancedMarkerElement,
PinElement: mockPinElement
})));
const mockExtend = jest.fn();
const mockLatLngBound = { extend: mockExtend };
const setupGoogleMock = () => {
global.window.google = {
maps: {
importLibrary: mockImportLibrary,
LatLngBounds: jest.fn(() => (mockLatLngBound)),
InfoWindow: mockInfoWindow
}
};
};
beforeEach(() => {
// clear constructors
mockMap.mockClear();
mockGeocoder.mockClear();
mockAdvancedMarkerElement.mockClear();
mockPinElement.mockClear();
// clear methods
mockExtend.mockClear();
mockGeocode.mockClear();
mockFitBounds.mockClear();
setupGoogleMock();
});
function getMountedComponent(mainInitialState = {}, initialData = {}, propsData = {}) {
const mountOptions = getMountOptions({
router: {
navigate: jest.fn()
}
});
const testingPinia = createTestingPinia({
initialState: {
main: mainInitialState
}
});
useMainStore(testingPinia);
mountOptions.global.plugins = [testingPinia];
mountOptions.data = () => (initialData);
mountOptions.propsData = propsData;
const wrapper = shallowMount(googleMap, mountOptions);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {});
wrapper.vm.setCmsContent = jest.fn();
return { wrapper };
}
async function awaitingSetupTicks(wrapper) {
for (let i = 0; i < 11; i++) {
// eslint-disable-next-line no-await-in-loop
await wrapper.vm.$nextTick();
}
}
describe('Google Map', () => {
describe('should render', () => {
test('expected initial data', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
// Assert
expect(wrapper.vm.$data).toMatchSnapshot();
});
test('map', async () => {
// Arrange
const mapReference = '#map';
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
// Act
const map = wrapper.find(mapReference);
// Assert
expect(map.exists()).toBeTruthy();
});
});
test.each([
['13001', '13001 USA'],
['9', '9 USA'],
['', ' USA'],
[null, ' USA']
])(
'given that zipcode set to "%p", computed zipCodeAddress should return "%p"',
async (zipCode, expected) => {
// Arrange
const { wrapper } = getMountedComponent({}, {}, { zipCode });
await awaitingSetupTicks(wrapper);
// Act
const result = wrapper.vm.zipCodeAddress;
// Assert
expect(result).toBe(expected);
}
);
test('before mount addresses creates map with expected markers', async () => {
// Arrange
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
const zipCode = '00882';
const [address1, address2, position1, position2] = ['a1', 'a2', 'p1', 'p2'];
const markers = [
{
fullAddress: address1
},
{
fullAddress: address2
}];
mockGeocode.mockImplementation((obj) => {
let result = {};
if (obj.address.includes(zipCode)) {
result = zipCodeGeocodeResult;
} else if (obj.address === address1) {
result = {
results: [
{ geometry: { location: position1 } }
]
};
} else if (obj.address === address2) {
result = {
results: [
{ geometry: { location: position2 } }
]
};
}
return Promise.resolve(result);
});
const propsData = { markers, zipCode };
const { wrapper } = getMountedComponent({}, {}, propsData);
await awaitingSetupTicks(wrapper);
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position1
}));
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position2
}));
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(4);
expect(mockFitBounds).toBeCalledTimes(1);
expect(mockGetCenter).toBeCalledTimes(1);
expect(mockNorthEast).toBeCalledTimes(1);
expect(mockSouthWest).toBeCalledTimes(1);
});
test('watch addresses creates map with expected markers', async () => {
// Arrange
const zipCode = '00882';
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
const [address1, address2, position1, position2] = ['a1', 'a2', 'p1', 'p2'];
mockGeocode.mockImplementation((obj) => {
let result = {};
if (obj.address.includes(zipCode)) {
result = zipCodeGeocodeResult;
} else if (obj.address === address1) {
result = {
results: [
{ geometry: { location: position1 } }
]
};
} else if (obj.address === address2) {
result = {
results: [
{ geometry: { location: position2 } }
]
};
}
return Promise.resolve(result);
});
const markers = [
{
fullAddress: address1
},
{
fullAddress: address2
}];
// Act
const { wrapper } = getMountedComponent({}, { zipCode });
await awaitingSetupTicks(wrapper);
await wrapper.vm.$options.watch.markers.call(wrapper.vm, markers);
await awaitingSetupTicks(wrapper);
// Assert
expect(mockMap).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position1
}));
expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({
map: expect.anything(),
position: position2
}));
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(6);
expect(mockFitBounds).toBeCalledTimes(2);
expect(mockGetCenter).toBeCalledTimes(2);
expect(mockNorthEast).toBeCalledTimes(2);
expect(mockSouthWest).toBeCalledTimes(2);
});
describe('method', () => {
test('getMap calls map constructor with expected parameters', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const center = { lat: 90, lng: -5 };
// Act
const result = await wrapper.vm.getMap(center);
// Assert
expect(mockMap).toHaveBeenCalledWith(
null,
expect.objectContaining({ center, mapId: 'map_id', mapTypeControl: false })
);
expect(result).toBe(mockMapInstance);
});
test.each([
[undefined, 0],
[null, 0],
[[], 0],
[['loc1'], 1],
[['loc1', 'loc1', 'loc1', 'loc1'], 4]
])(
'given parameter %p, getBounds calls extend %p time(s)',
async (locations, numberOfExtendCallsPostMount) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
mockGeocode.mockImplementation(() => {
const result = {
results: [
{ geometry: { location: 'p1' } }
]
};
return Promise.resolve(result);
});
// Act
const result = wrapper.vm.getBounds(locations);
// Assert
expect(mockExtend).toHaveBeenCalledTimes(numberOfExtendCallsPostMount);
expect(result).toBe(mockLatLngBound);
}
);
test.each([
[1, null, mockGeocoderInstance1],
[1, undefined, mockGeocoderInstance1],
[0, mockGeocoderInstance2, mockGeocoderInstance2]
])(
'setGeocoder calls Geocoder constructor %p time(s) when original geocoder %p and geocoder set to %p',
async (numberOfCalls, originalGeocoder, newGeocoder) => {
// Arrange
const { wrapper } = getMountedComponent({}, { geocoder: originalGeocoder }, {});
await awaitingSetupTicks(wrapper);
// Act
await wrapper.vm.setGeocoder();
await wrapper.vm.$nextTick();
// Assert
expect(mockGeocoder).toHaveBeenCalledTimes(numberOfCalls);
expect(wrapper.vm.geocoder).toEqual(newGeocoder);
}
);
test.each([
[null, 0],
[undefined, 0],
[[], 0],
[['p1'], 1],
[['p1', 'p2', 'p3'], 3]
])(
'addMarkersToMap given positions argument %p, creates AdvancedMarkerElement %p time(s)',
async (positions, numberOfCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const map = jest.fn();
// Act
await wrapper.vm.addMarkersToMap(map, positions);
// Assert
expect(mockAdvancedMarkerElement).toHaveBeenCalledTimes(numberOfCalls);
}
);
describe('getLocationsFromAddresses', () => {
test.each([
[null, 0],
[undefined, 0],
[[], 0],
[['a1'], 1],
[['a1', 'a2', 'a3'], 3]
])(
'given address(es) %p calls geocode %p times',
async (addresses, numberOfCallsPostMount) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const numberOfCallsFromMount = 1;
expect(mockGeocode).toHaveBeenCalledTimes(numberOfCallsFromMount);
// Act
await wrapper.vm.getLocationsFromAddresses(addresses?.map((a) => ({ fullAddress: a })));
await wrapper.vm.$nextTick();
// Assert
expect(mockGeocode).toHaveBeenCalledTimes(numberOfCallsFromMount + numberOfCallsPostMount);
expect(wrapper.vm.geocoder).not.toBeNull();
}
);
test('only returns geocode results that are not null', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const addresses = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8'].map((a) => ({ fullAddress: a }));
mockGeocode.mockImplementationOnce(() => null)
.mockImplementationOnce(() => ({ results: null }))
.mockImplementationOnce(() => ({ results: [] }))
.mockImplementationOnce(() => ({ results: [{}] }))
.mockImplementationOnce(() => (
{ results: [{ geometry: undefined }] }
))
.mockImplementationOnce(() => (
{ results: [{ geometry: {} }] }
))
.mockImplementationOnce(() => (
{
results: [
{ geometry: { location: 'some value' } }
]
}
))
.mockImplementationOnce(() => (
{
results: [
{ geometry: { location: 'some other value' } }
]
}
));
const expectedResult = ['some value', 'some other value'];
// Act
const result = (await wrapper.vm.getLocationsFromAddresses(addresses)).map((a) => a.position);
// Assert
expect(result.length).toBe(2);
expect(result).toEqual(expectedResult);
});
});
describe('getBoundsFromAddress', () => {
test('returns expected when geocode returns valid result', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const address = 'random address';
const expectedResult = 'bounds returned';
const geocodeResult = {
results: [
{
geometry: { bounds: expectedResult }
}
]
};
mockGeocode.mockImplementationOnce(() => Promise.resolve(geocodeResult));
// Act
const result = await wrapper.vm.getBoundsFromAddress(address);
// Assert
expect(result).toBe(expectedResult);
expect(wrapper.vm.geocoder).not.toBeNull();
});
test('returns null when geocode returns invalid result', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await awaitingSetupTicks(wrapper);
const address = 'random address';
mockGeocode.mockImplementationOnce(() => Promise.resolve({}));
// Act
const result = await wrapper.vm.getBoundsFromAddress(address);
// Assert
expect(result).toBeNull();
expect(wrapper.vm.geocoder).not.toBeNull();
});
});
describe('createMapWithMarkersForAddresses', () => {
test('calls expected when addresses provided', async () => {
// Arrange
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
const shopGeocodeResult = {
results: [
{ geometry: { location: 'valid location' } }
]
};
const zipCode = '00882';
mockGeocode.mockImplementation((obj) =>
Promise.resolve(obj.address.includes(zipCode)
? zipCodeGeocodeResult
: shopGeocodeResult));
const addresses = [{ fullAddress: 'a1' }, { fullAddress: 'a2' }];
const { wrapper } = getMountedComponent({}, { zipCode });
await awaitingSetupTicks(wrapper);
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
await awaitingSetupTicks(wrapper);
// Assert
expect(mockMap).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(6);
expect(mockFitBounds).toBeCalledTimes(2);
expect(mockGetCenter).toBeCalledTimes(2);
expect(mockNorthEast).toBeCalledTimes(2);
expect(mockSouthWest).toBeCalledTimes(2);
});
test.each([null, undefined, []])(
'calls expected when addresses argument is "%p"',
async (addresses) => {
// Arrange
const mockGetCenter = jest.fn();
const mockNorthEast = jest.fn();
const mockSouthWest = jest.fn();
const zipCodeGeocodeResult = {
results: [
{
geometry: {
bounds:
{
getCenter: mockGetCenter,
getNorthEast: mockNorthEast,
getSouthWest: mockSouthWest
}
}
}
]
};
mockGeocode.mockImplementation(() => Promise.resolve(zipCodeGeocodeResult));
const zipCode = '00882';
const { wrapper } = getMountedComponent({}, { zipCode });
await awaitingSetupTicks(wrapper);
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
await awaitingSetupTicks(wrapper);
// Assert
expect(mockMap).toBeCalledTimes(2);
expect(mockAdvancedMarkerElement).toBeCalledTimes(0);
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(4);
expect(mockFitBounds).toBeCalledTimes(2);
expect(mockGetCenter).toBeCalledTimes(2);
expect(mockNorthEast).toBeCalledTimes(2);
expect(mockSouthWest).toBeCalledTimes(2);
}
);
});
});
});