1

I am creating an end-to-end test for an Angular 1.5 application using Protractor. The application allows the user to upload a file to a backend Web API, by selecting a file using a standard input type="file" control and a submit button. I have a Controller that basically behaves like this:

function exampleController() {
    var vm = this;

    vm.status = "";
    vm.buttonClickHandler = buttonClickHandler;

    function buttonClickHandler() {
        vm.status = "calling";
        service.makeAsyncCall()
               .then(function() {
                   vm.status = "success";
               }, function() {
                   vm.status = "error";
               });
    }
}

The buttonClickHandler is called when the user clicks the submit button.

How do I write an end-to-end test using Protractor that verifies that the status changes to "calling" when the user clicks the button, and then to "success" when the promise resolves?

In most of my attempts, I could verify that vm.status had been set to "success", or I could verify that it was set to "calling" if I set ignoreSynchronization = true, but the latter only works when I build in an artificial delay in my Web API backend call, otherwise the process was apparently too fast and the value would show "success".

Daan
  • 6,952
  • 4
  • 29
  • 36
  • 1
    Is there any indication on the UI side that `status` changes? – alecxe May 04 '16 at 17:55
  • @alecxe yes, the UI correctly shows the `status` changes that I want. I just can't seem to capture them in an automated test. – Daan May 04 '16 at 17:56
  • Got it. Is there say an element with the status text or a loading spinner that we can check instead accessing the field in the controller? Thanks. – alecxe May 04 '16 at 17:57
  • Sure, the status is simply bound to a div like so: `
    {{vm.status}}
    `, I access it in protractor using `element(by.binding("vm.status"))` which gives me the correct element.
    – Daan May 04 '16 at 18:06

1 Answers1

1

This is rather ugly, flaky and it's a bad practice but I would give a try anyway :)

it('click test', function () {
    $('button').click();

    browser.wait(function () {
        // Polls 'as fast as possible' until it evalutes to truthy value
        // This might be flaky if 'calling' status is shorter than
        // 1 polling interval (whose length can't be determined)
        return $('div_containing_status').getText().then(function (status) {
            return /calling/.test(status);
        });
    }, 10000);

    expect($('div_containing_status').getText()).toBe('calling');

    browser.wait(function () {
        return $('div_containing_status').getText().then(function (status) {
            return /success/.test(status);
        });
    }, 10000);

    expect($('div_containing_status').getText()).toBe('success');
});

UPDATE: edited info about polling based on @alecxe comment

finspin
  • 4,021
  • 6
  • 38
  • 66
  • Actually, [there is no explicit "poll frequency" for the `browser.wait()`](http://stackoverflow.com/questions/33704000/controlling-poll-frequency-of-browser-wait-fluent-wait). But I understand your idea and I wonder if this is gonna work reliably or not. Thanks. – alecxe May 04 '16 at 20:00
  • Interesting, I wonder where does the 500ms info come from :). I always thought it's 500ms but I just tested it and browser.wait() does indeed poll rather infrequently: 23:15:18.976 INFO - Executing: [get current url]) 23:15:18.990 INFO - Done: [get current url] 23:15:18.997 INFO - Executing: [get current url]) 23:15:19.006 INFO - Done: [get current url] 23:15:19.028 INFO - Executing: [get current url]) 23:15:19.044 INFO - Done: [get current url] 23:15:19.074 INFO - Executing: [get current url]) 23:15:20.384 INFO - Done: [get current url] – finspin May 04 '16 at 20:24
  • Yeah, 500 ms is the default poll frequency in other selenium language bindings, like Python or Java, though it's configurable there. – alecxe May 04 '16 at 20:42
  • I'll test this on the actual code base next Monday, but I have a suspicion that the polling interval will be too long to capture the first "calling" status. The API call returns very quickly... – Daan May 05 '16 at 10:26