Testing

Learn how and why to test your Ember apps →

Maps introduce two additional complications to testing.

The first is timing. How do we know when to run the tests? Is the map ready? Are the components there?

The second is interaction. There’s no guarantee that any of the map components have DOM elements to interact with, and Google Maps doesn’t provide an official API to label, find, and interact with components drawn on the map. So you generally can’t just find some marker and click on it.

These two issues are tackled by the setupMapTest and waitForMap functions exported by the test-support module.

The setupMapTest hook is similar to the setupRenderingTest and setupApplicationTest hooks used in Ember tests. You run it once in your test module and it will set up everything for tracking any maps that are drawn during the tests.

And waitForMap let’s you pause the test while the map is rendering. Once the map is idle, it’ll resolve with the map instance and all of it’s components. You can use those instances to simulate users interacting with the map.

It’ll make a bit more sense in the context of an example. Let’s look at a sample acceptance test first.

Acceptance testing

Let’s say we draw a marker in our app which should show a popup with some text when clicked.

<GMap @lat={{this.london.lat}} @lng={{this.london.lng}} as |map|>

  <map.marker
    @lat={{this.london.lat}}
    @lng={{this.london.lng}}
    @onClick={{toggle "markerTooltipOpen" this}} as |marker|>

    <marker.infoWindow
      @isOpen={{this.markerTooltipOpen}}
      @onCloseclick={{fn (mut this.markerTooltipOpen) false}}>
      <span data-test-info-window>You clicked me!</span>
    </marker.infoWindow>

  </map.marker>
</GMap>

That popup is very important to our app’s functionality, so we’d like to make sure that it’s actually shown. Here’s how you’d write an acceptance test for that.

import { module, test } from 'qunit';
import { visit, waitFor } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';

// Import the helper functions from test support
import {
  setupMapTest,
  trigger,
  waitForMap,
} from 'ember-google-maps/test-support';

module('Acceptance | map', function (hooks) {
  setupApplicationTest(hooks);
  // Run the setup hook
  setupMapTest(hooks);

  test('visiting /map', async function (assert) {
    await visit('/map');

    // Get the last map drawn on the page and its components
    let { map, components } = await waitForMap();

    let marker = components.markers[0].mapComponent;
    // Use the `trigger` helper to simulate events on map components
    trigger(marker, 'click');

    let infoWindow = await waitFor('[data-test-info-window]');
    assert.dom(infoWindow).hasText('You clicked me!');

    // Move the map, for example
    map.panBy(100, 0);
    // And so on...
  });
});

If you’re wondering how to find a specific marker out of hundreds, remember that you can always pass extra arguments to the marker component to help you find it later.

<GMap @lat={{this.london.lat}} @lng={{this.london.lng}} as |map|>
  {{#each this.locations as |location|}}
    <map.marker
      @locationId={{location.id}}
      @lat={{location.lat}}
      @lng={{location.lng}} />
  {{/each}}
</GMap>
import { module, test } from 'qunit';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import {
  setupMapTest,
  trigger,
  waitForMap,
} from 'ember-google-maps/test-support';

module('Acceptance | map', function (hooks) {
  setupApplicationTest(hooks);
  setupMapTest(hooks);

  test('visiting /locations', async function (assert) {
    await visit('/locations');

    let { components } = await waitForMap();

    let marker = components.markers.find(
      (marker) => marker.mapComponent.locationId === '#some-important-location'
    );
    trigger(marker.mapComponent, 'click');

    // Assert something...
  });
});
Multiple maps on the same page

If, for some reason, you have more than one map visible in a single test, you can differentiate between them by providing an id.

In your template, pass an id to the map.

<GMap id="some-unique-id" @lat={{this.lat}} @lng={{this.lng}} />

Then, in your tests, give the same id to the waitForMap function.

import { module, test } from 'qunit';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { setupMapTest, waitForMap } from 'ember-google-maps/test-support';

module('Acceptance | map', function (hooks) {
  setupApplicationTest(hooks);
  setupMapTest(hooks);

  test('visiting /map', async function (assert) {
    await visit('/map');

    let { map, components } = await waitForMap('some-unique-id');

    assert.ok(map);
  });
});
Integration testing

When it comes to maps, integration testing isn’t too different to acceptance testing. Run the setupMapTest hook and use waitForMap to wait for the map to render. Just don’t forget about waitForMap! Even if you don’t need any of the map instances it returns, you should still wait for it to resolve.

import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { setupMapTest, waitForMap } from 'ember-google-maps/test-support';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | map', function (hooks) {
  setupRenderingTest(hooks);
  // Run the setup hook
  setupMapTest(hooks);

  test('it works', async function (assert) {
    this.lat = '51';
    this.lng = '0';

    await render(hbs`
      <GMap @lat={{this.lat}} @lng={{this.lng}} />
    `);

    let api = await waitForMap();
    // or just `await waitForMap();` if you don’t need the map instance

    assert.ok(api.map);
  });
});

Let’s see how we can efficiently deal with large numbers of markers using clustering.

Clustering ›