Question

I have a partial with a select2 element utilizing Angular UI http://angular-ui.github.io/

The issue I am running into is that the element is required and although i have successfully set the field through the following code, the required attribute is not removed as Angular's model must not be updating due to the outside change and I am not sure how to either provide a $scope.apply() or utilize another function of Angular to continue the test.

First to allow for direct jQuery functions to run: (taken from How to execute jQuery from Angular e2e test scope?)

angular.scenario.dsl('jQueryFunction', function() {
return function(selector, functionName /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    return this.addFutureAction(functionName, function($window, $document, done) {
        var $ = $window.$; // jQuery inside the iframe
        var elem = $(selector);
        if (!elem.length) {
            return done('Selector ' + selector + ' did not match any elements.');
        }
        done(null, elem[functionName].apply(elem, args));
    });
};
});

Then to change the field value:

jQueryFunction('#s2id_autogen1', 'select2', 'open');
    jQueryFunction('#s2id_autogen1', 'select2', "val", "US");
    jQueryFunction('#s2id_autogen1', 'select2', 'data', {id: "US", text: "United States"});
    jQueryFunction('.select2-results li:eq(3)', 'click');
    jQueryFunction('#s2id_autogen1', 'trigger', 'change');
    jQueryFunction('#s2id_autogen1', 'select2', 'close');
    input('request._countrySelection').enter('US');

Note that not all of those functions are needed to reflect the changes to the ui, merely all that I have utilized to try and get this working...

Was it helpful?

Solution 2

I was unable to get this to work within the Karma test runner, however this became significantly easier within the protractor test suite.

To accomplish this within the protractor test suite I used the following to select the first select2 box on the page and select the first option within that box:

var select2 = element(by.css('div#s2id_autogen1'));
select2.click();
var lis = element.all(by.css('li.select2-results-dept-0'));
lis.then(function(li) {
    li[0].click();
});

The next select2 on the page has an id of s2id_autogen3

OTHER TIPS

To get this to work I consulted both Brian's answer and sinelaw, but it still failed in my case for two reasons:

  1. clicking on 'div#s2id_autogen1' does not open the select2 input for me, the selector I used was 'div#s2id_autogen1 a'
  2. getting the select2 element I would get the ElementNotVisibleError, probably because my select2 is inside a bootstrap modal, so I explicitly wait for the element to be visible before clicking it (you can read the original hint I read to use this here).

The resulting code is:

function select2ClickFirstItem(select2Id) {
        var select2Input;

        // Wait for select2 element to be visible
        browser.driver.wait(function() {
            select2Input =  element(by.css('#s2id_' + select2Id + ' a'));
            return select2Input;
        }).then(function() {
            select2Input.click();

            var items = element.all(by.css('.select2-results-dept-0'));
            browser.driver.wait(function () {
                return items.count().then(function (count) {
                    return 0 < count;
                });
            });
            items.get(0).click();
        });
    }

Hope it helps.

I'll second what @Brian said if you use protractor and the new karma this has worked for me:

function uiSelect(model, hasText) {
    var selector = element(by.model('query.cohort')),
        toggle = selector.element(by.css('.ui-select-toggle'));

    toggle.click();

    browser.driver.wait(function(){
        return selector.all(by.css('.ui-select-choices-row')).count().then(function(count){
            return count > 0;
        });
    }, 2000);

    var choice = selector.element(by.cssContainingText('.ui-select-choices-row',hasText));
    choice.click();
};

use it like:

if the value of the item you want to select is "Q3 2013" you can provide it the model of the selector and an exact or partial text match of the item you want to select.

uiSelect('query.cohort','Q3 2013');

or

uiSelect('query.cohort','Q3');

will both work

I made it work under Karma with following changes.

Add following DSL to the top of your e2e test file:

angular.scenario.dsl('jQueryFunction', function() {
  return function(selector, functionName /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    return this.addFutureAction(functionName, function($window, $document, done) {
      var $ = $window.$; // jQuery inside the iframe
      var elem = $(selector);
      if (!elem.length) {
        return done('Selector ' + selector + ' did not match any elements.');
      }
      done(null, elem[functionName].apply(elem, args));
    });
  };
});

Then to change select2 value in your scenario use

it('should narrow down organizations by likeness of name entered', function() {
  jQueryFunction('#s2id_physicianOrganization', 'select2', 'open');
  jQueryFunction('#s2id_physicianOrganization', 'select2', 'search', 'usa');
  expect(element('div.select2-result-label').count()).toBe(2);
});

Sometimes the select2 may take time to load, especially when working with ajax-loaded data. So when using protractor, and expanding on Brian's answer, here's a method I've found to be reliable:

function select2ClickFirstItem(select2Id) {
    var select2 = element(by.css('div#s2id_' + select2Id));
    select2.click();
    var items = element.all(by.css('.select2-results-dept-0'));
    browser.driver.wait(function () {
        return items.count().then(function (count) {
            return 0 < count;
        })
    });
    items.get(0).click();
}

This uses the fact that driver.wait can take a promise as a result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top