4

I have just recently started working on integrating a test framework into my AngularJS application, using Protractor do build the test cases & run the tests.

Having never used automated testing before, I am still new to it, and still getting to grips with how it works, how to write the test scripts, etc.

I am currently writing a test to check that the correct 'sub-menu' is displayed when the user hovers the cursor over a button on the main navigation bar. The test currently looks like this:

it('should display the Pages menu', function() {
    browser.actions().mouseMove(pagesMenuBtn).perform();
    browser.pause();
    expect(pageVarBrowser.isDisplayed()).toBeTruthy();
    browser.pause();
    browser.actions().mouseMove(userCircle).perform();
    browser.pause();
    expect(pageVarBrowser.isDisplayed()).toBeFalsy();
});

pagesMenuBtn is the menu button that will display a sub-menu listing the rest of the pages of the application when the user hovers the cursor over it- it has been defined with:

var pagesMenuBtn = element(by.id('.nav-container > model[type=href] > value["#/pages"]'));

pageVarBrowser is one of the buttons that is displayed on the sub-menu shown when hovering the cursor over pagesMenuBtn, and is defined with:

var pageVarBrowser = element(by.css('.nav-container > model[type=href] > value["#/pages/varbrowser"]'));

userCircle is simply another HTML element displayed in a different location on the page (just so that I could move the cursor somewhere off the pagesMenuBtn), and has been defined with:

var userCircle = element(by.id('icon-user-circle'));

This doesn't appear to be an issue with the syntax/ logic of the tests I've written/ am writing, but when I currently run my tests with the command protractor conf.js, the script starts up, and starts running through correctly, but when it reaches the above test, I get the following output in the console:

Error: Timeout = Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Does this mean that my tests have failed because they took too long to execute? If so, how can I fix this? Can I change the 'timeout' variable to a greater value?

In the console where I ran the command protractor conf.js, I get the following error message about the failure:

A Jasmine spec timed out. Resetting the WebDriver Control Flow

Failures:

1) MyApp should display the Pages menu Message: Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular While waiting for element with locator - Locator: By(css selector, *[id=".nav-container\ >\ model[type\=href]\ >\ value[\"#/pages\"]"]). The following tasks were pending: - $timeout: function () { clearEvents(type); } - $timeout: function setErrorMsg() { // Broadcast system timeout lock event $rootScope.$broadcast('um:LCK_TIMEOUT');

           // Save the error message
            dateObj.error = (
                'Timeout error data last fetched: ' + dateObj.localFormat
            );
        }
 - $timeout: function clearTags() {
            // Go through each of the tags
            angular.forEach(tags, function (tag){
                // Clear the value
                tag.value = null;

              // If the meta information is set
                if (tag.meta) {
                    // Clear the meta raw value
                    tag.meta.raw = null;
                }
            });

           // Empty the active alarms container
            activeAlarms = {};

           // Make sure the observers notice the value change
            notifyObservers();
        }

Anyone have any suggestions as to how I can get this test running correctly?

Edit

So, after removing a couple of the calls to browser.pause(); from my script, I actually got a different error message that says:

Failed: No element found using locator: By(css selector, *[id=".nav-container\ >\ model[type\=href]\ >\ value[\"#/pages\"]"])

So it appears it doesn't like the way I'm trying to get hold of the element from the sub-menu...

Edit

Although the initial error was complaining about a timeout, I'm now not sure that this is the cause of the issue. I removed the calls to browser.pause(); as mentioned in my earlier edit, and then started getting the error message above instead. I suspect that this is due to the location of the element that I'm trying to find/ the structure of the HTML in which it's nested. That HTML is:

