Finishing unit tests

This commit is contained in:
Michaela Brydon 2023-11-15 16:25:27 -05:00
parent 612a2f8b7b
commit 352f3acb43
4 changed files with 418 additions and 11 deletions

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Google Maps should render expected initial data 1`] = `
exports[`Google Map should render expected initial data 1`] = `
Object {
"country": "USA",
"geocoder": null,

View file

@ -1,7 +1,53 @@
import { shallowMount } from '@vue/test-utils';
import googleMap from '@/iss-components/google-map/google-map.vue';
describe('Google Maps', () => {
const mockFitBounds = jest.fn();
const mockMapInstance = {
fitBounds: mockFitBounds
};
const mockMap = jest.fn(() => mockMapInstance);
const mockGeocode = jest.fn();
const mockGeocoderInstance = {
geocode: mockGeocode
};
const mockGeocoder = jest.fn(() => (mockGeocoderInstance));
const mockAdvancedMarkerElement = jest.fn();
const mockImportLibrary = jest.fn().mockImplementation(() => (Promise.resolve({
Map: mockMap,
Geocoder: mockGeocoder,
AdvancedMarkerElement: mockAdvancedMarkerElement
})));
const mockExtend = jest.fn();
const mockLatLngBound = { extend: mockExtend };
const setupGoogleMock = () => {
global.window.google = {
maps: {
importLibrary: mockImportLibrary,
LatLngBounds: jest.fn(() => (mockLatLngBound))
}
};
};
beforeEach(() => {
// clear constructors
mockMap.mockClear();
mockGeocoder.mockClear();
mockAdvancedMarkerElement.mockClear();
// clear methods
mockExtend.mockClear();
mockGeocode.mockClear();
mockFitBounds.mockClear();
setupGoogleMock();
});
describe('Google Map', () => {
describe('should render', () => {
test('expected initial data', async () => {
// Arrange
@ -15,7 +61,6 @@ describe('Google Maps', () => {
// Arrange
const mapReference = '#map';
const wrapper = shallowMount(googleMap, {});
// await wrapper.vm.$nextTick();
// Act
const map = wrapper.find(mapReference);
@ -24,4 +69,360 @@ describe('Google Maps', () => {
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 = shallowMount(googleMap, {});
await wrapper.setData({ zipCode });
// Act
const result = wrapper.vm.zipCodeAddress;
// Assert
expect(result).toBe(expected);
}
);
test('watch addresses creates map with expected markers', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
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 newAddresses = [address1, address2];
// Act
await wrapper.vm.$options.watch.addresses.call(wrapper.vm, newAddresses);
for (let i = 0; i < 11; i++) {
// eslint-disable-next-line no-await-in-loop
await wrapper.vm.$nextTick();
}
// 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);
});
describe('method', () => {
test('getMap calls map constructor with expected parameters', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
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)',
(locations, numberOfExtendCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
// Act
const result = wrapper.vm.getBounds(locations);
// Assert
expect(mockExtend).toHaveBeenCalledTimes(numberOfExtendCalls);
expect(result).toBe(mockLatLngBound);
}
);
test.each([
[1, null, mockGeocoderInstance],
[1, undefined, mockGeocoderInstance],
[0, {}, {}],
[0, { value: 'any value' }, { value: 'any value' }]
])(
'setGeocoder calls Geocoder constructor %p time(s) when original geocoder %p and geocoder set to %p',
async (numberOfCalls, originalGeocoder, newGeocoder) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
await wrapper.setData({ geocoder: originalGeocoder });
// Act
await wrapper.vm.setGeocoder();
// 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, {});
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, numberOfCalls) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
// Act
await wrapper.vm.getLocationsFromAddresses(addresses);
// Assert
expect(mockGeocode).toHaveBeenCalledTimes(numberOfCalls);
expect(wrapper.vm.geocoder).not.toBeNull();
}
);
test('only returns geocode results that are not null', async () => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const addresses = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8'];
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);
// 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, {});
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, {});
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 wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
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' } }
]
};
mockGeocode.mockImplementation((obj) =>
Promise.resolve(obj.address.includes(zipCode)
? zipCodeGeocodeResult
: shopGeocodeResult));
const addresses = ['a1', 'a2'];
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(2);
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.each([null, undefined, []])(
'calls expected when addresses argument is "%p"',
async (addresses) => {
// Arrange
const wrapper = shallowMount(googleMap, {});
const zipCode = '00882';
await wrapper.setData({ zipCode });
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));
// Act
await wrapper.vm.createMapWithMarkersForAddresses(addresses);
// Assert
expect(mockMap).toBeCalledTimes(1);
expect(mockAdvancedMarkerElement).toBeCalledTimes(0);
expect(mockGeocoder).toBeCalledTimes(1);
expect(mockExtend).toBeCalledTimes(2);
expect(mockFitBounds).toBeCalledTimes(1);
expect(mockGetCenter).toBeCalledTimes(1);
expect(mockNorthEast).toBeCalledTimes(1);
expect(mockSouthWest).toBeCalledTimes(1);
}
);
});
});
});

View file

@ -7,12 +7,10 @@
<script>
export default {
name: 'google-map',
components: { },
props: {
addresses: Array,
zipCode: String
},
emits: [],
data() {
return {
country: 'USA',
@ -21,7 +19,7 @@ export default {
},
computed: {
zipCodeAddress() {
return `${this.zipCode} ${this.country}`;
return `${this.zipCode ?? ''} ${this.country}`;
}
},
watch: {
@ -40,7 +38,7 @@ export default {
},
getBounds(locations) {
const bounds = new window.google.maps.LatLngBounds();
locations.forEach((location) => bounds.extend(location));
locations?.forEach((location) => bounds.extend(location));
return bounds;
},
async setGeocoder() {
@ -51,19 +49,25 @@ export default {
},
async addMarkersToMap(map, positions) {
const { AdvancedMarkerElement } = await window.google.maps.importLibrary('marker');
positions.forEach((position) => new AdvancedMarkerElement({ map, position }));
positions?.forEach((position) => new AdvancedMarkerElement({ map, position }));
},
async getLocationsFromAddresses(addresses) {
await this.setGeocoder();
const geocodeShopAddressPromises = addresses?.map((address) => this.geocoder.geocode({ address })) ?? [];
const geocodeShopAddressResults = await Promise.all(geocodeShopAddressPromises);
return geocodeShopAddressResults.map((result) => result.results[0].geometry.location);
return geocodeShopAddressResults.map((result) =>
(result?.results?.length > 0
? result.results[0].geometry?.location
: null))
.filter((loc) => loc != null);
},
async getBoundsFromAddress(address) {
await this.setGeocoder();
const result = await this.geocoder.geocode({ address });
return result.results[0].geometry.bounds;
return result?.results?.length > 0
? result.results[0].geometry?.bounds
: null;
},
async createMapWithMarkersForAddresses(addresses) {
const shopPositions = await this.getLocationsFromAddresses(addresses);

View file

@ -1,5 +1,6 @@
<template>
<div>
<div class="page-container-grouped-styles">
<div class="fade-on-route-transition">
<siteHeader
ref="siteHeader"
:cmsWidgetName="widget.siteHeader" />
@ -97,6 +98,7 @@
</div>
</Form>
</div>
</div>
</template>
<script>
// Components