I have been recently building out the end2end test suites for our MobileCaddy Seed and Shell applications. The core goal was to give Salesforce mobile application developers a starting point on which they could add their own test suites and specs, giving them a head-start when end2end testing Ionic apps. During this process I ran into a couple of “gotchas” when dealing with Ionic’s collection-repeat directive. This post should highlight the issues, propose some work-arounds and also lay the outline on how we rectify the issues we’ve seen.
This post assumes you know about Protractor, if not you can read up here, but the long-and-short is that it’s a testing tool built for AngularJS applications. It is assumes you are familiar with Ionic’s (incredibly brilliant) collection-repeat directive.
The Scenario
I’m going to use our Time & Expenses mobile application for Salesforce as our example (you can download it from github). The main screen of our application simply lists projects that are assigned to our currently logged in user. This list uses Ionic’s collection-repeat to populate the DOM with our projects, and with our application in test mode the list should contain 5 projects. What I want to do is use Protractor to end2end test that our app starts up and that this screen has 5 projects listed.
With this in mind our application code looks something like this;
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<ion-list> <ion-item class="item" collection-repeat="project in projects" type="item-text-wrap" href="#/tab/project/{{project.Id}}"> <h3>{{project.Name}}</h3> <p>{{project.Description}}</p> </ion-item> </ion-list> |
And our test like this;
1 2 3 4 5 6 |
it("should have a projects list with 5 projects", function() { var projects = element.all(by.repeater('project in projects')); expect(projects.count()).toEqual(5); }); |
We’re using the repeater locator to pull out the list of items in our collection-repeat and then we’re checking this against our expected number of projects… or at least that’s what we wanted to do.
The Issue(s)
After running our protractor test we run into the following error;
1 2 3 4 5 6 7 8 9 10 |
Failures: 1) Projects should have a projects list with 5 projects Message: Expected 0 to equal 5. Stacktrace: Error: Failed expectation at /home/todd/projects/mc/seed-expenses-ionic/tests/e2e/test.spec.js:12:28 at Array.forEach (native) |
So what happened? Well actually Protractor doesn’t support collection-repeats in it’s repeater locator. That’s OK right because we can work around this and use a different locator perhaps. In our case we can use the binding locator and look for project.Name. This could be the first stab at our new code;
1 2 3 4 5 6 |
it("should have a projects list with 5 projects", function() { var projects = element.all(by.binding('project.Name')); expect(projects.count()).toEqual(5); }); |
Let’s run our test again, and this should work right?
Wrong! Here’s what we get;
1 2 3 4 5 6 7 8 9 10 |
Failures: 1) Projects should have a projects list with 5 projects Message: Expected 20 to equal 5. Stacktrace: Error: Failed expectation at /home/todd/projects/mc/seed-expenses-ionic/tests/e2e/test.spec.js:12:28 at Array.forEach (native) |
Where did that count of 20 come from? We only have 5 projects.
After digging into the Ionic code I can see that collection-repeat currently creates 20 DOM items, regardless of if there are less or not.
Hmmm… so what can we do?
The Work-Arounds
The first step, mentioned above, is to use an alternate locator instead of repeater. But this only takes us so far.
The second step (which is admittedly ugly) is to spec our test like this;
1 2 3 4 5 6 7 |
it("should have a projects list with 5 projects", function() { var projects = element.all(by.binding('project.Name')); expect(projects.get(6).getText()).toContain('Drive Bus to station'); expect(projects.get(6).getText()).toEqual(''); }); |
Now we’re using the binding locator and also checking that the 5th element has some content and that the 6th is empty. Of course this is not ideal, but hey, this is a work-around right… and does it work?
1 2 3 4 5 |
should have a projects list with 5 projects - pass Finished in 1.12 seconds 1 tests, 2 assertions, 0 failures |
Woohoo!
Our (planned) Solution
There are actually two prongs to what we’re going to do to make this nicer and better;
1) Pull Request to Ionic for collection-repeat
We currently have a PR submitted to Ionic to dynamically scale the number of DOM elements created in a collection-repeat. This will remove the need to check, for example, the content of elements that shouldn’t really exist.
2) Create a protractor-ionic-locator Package
UPDATE: We have launched our protractor-ionic-locator package… check it out. This adds a custom locator to select collection-repeat lists (with more locators to follow)
We’ll update as both of these progress… but in the meantime we hope that our write-up here might save you some time when end2end testing Ionic mobile applications.