Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Incorrect flow with ignoreSynchronization set to true? #2275

Closed
KrekkieD opened this issue Jun 23, 2015 · 23 comments
Closed

Incorrect flow with ignoreSynchronization set to true? #2275

KrekkieD opened this issue Jun 23, 2015 · 23 comments

Comments

@KrekkieD
Copy link

Consider this test:

    it ('should fill a form in the proper order', function () {

        browser.ignoreSynchronization = true;

        $('[ng-model="one"]').sendKeys('one');
        $('[ng-model="two"]').sendKeys('two');
        $('[ng-model="three"]').sendKeys('three');
        $('[ng-model="four"]').sendKeys('four');
        $('[ng-model="five"]').sendKeys('five');
        $('[ng-model="six"]').sendKeys('six');
        $('[ng-model="seven"]').sendKeys('seven');

    });

I would expect the inputs to be filled in this order, however they are filled in what appears to be a random order.

To make this extra fun, if I add a line browser.sleep(1); after all sendKeys commands, the flow is corrected and inputs are filled in the order specified. This seems strange. Should it not always use the sequential flow?

This did not seem to occur with protractor <2.0.0.
I use ignoreSynchronization because I need to manually bootstrap my application, but the same behavior occurs for ng-app based bootstraps.

@talcloudshare
Copy link

Same here, having problems with the flow. I've set ignoreSynchronization to true in the beginning of my test spec. In one of my page object's functions, which is only called at the end of the spec, I'm setting it to false. But, when the spec starts running it seems like ignoreSynchronization was set to false all along. Commenting out the "ignoreSynchronization = false" line resolves that issue.
For me it occurs also with protractor 1.8

@talcloudshare
Copy link

Adding a simplified example:

