@@ -217,4 +217,88 @@ expect(value).toBeGreaterThan(expected)
217
217
</pre>
218
218
219
219
# Example
220
- See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
220
+ See the {@link https://github.com/angular/angular-seed angular-seed} project for more examples.
221
+
222
+ ## Conditional actions with element(...).query(fn)
223
+
224
+ E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
225
+ queueing actions and expectations that can handle futures. From time to time, you might need
226
+ conditional assertions or element selection. Even though you should generally try to avoid this
227
+ (as it is can be sign for unstable tests), you can add conditional behavior with
228
+ `element(...).query(fn)`. The following code listing shows how this function can be used to delete
229
+ added entries (where an entry is some domain object) using the application's web interface.
230
+
231
+ Imagine the application to be structure into two views:
232
+
233
+ 1. *Overview view* which lists all the added entries in a table and
234
+ 2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
235
+ delete button, the user is redirected back to the *overview page*.
236
+
237
+ <pre>
238
+ beforeEach(function () {
239
+ var deleteEntry = function () {
240
+ browser().navigateTo('/entries');
241
+
242
+ // we need to select the <tbody> element as it might be the case that there
243
+ // are no entries (and therefore no rows). When the selector does not
244
+ // result in a match, the test would be marked as a failure.
245
+ element('table tbody').query(function (tbody, done) {
246
+ // ngScenario gives us a jQuery lite wrapped element. We call the
247
+ // `children()` function to retrieve the table body's rows
248
+ var children = tbody.children();
249
+
250
+ if (children.length > 0) {
251
+ // if there is at least one entry in the table, click on the link to
252
+ // the entry's detail view
253
+ element('table tbody a').click();
254
+ // and, after a route change, click the delete button
255
+ element('.btn-danger').click();
256
+ }
257
+
258
+ // if there is more than one entry shown in the table, queue another
259
+ // delete action.
260
+ if (children.length > 1) {
261
+ deleteEntry();
262
+ }
263
+
264
+ // remember to call `done()` so that ngScenario can continue
265
+ // test execution.
266
+ done();
267
+ });
268
+
269
+ };
270
+
271
+ // start deleting entries
272
+ deleteEntry();
273
+ });
274
+ </pre>
275
+
276
+ In order to understand what is happening, we should emphasize that ngScenario calls are not
277
+ immediately executed, but queued (in ngScenario terms, we would be talking about adding
278
+ future actions). If we had only one entry in our table, than the following future actions
279
+ would be queued:
280
+
281
+ <pre>
282
+ // delete entry 1
283
+ browser().navigateTo('/entries');
284
+ element('table tbody').query(function (tbody, done) { ... });
285
+ element('table tbody a');
286
+ element('.btn-danger').click();
287
+ </pre>
288
+
289
+ For two entries, ngScenario would have to work on the following queue:
290
+
291
+ <pre>
292
+ // delete entry 1
293
+ browser().navigateTo('/entries');
294
+ element('table tbody').query(function (tbody, done) { ... });
295
+ element('table tbody a');
296
+ element('.btn-danger').click();
297
+
298
+ // delete entry 2
299
+ // indented to represent "recursion depth"
300
+ browser().navigateTo('/entries');
301
+ element('table tbody').query(function (tbody, done) { ... });
302
+ element('table tbody a');
303
+ element('.btn-danger').click();
304
+ </pre>
0 commit comments