In this final part of this post, we will be locating the cluster markers (from the Markerclusterer or MarkerclustererPlus library) in a Google map. This is in the context of e2e testing for an AngularJS web application using Protractor.
The first part of the post was a brief introduction , while part 2 showed how to locate Google maps markers.
Markerclusterer
The cluster markers have this DOM structure.
<div class="cluster"> <img /> <div>10</div> </div>
Once again it is just a case of finder the right xpath expression to use as the locator. If we were only interested in the number of cluster markers on the map, we could just use the count() utility method for the ElementArrayFinder as we did for getting a count of the Google maps markers.
element.all(By.xpath("//div[@class=\"cluster\"]/div")).count();
However for the application I was working on, I needed the total number of markers represented by all the cluster markers.
The cluster marker div structure has an inner div that contains a number, this is the number of Google map markers that are not shown but are represented by the cluster. So here we have to get this number from all the cluster markers and add them up.
ElementArrayFinder has various functions such as each() and map() that would allow us to either iterate through the cluster markers and extract the information we need so that we can total the numbers. Luckily it also has a reduce() function that does exactly what we need, and here is an example of a spec test using it.
var checkClusterNumberCount = function(expectedCount) { element.all(By.xpath("//div[@class=\"cluster\"]/div")).reduce(function(accum, elem) { return elem.getText().then(function(text) { var num = parseInt(text); return accum + num; }); }, 0).then(function(result) { expect(result).toEqual(expectedCount); }); };
The explanation for this function:
1. The xpath expression is used to locate all the cluster markers in the DOM.
By.xpath(“//div[@class=\”cluster\”]/div”)
2. The function element.all() returns an ElementArrayFinder and the reduce() function is called. In this case it is passed our own reduce function that has 2 parameters:
- accum holds the accumulated number, which is initially set to zero
- elem is an ElementFinder from the ElementArrayFinder
3. For each ElementFinder we call the getText() to get the value from the inner div. This value is converted to a number and added to the accumulated number. Notice that since ElementFinder is also a promise, we need to use another callback from the getText() function.
4. We then use the expect() function to compare the final accumulated number to the number that we were expecting in order to pass the test.
Final Tip
Initially after I ran the code to count the markers (both the displayed markers and the ones contained in the cluster markers) I found that, although the code was working properly, some of the tests would occassionally fail. Eventually I worked out that even though in the callbacks the AngularJS code may have finished running, I still sometimes needed to wait for markers and markerclusterers to finish loading in the map.
I worked around this by added a small delay before trying to locate the markers, e.g.
browser.sleep(…);
This is a bit of a hack, but unfortunately I’m not aware of any way to get a notification from Google maps when markers have finished loading.