<li data-ng-mouseenter="setMenuTop($event)" ng-show="menu.pages.length > 0 || menu.upages.length > 0 || canCreatePage">
    <a href="#/pages" id="pagesMenuBtn" target="_self"><i class="ti-layers"></i><span data-i18n="Pages"></span></a>
        <ul class="text-capitalize">
            <li ng-repeat="page in menu.pages"><a href="#/{{page.location}}" target="_self"><i class="ti-angle-right"></i><span data-i18n="{{page.title}}"></span></a></li>
            <li ng-repeat="upage in menu.upages">
                <div class="nav-menu-divider" data-ng-if="$index === 0"></div>
                <a href="#/{{upage.location}}" target="_self">
                    <i class="ti-angle-right"></i><span data-i18n="{{upage.title}}"></span>
                </a>
            </li>
            <li data-ng-show="canCreatePage">
                <div class="nav-menu-divider"></div>
                <a href="#/pages/create" target="_self">
                    <i class="ti-angle-right"></i><span data-i18n="Create Page"></span>
                </a>
            </li>
        </ul>
</li>

The element that I'm trying to get hold of is actually one of the ones created with the line:

<i class="ti-angle-right"></i><span data-i18n="{{upage.title}}"></span>

inside this block. The element itself is not explicitly written, but is created by the Angular ng-repeat function, as an element in a list... Anyone know how I would get hold of a specific element from within this list?

  • Wait, what are these browser.pause() calls - what happens if you remove them? Thanks. – alecxe Sep 26 '17 at 14:16
  • The reason I've added the browser.pause(); calls are so that the script doesn't execute entirely on its own- it means I have to input 'c' into the command line every time it pauses in order to cause the script to continue. My reason for doing this was to allow me to 'step through' the tests, and see exactly where/ when everything is happening. – Noble-Surfer Sep 26 '17 at 14:20
  • Right, but, is not it what contributes to reaching the jasmine timeout (which is the default 11 seconds)? What happens if you remove them? – alecxe Sep 26 '17 at 14:21
  • But it seems my error is being caused by the line: var pagesMenuBtn = element(by.id('.nav-container > model[type=href] > value["#/pages"]')) - apparently it can't find the element using that locator... – Noble-Surfer Sep 26 '17 at 14:22
  • Ah, wait, I think I know what that is, posting an answer.. – alecxe Sep 26 '17 at 14:23
2

The most important thing in this traceback is actually this part:

The following tasks were pending: - $timeout: function () { clearEvents(type); } - $timeout: function setErrorMsg() { // Broadcast system timeout lock event $rootScope.$broadcast('um:LCK_TIMEOUT');

That $timeout is an Angular service which Protractor waits for to complete and does not really play together with nicely. This is quite a wide-spread issue and the only workarounds are:

  • turn the sync off using browser.ignoreSynchronization = true; and use browser.wait() to tackle flakiness and make tests more stable and reliable
  • switch to $interval instead of $timeout if you have such a control over the application

As far as your latest error, see if adding a browser.wait() with an explicit visibility wait condition would make the error go away:

var EC = protractor.ExpectedConditions;

browser.wait(EC.visibilityOf(pagesMenuBtn), 5000);
browser.actions().mouseMove(pagesMenuBtn).perform();

And, you are defining the "by id" locator but trying to specify a CSS selector inside it:

element(by.id('.nav-container > model[type=href] > value["#/pages"]'))

But, even if you meant by.css, this selector is going to match a element with the tag name equal to value..I doubt that you have that tag on a page.

  • Thanks for your answer. I don't think the issue is actually anything to do with a timeout though... After removing a couple of the calls to browser.pause(), I started getting an error that said: No element found using locator: .... I think that this is possibly due to the location of the element I'm trying to get hold of/ the structure of the code in which it is located. I'll add that HTML to my OP now. – Noble-Surfer Sep 26 '17 at 14:53
  • HTML added. As I've mentioned in the edit to my OP, I think that this issue is actually happening because of the structure of the HTML/ how the HTML element is being created...? – Noble-Surfer Sep 26 '17 at 15:07
  • @someone2088 $timeout might still be an issue when all other problems are solved - I'll keep it in the answer for now. Regarding your latest update - added some more information to the answer - please, check it out. Thanks. – alecxe Sep 26 '17 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.