Skip to content

Commit

Permalink
chore: clean up testing fakes
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 577297045
  • Loading branch information
awmack authored and copybara-github committed Oct 30, 2023
1 parent c04dd5e commit 219bb47
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 68 deletions.
10 changes: 10 additions & 0 deletions src/base/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export type StringFunction = (...args: any[]) => string;
*/
export declare interface StringLiterals extends
Record<string, string|StringFunction> {
BACK_BUTTON: string;
LOCATOR_ALL_LOCATIONS: string;
LOCATOR_LIST_HEADER: string;
LOCATOR_SEARCH_PROMPT: string;
LOCATOR_VIEW_DETAILS: string;
PLACE_CLEAR_ARIA_LABEL: string;
PLACE_CLOSED: string;
PLACE_CLOSED_PERMANENTLY: string;
Expand Down Expand Up @@ -51,6 +56,11 @@ export declare interface StringLiterals extends

/** String literals in the `en-US` locale. */
export const STRING_LITERALS_EN_US: StringLiterals = Object.freeze({
'BACK_BUTTON': 'Back',
'LOCATOR_ALL_LOCATIONS': 'All locations',
'LOCATOR_LIST_HEADER': 'Find a location near you',
'LOCATOR_SEARCH_PROMPT': 'Enter your address or zip code',
'LOCATOR_VIEW_DETAILS': 'View details',
'PLACE_CLEAR_ARIA_LABEL': 'Clear',
'PLACE_CLOSED': 'Closed',
'PLACE_CLOSED_PERMANENTLY': 'Permanently closed',
Expand Down
60 changes: 33 additions & 27 deletions src/place_picker/place_picker_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const FAKE_PLACE_FROM_QUERY = makeFakePlace({

describe('PlacePicker', () => {
const env = new Environment();
let fakeAutocomplete: jasmine.SpyObj<google.maps.places.Autocomplete>;

beforeAll(() => {
env.defineFakeMapElement();
Expand All @@ -57,13 +56,6 @@ describe('PlacePicker', () => {
const fakeCircle = jasmine.createSpyObj('Circle', ['getBounds']);
env.fakeGoogleMapsHarness!.libraries['maps'].Circle =
jasmine.createSpy().and.returnValue(fakeCircle);

// Create a fake Autocomplete class with test-specific logic.
fakeAutocomplete = jasmine.createSpyObj<google.maps.places.Autocomplete>(
'Autocomplete',
['addListener', 'bindTo', 'getBounds', 'getPlace', 'setOptions']);
spyOn(env.fakeGoogleMapsHarness!, 'autocompleteConstructor')
.and.returnValue(fakeAutocomplete);
});

async function prepareState(template?: TemplateResult) {
Expand Down Expand Up @@ -101,6 +93,8 @@ describe('PlacePicker', () => {
});

it('initializes Autocomplete widget with minimum configs', async () => {
spyOn(env.fakeGoogleMapsHarness!, 'autocompleteConstructor')
.and.callThrough();
const {picker, input, searchButton, clearButton} = await prepareState();

expect(env.fakeGoogleMapsHarness!.autocompleteConstructor)
Expand All @@ -117,6 +111,9 @@ describe('PlacePicker', () => {
});

it(`initializes Autocomplete widget based on attributes`, async () => {
spyOn(env.fakeGoogleMapsHarness!, 'autocompleteConstructor')
.and.callThrough();

// The call to `.Circle.and.exec()` grabs a reference to the Circle
// spy object without recording a call to the constructor spy (e.g.
// `.Circle()`)
Expand Down Expand Up @@ -160,13 +157,14 @@ describe('PlacePicker', () => {
picker.type = 'restaurant';
await env.waitForStability();

expect(fakeAutocomplete.setOptions).toHaveBeenCalledOnceWith({
bounds: FAKE_BOUNDS,
componentRestrictions: {country: ['uk']},
fields: [...PLACE_RESULT_DATA_FIELDS],
strictBounds: true,
types: ['restaurant'],
});
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.setOptions)
.toHaveBeenCalledOnceWith({
bounds: FAKE_BOUNDS,
componentRestrictions: {country: ['uk']},
fields: [...PLACE_RESULT_DATA_FIELDS],
strictBounds: true,
types: ['restaurant'],
});
});

it(`doesn't define bounds when only location bias is specified`, async () => {
Expand All @@ -175,7 +173,7 @@ describe('PlacePicker', () => {
picker.locationBias = {lat: 12, lng: 34};
await env.waitForStability();

expect(fakeAutocomplete.setOptions)
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.setOptions)
.toHaveBeenCalledOnceWith(jasmine.objectContaining({
bounds: undefined,
}));
Expand All @@ -187,7 +185,7 @@ describe('PlacePicker', () => {
picker.radius = 1000;
await env.waitForStability();

expect(fakeAutocomplete.setOptions)
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.setOptions)
.toHaveBeenCalledOnceWith(jasmine.objectContaining({
bounds: undefined,
}));
Expand All @@ -199,7 +197,8 @@ describe('PlacePicker', () => {
picker.placeholder = 'Search nearby places';
await env.waitForStability();

expect(fakeAutocomplete.setOptions).not.toHaveBeenCalled();
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.setOptions)
.not.toHaveBeenCalled();
});

it(`enables search & clear buttons on user input`, async () => {
Expand Down Expand Up @@ -230,12 +229,13 @@ describe('PlacePicker', () => {
it(`sets value based on user selection and fires event`, async () => {
const dispatchEventSpy = spyOn(PlacePicker.prototype, 'dispatchEvent');
let autocompleteSelectionHandler: Function;
fakeAutocomplete.addListener.withArgs('place_changed', jasmine.anything())
env.fakeGoogleMapsHarness!.autocompleteSpy.addListener
.withArgs('place_changed', jasmine.anything())
.and.callFake((eventName, handler) => {
autocompleteSelectionHandler = handler;
return {} as google.maps.MapsEventListener;
});
fakeAutocomplete.getPlace.and.returnValue(
env.fakeGoogleMapsHarness!.autocompleteSpy.getPlace.and.returnValue(
FAKE_PLACE_RESULT_FROM_AUTOCOMPLETE);
const {picker, input, searchButton, clearButton} = await prepareState();

Expand All @@ -256,7 +256,8 @@ describe('PlacePicker', () => {

it(`sets value to undefined when place's cleared & fires event`, async () => {
let autocompleteSelectionHandler: Function;
fakeAutocomplete.addListener.withArgs('place_changed', jasmine.anything())
env.fakeGoogleMapsHarness!.autocompleteSpy.addListener
.withArgs('place_changed', jasmine.anything())
.and.callFake((eventName, handler) => {
autocompleteSelectionHandler = handler;
return {} as google.maps.MapsEventListener;
Expand All @@ -280,7 +281,8 @@ describe('PlacePicker', () => {
});

it(`sets value based on place returned by Find Place request`, async () => {
fakeAutocomplete.getBounds.and.returnValue(FAKE_BOUNDS);
env.fakeGoogleMapsHarness!.autocompleteSpy.getBounds.and.returnValue(
FAKE_BOUNDS);
const {picker, input, searchButton, clearButton} = await prepareState();

await enterQueryText(input, '123 Main St');
Expand Down Expand Up @@ -308,7 +310,8 @@ describe('PlacePicker', () => {

it('sets value from fallback GA API when Place.findPlaceFromQuery is not available',
async () => {
fakeAutocomplete.getBounds.and.returnValue(FAKE_BOUNDS);
env.fakeGoogleMapsHarness!.autocompleteSpy.getBounds.and.returnValue(
FAKE_BOUNDS);
const {picker, input, searchButton, clearButton} = await prepareState();
(env.fakeGoogleMapsHarness!.findPlaceFromQueryHandler as jasmine.Spy)
.and.throwError(new Error(
Expand Down Expand Up @@ -412,7 +415,8 @@ describe('PlacePicker', () => {

await picker.bindTo(fakeMap);

expect(fakeAutocomplete.bindTo).toHaveBeenCalledOnceWith('bounds', fakeMap);
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.bindTo)
.toHaveBeenCalledOnceWith('bounds', fakeMap);
});

it(`binds to map bounds declaratively via attribute`, async () => {
Expand All @@ -422,7 +426,7 @@ describe('PlacePicker', () => {
`);
const mapElement = root.querySelector<FakeMapElement>('gmp-map')!;

expect(fakeAutocomplete.bindTo)
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.bindTo)
.toHaveBeenCalledOnceWith('bounds', mapElement.innerMap);
});

Expand All @@ -431,7 +435,8 @@ describe('PlacePicker', () => {
<gmpx-place-picker for-map="my-map"></gmpx-place-picker>
`);

expect(fakeAutocomplete.bindTo).not.toHaveBeenCalled();
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.bindTo)
.not.toHaveBeenCalled();
});

it(`doesn't bind to map bounds when id matches non-Map element`, async () => {
Expand All @@ -440,6 +445,7 @@ describe('PlacePicker', () => {
<div id="my-map"></div>
`);

expect(fakeAutocomplete.bindTo).not.toHaveBeenCalled();
expect(env.fakeGoogleMapsHarness!.autocompleteSpy.bindTo)
.not.toHaveBeenCalled();
});
});
31 changes: 5 additions & 26 deletions src/route_building_blocks/route_marker/route_marker_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import '../../testing/fake_gmp_components.js';
// import 'jasmine'; (google3-only)

import {html, TemplateResult} from 'lit';
Expand All @@ -19,25 +18,6 @@ import {RouteMarker} from './route_marker.js';

type LatLng = google.maps.LatLng;

const FAKE_MARKER_LIBRARY = {
AdvancedMarkerElement: class {
position?: LatLng|null;
zIndex?: number|null;
title: string = '';
map?: google.maps.Map|null;

innerContent?: Element|null;
set content(content: Element|null|undefined) {
// Detach from the DOM as in the real AdvancedMarkerElement
content?.remove();
this.innerContent = content;
}
get content(): Element|null|undefined {
return this.innerContent;
}
}
};

function fakeRouteBetween(
[startLat, startLng]: [number, number],
[endLat, endLng]: [number, number]): google.maps.DirectionsRoute {
Expand All @@ -54,15 +34,14 @@ describe('RouteMarker', () => {

beforeAll(() => {
env.defineFakeMapElement();
});

beforeEach(() => {
env.importLibrarySpy!.and.returnValue(FAKE_MARKER_LIBRARY);
env.defineFakeAdvancedMarkerElement();
});

async function prepareState(template?: TemplateResult) {
const constructorSpy =
spyOn(FAKE_MARKER_LIBRARY, 'AdvancedMarkerElement').and.callThrough();
const constructorSpy = spyOn(
env.fakeGoogleMapsHarness!.libraries['marker'],
'AdvancedMarkerElement')
.and.callThrough();
const root =
env.render(template ?? html`<gmpx-route-marker></gmpx-route-marker>`);
await env.waitForStability();
Expand Down
15 changes: 14 additions & 1 deletion src/testing/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {ReactiveElement, render as litRender, TemplateResult} from 'lit';

import {APILoader} from '../api_loader/api_loader.js';

import {FakeMapElement} from './fake_gmp_components.js';
import {FakeAdvancedMarkerElement, FakeMapElement} from './fake_gmp_components.js';
import {FakeGoogleMapsHarness} from './fake_google_maps.js';

declare global {
Expand Down Expand Up @@ -103,12 +103,25 @@ export class Environment {
return root;
}

/**
* Inserts a fake implementation of <gmp-map> into the test environment.
*/
defineFakeMapElement() {
if (!customElements.get('gmp-map')) {
customElements.define('gmp-map', FakeMapElement);
}
}

/**
* Inserts a fake implementation of <gmp-advanced-marker> into the test
* environment.
*/
defineFakeAdvancedMarkerElement() {
if (!customElements.get('gmp-advanced-marker')) {
customElements.define('gmp-advanced-marker', FakeAdvancedMarkerElement);
}
}

/**
* Waits for all Lit `ReactiveElement` children of the given parent node to
* finish rendering.
Expand Down
13 changes: 13 additions & 0 deletions src/testing/fake_autocomplete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

// import 'jasmine'; (google3-only)

/** Creates a Jasmine spy to replace an Autocomplete object. */
export const makeFakeAutocomplete = () =>
jasmine.createSpyObj<google.maps.places.Autocomplete>(
'Autocomplete',
['addListener', 'bindTo', 'getBounds', 'getPlace', 'setOptions']);
33 changes: 28 additions & 5 deletions src/testing/fake_gmp_components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,46 @@

import {LitElement} from 'lit';

import {LatLng, LatLngBounds, LatLngBoundsLiteral, LatLngLiteral} from '../utils/googlemaps_types.js';
import {LatLng, LatLngLiteral} from '../utils/googlemaps_types.js';

declare global {
interface HTMLElementTagNameMap {
'gmp-map': FakeMapElement;
'gmp-advanced-marker': FakeAdvancedMarkerElement;
}
}

/** A fake google.maps.MapElement class for testing purposes. */
/** A fake `google.maps.MapElement` class for testing purposes. */
export class FakeMapElement extends LitElement {
/** Test-only property to spy on calls to fitBounds(). */
readonly fitBoundsSpy = jasmine.createSpy('fitBounds');

center: LatLng|LatLngLiteral|null = null;

// tslint:disable-next-line:prefer-type-annotation
readonly innerMap = {
fitBounds: (bounds: LatLngBounds|LatLngBoundsLiteral) => {}
} as google.maps.Map;
readonly innerMap = {fitBounds: this.fitBoundsSpy} as unknown as
google.maps.Map;

mapId: string|null = null;
zoom: number|null = null;
}

/**
* A fake `google.maps.AdvancedMarkerElement` class for testing purposes.
*/
export class FakeAdvancedMarkerElement extends LitElement {
position?: LatLng|null;
zIndex?: number|null;
override title = '';
map?: google.maps.Map|null;

innerContent?: Element|null;
set content(content: Element|null|undefined) {
// Detach from the DOM as in the real AdvancedMarkerElement
content?.remove();
this.innerContent = content;
}
get content(): Element|null|undefined {
return this.innerContent;
}
}
Loading

0 comments on commit 219bb47

Please sign in to comment.