it('should be able to get page without failing', function () {
    browser.ignoreSynchronization = true;
    var loginButton = element(by.xpath("//input[@value = 'Login']"));
    browser.get("/");
    expect(loginButton.waitReady()).toBeTruthy();
    browser.ignoreSynchronization = false;
}

Beginning to suspect the problem is separated from the issue and is related to waitReady somehow, since I replaced it with isPresent() and now it seems to work

@KrekkieD
Copy link
Author

@talcloudshare your example is actually correct behavior, as your ignoreSynchronization change does not return a webdriver promise. The full it is read on start of the test, the actual execution in-browser follows after that. That means while reading your it it is queueing the webdriver commands (= promises) as well as executing other code (your ignoreSync change). I think in your situation you should change the ignoreSynchronization property in either a following it, or create a webdriver promise at the end of your it and set the property in the resolve of that promise.

edit: isPresent has a timeframe in which it waits for the element to appear (this is the implicitWait timeout). So these async issues get some slack because of this isPresent time flexibility.

@talcloudshare
Copy link

Thanks @KrekkieD

I was trying to resolve the issue using control flows:

it('should be able to get page without failing', function () {
        var flow = protractor.promise.controlFlow();
        flow.execute(function () {
            console.log("Set no angular");
            browser.ignoreSynchronization = true;
        });

        var loginButton = element(by.xpath("//input[@value = 'Login']"));
        browser.get("/");
        expect(loginButton.waitReady()).toBeTruthy();

        flow.execute(function () {
            console.log("Set angular");
            browser.ignoreSynchronization = false;
        });
}

When running the spec, it seems like setting ignoreSynchronization didn't have an affect:

>protractor protractor.conf.js --baseUrl=...
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
Set no angular
F

Failures:

  1) trainer should be able to instruct students
   Message:
     Error: Angular could not be found on the page .../ : retries looking for angular exceeded

Tried adding browser.sleep(10000) inside and outside the flow.execute, to make sure there's a time delta between setting ignoreSynchronization and getting the URL - and it still fails. Setting ignoreSynchronization outside the flow.execute works (but then I go back to the previous issue of only being able to set ignoreSynchronization once per spec).

@KrekkieD
Copy link
Author

It appears that using protractor.promise.controlFlow().execute() also corrects the flow, as this test case also executes in order of definition (ignoreSynchronization is true):

it ('should fill a form in the proper order', function () {

    $('[ng-model="one"]').sendKeys('one');
    $('[ng-model="two"]').sendKeys('two');
    $('[ng-model="three"]').sendKeys('three');
    $('[ng-model="four"]').sendKeys('four');
    $('[ng-model="five"]').sendKeys('five');
    $('[ng-model="six"]').sendKeys('six');
    $('[ng-model="seven"]').sendKeys('seven');

    protractor.promise.controlFlow().execute(function () {});

});

@hankduan
Copy link
Contributor

I can't seem to reproduce this behavior:

Here's the test case I used:

  it ('should fill a form in the proper order', function () {
      browser.get('http://www.angularjs.org');
      browser.ignoreSynchronization = true;

      element(by.model('yourName')).sendKeys('1');
      element(by.model('yourName')).sendKeys('2');
      element(by.model('yourName')).sendKeys('3');
      element(by.model('yourName')).sendKeys('4');
      element(by.model('yourName')).sendKeys('5');
      element(by.model('yourName')).sendKeys('6');
      element(by.model('yourName')).sendKeys('7');

      expect(element(by.binding('yourName')).getText()).toBe('Hello 1234567!');
  });

Is your app public for me to test on?

@KrekkieD
Copy link
Author

If you do a clone of https://github.com/KrekkieD/buenos-protractor and run npm test you can see the test running. Look at the spec test/specs/reproduce.spec.js to see what expectations are met. This demonstrates the incorrect flow and also how it can be corrected.

@hankduan
Copy link
Contributor

@KrekkieD The issue looks very similar to SeleniumHQ/selenium#444, which will be fixed in webdriver 2.46.0. That being said, Protractor is having some issues upgrading to that (#2245).

A quick fix you can do right now though is to upgrade to jasmine2 in your spec.

@talcloudshare
Copy link

@hankduan Is the issue I reported somehow related to that? Otherwise I'll create a new issue

@hankduan
Copy link
Contributor

@talcloudshare Try this:

beforeEach(function() {
  browser.ignoreSynchronization = true;
});

it('should be able to get page without failing', function () {
        var loginButton = element(by.xpath("//input[@value = 'Login']"));
        browser.get("/");
        expect(loginButton.waitReady()).toBeTruthy();
};

afterEach(function() {
  browser.ignoreSynchronization = false;
});

@talcloudshare
Copy link

@hankduan Thanks but this doesn't resolve my issue, I used a simplified example. I do need to toggle ignoreSynchronization several times during my tests - not only beginning and ending. I am testing a site which has some of the pages in Angular, while others still haven't been converted to Angular. Each test flow consists of going through multiple pages - some in Angular, some aren't (yet). Therefor I am looking for a solution other than using beforeEach/afterEach, and I'd rather not set browser.ignoreSynchronization = true for the entire tests.

@hankduan
Copy link
Contributor

In that case, try:

it('should be able to get page without failing', function () {
        var flow = protractor.promise.controlFlow();
        flow.execute(function () {
            console.log("Set no angular");
            browser.ignoreSynchronization = true;
        });

        flow.execute(function () {
            var loginButton = element(by.xpath("//input[@value = 'Login']"));
            browser.get("/");
            expect(loginButton.waitReady()).toBeTruthy();
        });

        flow.execute(function () {
            console.log("Set angular");
            browser.ignoreSynchronization = false;
        });
}

I haven't personally tested it, and there's this lingering issue SeleniumHQ/selenium#715, but give it a try. It should work

@iljapavlovs
Copy link

ive got the same issue.

@talcloudshare
Copy link

It does seem to work but it seems like high maintenance and it makes the tests unreadable (the test I provided was a simplified example, the tests I have are more complex and structured with page objects and other fun stuff). I am trying to get multiple developers from my organization to work with protractor and writing tests this way will just keep everybody away.

I was trying to use browser.call, and it doesn't seem to work the way I expect it to (From the docs: Schedules a command to execute a custom function.).

browser.call(function () { console.log("Set no angular"); browser.ignoreSynchronization = true; });
var loginButton = element(by.xpath("//input[@value = 'Login']"));
browser.get("/");
expect(loginButton.waitReady()).toBeTruthy();
browser.call(function () { console.log("Set angular"); browser.ignoreSynchronization = false; });

Again,

>protractor protractor.conf.js --baseUrl=...
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
Set no angular
F

Failures:

  1) trainer should be able to instruct students
   Message:
     Error: Angular could not be found on the page .../ : retries looking for angular exceeded

@KrekkieD
Copy link
Author

KrekkieD commented Jul 2, 2015

@talcloudshare it seems you'll need to manually change the ignoreSynchronization setting anyway. My suggestion would be to create an it in which you set it to true as first it in your describe, then write the it's that you need for your test, and as final it you reset ignoreSynchronization to false. This makes it relatively simple. You could use a helper class so you can reuse it across your spec files (i.e. export a function in your helper class that will create the it for you).

It could look like this;

describe('something', function () {

// manually
it ('should set sync', function () {
    browser.ignoreSynchronization = true;
});

// or using a class
require('./someclass').ignoreSync();



it ('should test something you want', function () { /* ... */ });



// manually
it ('should reset sync', function () {
    browser.ignoreSynchronization = false;
});

// or using a class
require('./someclass').resetIgnoreSync();

});


});

@talcloudshare
Copy link

Problem with that approach is that it prevents me from wrapping the toggling of browser.ignoreSynchronization inside the page object to which it belongs. I want the page object itself to set the mode - whether Angular is needed or not, and to keep the specs themselves clean and readable, even for people with lesser technical knowledge.

@hankduan
Copy link
Contributor

hankduan commented Jul 2, 2015

@talcloudshare If this is something you need to reuse, you should put it in a helper function.

i.e.

var runNonAngular = function(fn) {
        var flow = protractor.promise.controlFlow();
        flow.execute(function () {
            browser.ignoreSynchronization = true;
        });
        flow.execute(fn);
        flow.execute(function () {
            browser.ignoreSynchronization = false;
        });
} 

Then you can use it as:

describe('mixing angular with nonAngular steps', function() {
  it('should work', function() {
    browser.get('http://www.myangularapp.com');
    $('angularStuff').click();
    runNonAngular(function() {
      $('navigateToNonAngularPage').click()
      $('randomInput').sendKeys('foo');
      $('backtoAngularPage').click();
    })
    $('backOnAngularPage').getText();
  });
})

Keep in mind that although Protractor works for non-angular apps, it is not designed for them, so you will need to write some helpers to get non-angular apps to work. That being said, if you design the helpers properly, the helper themselves will be ugly, but the test should remain very elegant.

And of course, I'm just giving you an example of a generic helper. Your helper should look much more different, as I have no idea what you are doing with them.

@hankduan
Copy link
Contributor

hankduan commented Jul 2, 2015

@KrekkieD I would not separate the contents of a single test into multiple its because it will make your test very difficult to read and maintain.

@talcloudshare
Copy link

Thanks, this approach seem to be working well for me.

@hankduan
Copy link
Contributor

hankduan commented Jul 5, 2015

This issue contains both a question and an issue. The former is answered, and the latter is a dup.
Closing. Please open a new one if necessary.

@hankduan hankduan closed this as completed Jul 5, 2015
@mfulton26
Copy link

@hankduan I have modified your AnagularJS homepage test example to reproduce this issue:

it('should fill a form in the proper order', function () {
    browser.get('http://www.angularjs.org');
    browser.ignoreSynchronization = true;

    element(by.model('todoList.todoText')).sendKeys('foo');
    element(by.model('yourName')).sendKeys('1');
    element(by.model('yourName')).sendKeys('2');
    element(by.model('yourName')).sendKeys('3');
    element(by.model('yourName')).sendKeys('4');
    element(by.model('yourName')).sendKeys('5');
    element(by.model('yourName')).sendKeys('6');
    element(by.model('yourName')).sendKeys('7');
});

Do protractor tests have to contain an expect statement?

With or without an expect statement I would expect the input order to be ['foo', '1', '2', '3', '4', '5', '6', '7'] but instead I am getting ['1', 'foo', '2', '3', '4', '5', '6', '7'].

P.S. Executing as above can be a bit tricky to see that '1' is sent before 'foo' so I added a browser.sleep to better demonstrate the issue:

it('should fill a form in the proper order', function () {
    browser.get('http://www.angularjs.org');
    browser.ignoreSynchronization = true;

    element(by.model('todoList.todoText')).sendKeys('foo');
    element(by.model('yourName')).sendKeys('1').then(function() {
        return browser.sleep(1000);
    });
    element(by.model('yourName')).sendKeys('2');
    element(by.model('yourName')).sendKeys('3');
    element(by.model('yourName')).sendKeys('4');
    element(by.model('yourName')).sendKeys('5');
    element(by.model('yourName')).sendKeys('6');
    element(by.model('yourName')).sendKeys('7');
});

@hankduan
Copy link
Contributor

like I said earlier:


The issue looks very similar to SeleniumHQ/selenium#444, which will be fixed in webdriver 2.46.0. That being said, Protractor is having some issues upgrading to that (#2245).


@g0rd
Copy link

g0rd commented Jan 18, 2016

I am trying to test non-angular page using @hankduan suggestion and the helper function actually doesn't run and the test gives me success on completion.
Code is as below:

var runNonAngular = function(fn) {
            var flow = protractor.promise.controlFlow();
            flow.execute(function () {
                browser.ignoreSynchronization = true;
            });
            flow.execute(fn);
            flow.execute(function () {
                browser.ignoreSynchronization = false;
            });
        };

AND

it("should perform the Tast",
  function () {
    runNonAngular(function() {
      FUNCTION(); //helper function from another file
    })
  });

I also, stumbled upon this which goes in a little depth so I integrated that logic and still passes without actually performing the task:

var runNonAngular = function(fn) {
            var flow = protractor.promise.controlFlow();
            flow.execute(function () {
                browser.ignoreSynchronization = true;
            });
            flow.execute(fn);
            var allPromise = protractor.promise.all(fn);
            flow.execute(function () {
                browser.ignoreSynchronization = false;
            });
            return allPromise;
        };

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants