Skip to content

Instantly share code, notes, and snippets.

@tortuetorche
Last active May 28, 2021 10:41
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save tortuetorche/412fbac4f17db5e78e79 to your computer and use it in GitHub Desktop.
Save tortuetorche/412fbac4f17db5e78e79 to your computer and use it in GitHub Desktop.
Select2 v3.5 helpers for the WebDriver module of Codeception 2.1 (WIP)
<?php
namespace Helper;
// Select2 helpers for the jQuery based replacement for select boxes.
// See: http://select2.github.io/select2
// Author: Tortue Torche <tortuetorche@spam.me>
// License: MIT
//
// Installation:
// * Put this file in your 'tests/_support/Helper' directory
// * Add it in your 'tests/acceptance.suite.yml' file, like this:
// class_name: AcceptanceTester
// modules:
// enabled:
// - WebDriver:
// url: 'http://localhost:8000'
// # ...
// - \Helper\Select2
//
// * Then run ./vendor/bin/codecept build
class Select2 extends \Codeception\Module
{
/**
* @param $selector
* @param $optionText
* @param bool $expectedReturn Default to true
*
* @return string JavaScript
*/
protected function _optionIsSelectedForSelect2($selector, $optionText, $expectedReturn = true)
{
$returnFlag = $expectedReturn === true ? '' : '!';
return $script = <<<EOT
return (function (\$) {
var isSelected = false;
var values = \$("$selector").select2("data");
values = \$.isArray(values ) ? values : [values];
if (values && values.length > 0) {
isSelected = values.some(function (data) {
if (data && data.text && data.text === "$optionText") {
return data;
}
});
}
return ${returnFlag}isSelected;
}(jQuery));
EOT;
}
/**
* Wait until the select2 component is loaded
*
* @param $selector
* @param int $timeout seconds. Default to 5
*/
public function waitForSelect2($selector, $timeout = 5)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$I->waitForJS('return !!jQuery("'.$selector.'").data("select2");', $timeout);
}
/**
* Checks that the given option is not selected.
*
* @param $selector
* @param $optionText
* @param int $timeout seconds. Default to 5
*/
public function dontSeeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector, $timeout);
$script = $this->_optionIsSelectedForSelect2($selector, $optionText, false);
$I->waitForJS($script, $timeout);
}
/**
* Checks that the given option is selected.
*
* @param $selector
* @param $optionText
* @param int $timeout seconds. Default to 5
*/
public function seeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector, $timeout);
$script = $this->_optionIsSelectedForSelect2($selector, $optionText);
$I->waitForJS($script, $timeout);
}
/**
* Selects an option in a select2 component.
*
* $I->selectOptionForSelect2('#my_select2', 'Option value');
* $I->selectOptionForSelect2('#my_select2', ['Option value 1', 'Option value 2']);
* $I->selectOptionForSelect2('#my_select2', ['text' => 'Option text']);
* $I->selectOptionForSelect2('#my_select2', ['id' => 'Option value', 'text' => 'Option text']);
*
* @param $selector
* @param $option
* @param int $timeout seconds. Default to 1
*/
public function selectOptionForSelect2($selector, $option, $timeout = 1)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector, $timeout);
if (is_string($option) || (is_array($option) && array_values($option) === $option)) {
// $option is a string or a non associative array
$I->executeJS('jQuery("'.$selector.'").select2("val", '.json_encode($option).');', $timeout);
} else if(is_array($option)) {
$optionId = 'null';
if (isset($option['text']) && empty($option['id'])) {
$optionText = $option['text'];
$optionId = <<<EOT
function() {
if (!\$.expr[':'].textEquals) {
// Source: http://stackoverflow.com/a/26431267
\$.expr[':'].textEquals = function(el, i, m) {
var searchText = m[3];
return $(el).text().trim() === searchText;
}
}
// Find select option by text
return \$(\$("$selector").data("select2").select).find("option:textEquals('$optionText'):first").val();
}();
EOT;
}
$jsonOption = json_encode($option);
$script = <<<EOT
(function (\$) {
var option = $jsonOption;
if (!option.id) {
option.id = $optionId;
}
\$("$selector").select2("data", option);
\$(\$("$selector").data("select2").select).trigger('change');
}(jQuery));
EOT;
$I->executeJS($script, $timeout);
} else {
$I->fail();
}
}
/**
* Unselect an option in the given select2 component.
*
* @param $selector
* @param $option
* @param int $timeout seconds. Default to 1
*/
public function unselectOptionForSelect2($selector, $option = null, $timeout = 1)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector, $timeout);
if ($option && is_string($option)) {
$script = <<<EOT
(function (\$) {
var values = \$("$selector").select2("val");
var index = values.indexOf("$option");
if (index > -1) {
values.splice(index, 1);
}
\$("$selector").select2("val", values);
\$(\$("$selector").data("select2").select).trigger('change');
}(jQuery));
EOT;
$I->executeJS($script, $timeout);
} else {
$I->executeJS('jQuery("'.$selector.'").select2("val", "");', $timeout);
}
}
/**
* Open the Select2 component
* @param string $selector
*/
public function openSelect2($selector)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector);
$I->executeJS('jQuery("'.$selector.'").select2("open");');
}
/**
* Close the Select2 component
* @param string $selector
*/
public function closeSelect2($selector)
{
$I = $this->getAcceptanceModule();
$selector = $this->getSelect2Selector($selector);
$this->waitForSelect2($selector);
$I->executeJS('jQuery("'.$selector.'").select2("close");');
}
protected function getSelect2Selector($selector)
{
return preg_replace("/^\#((?!s2id_).+)$/", '#s2id_$1', $selector);
}
protected function getAcceptanceModule()
{
if (! $this->hasModule('WebDriver')) {
throw new \Exception("You must enable the WebDriver module", 1);
}
return $this->getModule('WebDriver');
}
}
@burzum
Copy link

burzum commented Nov 4, 2016

Has something changed? I found this nice looking code snippet but it's not really working for me. Do I get this right, that I'm using the id / selector of the input that has the select2 applied?

I'm using this version of select2, taken from my bower file "select2": "^4.0.3",.

There was 1 error:

---------
1) CompanyRegistrationCept: Register a company user
 Test  ..\wa3/.\codeception\acceptance\Users\CompanyRegistrationCept.php

  [Facebook\WebDriver\Exception\TimeOutException]

Scenario Steps:

 9. $I->openSelect2("#company-country-id") at codeception\acceptance\Users\CompanyRegistrationCept.php:19
 8. $I->fillField("company[city]","Cake") at codeception\acceptance\Users\CompanyRegistrationCept.php:13
 7. $I->fillField("company[street]","Baker Street") at codeception\acceptance\Users\CompanyRegistrationCept.php:12
 6. $I->fillField("company[tel1]","1234567890") at codeception\acceptance\Users\CompanyRegistrationCept.php:11
 5. $I->fillField("company[email]","acceptance-test-company@world-architects.com") at codeception\acceptance\Users\CompanyRegistrationCept.php:10
 4. $I->fillField("company[url]","http://cakephp.org") at codeception\acceptance\Users\CompanyRegistrationCept.php:9

#1  C:\xampp\htdocs\wa3\codeception\_support\Helper\Select2.php:61
#2  C:\xampp\htdocs\wa3\codeception\_support\Helper\Select2.php:189
#3  Helper\Select2->openSelect2
#4  C:\xampp\htdocs\wa3\codeception\_support\_generated\AcceptanceTesterActions.php:2840
#5  C:\xampp\htdocs\wa3\codeception\acceptance\Users\CompanyRegistrationCept.php:19

Fixed it

There are two issues. I had to return the selector without modifying it in getSelect2Selector() to simply return the passed selector directly, without changing it.

The other issue for me was that I passed an integer to the option:

$I->openSelect2('#company-country-id');
$I->selectOptionForSelect2('#company-country-id', '9');

I fixed this by adding an is_int() check to the code and casting it to string if it's an int.

Code

<?php
namespace Helper;

// Select2 helpers for the jQuery based replacement for select boxes.
// See: http://select2.github.io/select2
// Author: Tortue Torche <tortuetorche@spam.me>
// Author: Florian Krämer
// License: MIT
//
// Installation:
// * Put this file in your 'tests/_support/Helper' directory
// * Add it in your 'tests/acceptance.suite.yml' file, like this:
//    class_name: AcceptanceTester
//    modules:
//        enabled:
//            - WebDriver:
//              url: 'http://localhost:8000'
//              # ...
//            - \Helper\Select2
//
// * Then run ./vendor/bin/codecept build

class Select2 extends \Codeception\Module
{
    /**
     * @param $selector
     * @param $optionText
     * @param bool $expectedReturn Default to true
     *
     * @return string JavaScript
     */
    protected function _optionIsSelectedForSelect2($selector, $optionText, $expectedReturn = true)
    {
        $returnFlag = $expectedReturn === true ? '' : '!';
        return $script = <<<EOT
return (function (\$) {
  var isSelected = false;
  var values = \$("$selector").select2("data");
  values = \$.isArray(values ) ? values : [values];
  if (values && values.length > 0) {
    isSelected = values.some(function (data) {
      if (data && data.text && data.text === "$optionText") {
        return data;
      }
    });
  }
  return ${returnFlag}isSelected;
}(jQuery));
EOT;
    }

    /**
     * Wait until the select2 component is loaded
     *
     * @param $selector
     * @param int $timeout seconds. Default to 5
     */
    public function waitForSelect2($selector, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $I->waitForJS('return !!jQuery("'.$selector.'").data("select2");', $timeout);
    }

    /**
     * Checks that the given option is not selected.
     *
     * @param $selector
     * @param $optionText
     * @param int $timeout seconds. Default to 5
     */
    public function dontSeeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        $script = $this->_optionIsSelectedForSelect2($selector, $optionText, false);
        $I->waitForJS($script, $timeout);
    }

    /**
     * Checks that the given option is selected.
     *
     * @param $selector
     * @param $optionText
     * @param int $timeout seconds. Default to 5
     */
    public function seeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        $script = $this->_optionIsSelectedForSelect2($selector, $optionText);
        $I->waitForJS($script, $timeout);
    }

    /**
     * Selects an option in a select2 component.
     *
     *   $I->selectOptionForSelect2('#my_select2', 'Option value');
     *   $I->selectOptionForSelect2('#my_select2', ['Option value 1', 'Option value 2']);
     *   $I->selectOptionForSelect2('#my_select2', ['text' => 'Option text']);
     *   $I->selectOptionForSelect2('#my_select2', ['id' => 'Option value', 'text' => 'Option text']);
     *
     * @param $selector
     * @param $option
     * @param int $timeout seconds. Default to 1
     */
    public function selectOptionForSelect2($selector, $option, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);

        if (is_int($option)) {
        	$option = (string)$option;
        }

        if (is_string($option) || (is_array($option) && array_values($option) === $option)) {
            $I->executeJS('jQuery("'.$selector.'").select2("val", '.json_encode($option).');', $timeout);
        } else if(is_array($option)) {
            $optionId = 'null';
            if (isset($option['text']) && empty($option['id'])) {
                $optionText = $option['text'];
                $optionId = <<<EOT
function() {
  if (!\$.expr[':'].textEquals) {
    // Source: http://stackoverflow.com/a/26431267
    \$.expr[':'].textEquals = function(el, i, m) {
      var searchText = m[3];
      return $(el).text().trim() === searchText;
    }
  }
  // Find select option by text
  return \$(\$("$selector").data("select2").select).find("option:textEquals('$optionText'):first").val();
}();
EOT;
            }
            $jsonOption = json_encode($option);
            $script = <<<EOT
(function (\$) {
  var option = $jsonOption;
  if (!option.id) {
    option.id = $optionId;
  }
  \$("$selector").select2("data", option);
  \$(\$("$selector").data("select2").select).trigger('change');
}(jQuery));
EOT;
            $I->executeJS($script, $timeout);
        } else {
            $I->fail();
        }
    }

    /**
     * Unselect an option in the given select2 component.
     *
     * @param $selector
     * @param $option
     * @param int $timeout seconds. Default to 1
     */
    public function unselectOptionForSelect2($selector, $option = null, $timeout = 1)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        if ($option && is_string($option)) {
            $script = <<<EOT
(function (\$) {
  var values = \$("$selector").select2("val");
  var index = values.indexOf("$option");
  if (index > -1) {
    values.splice(index, 1);
  }
  \$("$selector").select2("val", values);
  \$(\$("$selector").data("select2").select).trigger('change');
}(jQuery));
EOT;
            $I->executeJS($script, $timeout);
        } else {
            $I->executeJS('jQuery("'.$selector.'").select2("val", "");', $timeout);
        }
    }

    /**
     * Open the Select2 component
     * @param string $selector
     */
    public function openSelect2($selector)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector);
        $I->executeJS('jQuery("'.$selector.'").select2("open");');
    }

    /**
     * Close the Select2 component
     * @param string $selector
     */
    public function closeSelect2($selector)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector);
        $I->executeJS('jQuery("'.$selector.'").select2("close");');
    }

    protected function getSelect2Selector($selector)
    {
        return $selector;
        //return preg_replace("/^\#((?!s2id_).+)$/", '#s2id_$1', $selector);
    }

    protected function getAcceptanceModule()
    {
        if (! $this->hasModule('WebDriver')) {
            throw new \Exception("You must enable the WebDriver module", 1);
        }

        return $this->getModule('WebDriver');
    }
}

@novrian
Copy link

novrian commented Jan 20, 2017

@drmmr763
Copy link

drmmr763 commented Jun 6, 2017

Just another thanks from me to you both @tortuetorche @burzum. Saved me tons of time this morning.

Fyi I didn't have IDs on some of my select2 elements, ended up selecting via the select element name and it worked great: $I->selectOptionForSelect2('select[name=searchStatus]', 'active');

@tomwalsh
Copy link

tomwalsh commented Oct 5, 2017

If you are using Select2 version 4.0 or greater this code will no longer work as expected. It still sort of works, but only in a few specific instances. Select2 has moved away from custom select2() based methods for accessing the data to more agnostic jQuery methods.

You can now use things like .val() and .find() directly on the select2 element instead of having to access them through the .select2() method on that element. This new version of the code works with Select2 >= 4.0.

Also, I am invoking the Select2 select2:select event on the elements when they are changed to ensure that code listening for those events is also triggered.

<?php // @codingStandardsIgnoreFile
namespace Helper;

// Select2 version 4.0 or greater helpers for the jQuery based replacement for select boxes.
// See: http://select2.github.io/select2
// Author: Tortue Torche <tortuetorche@spam.me>
// Author: Florian Krämer
// Author: Tom Walsh
// License: MIT
//
// Installation:
// * Put this file in your 'tests/_support/Helper' directory
// * Add it in your 'tests/acceptance.suite.yml' file, like this:
//    class_name: AcceptanceTester
//    modules:
//        enabled:
//            - WebDriver:
//              url: 'http://localhost:8000'
//              # ...
//            - \Helper\Select2
//
// * Then run ./vendor/bin/codecept build

class Select2 extends \Codeception\Module
{
    /**
     * @param $selector
     * @param $optionText
     * @param bool $expectedReturn Default to true
     *
     * @return string JavaScript
     */
    protected function _optionIsSelectedForSelect2($selector, $optionText, $expectedReturn = true)
    {
        $returnFlag = $expectedReturn === true ? '' : '!';
        return $script = <<<EOT
return (function (\$) {
  var isSelected = false;
  var values = \$("$selector").val();
  values = \$.isArray(values ) ? values : [values];
  if (values && values.length > 0) {
    isSelected = values.some(function (data) {
      if (data && data.text && data.text === "$optionText") {
        return data;
      }
    });
  }
  return ${returnFlag}isSelected;
}(jQuery));
EOT;
    }

    /**
     * Wait until the select2 component is loaded
     *
     * @param $selector
     * @param int $timeout seconds. Default to 5
     */
    public function waitForSelect2($selector, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $I->waitForJS('return !!jQuery("'.$selector.'").data("select2");', $timeout);
    }

    /**
     * Checks that the given option is not selected.
     *
     * @param $selector
     * @param $optionText
     * @param int $timeout seconds. Default to 5
     */
    public function dontSeeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        $script = $this->_optionIsSelectedForSelect2($selector, $optionText, false);
        $I->waitForJS($script, $timeout);
    }

    /**
     * Checks that the given option is selected.
     *
     * @param $selector
     * @param $optionText
     * @param int $timeout seconds. Default to 5
     */
    public function seeOptionIsSelectedForSelect2($selector, $optionText, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        $script = $this->_optionIsSelectedForSelect2($selector, $optionText);
        $I->waitForJS($script, $timeout);
    }

    /**
     * Selects an option in a select2 component.
     *
     *   $I->selectOptionForSelect2('#my_select2', 'Option value');
     *   $I->selectOptionForSelect2('#my_select2', ['Option value 1', 'Option value 2']);
     *   $I->selectOptionForSelect2('#my_select2', ['text' => 'Option text']);
     *   $I->selectOptionForSelect2('#my_select2', ['id' => 'Option value', 'text' => 'Option text']);
     *
     * @param $selector
     * @param $option
     * @param int $timeout seconds. Default to 1
     */
    public function selectOptionForSelect2($selector, $option, $timeout = 5)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);

        if (is_int($option)) {
        	$option = (string)$option;
        }

        if (is_string($option) || (is_array($option) && array_values($option) === $option)) {
            $I->executeJS('jQuery("'.$selector.'").select2("val", '.json_encode($option).');', $timeout);
            $I->executeJS('jQuery("'.$selector.'").trigger("select2:select").trigger("change");', $timeout);
        } else if(is_array($option)) {
            $optionId = 'null';
            if (isset($option['text']) && empty($option['id'])) {
                $optionText = $option['text'];
                $optionId = <<<EOT
function() {
  if (!\$.expr[':'].textEquals) {
    // Source: http://stackoverflow.com/a/26431267
    \$.expr[':'].textEquals = function(el, i, m) {
      var searchText = m[3];
      return $(el).text().trim() === searchText;
    }
  }
  // Find select option by text
  return \$("$selector").find("option:textEquals('$optionText'):first").val();
}();
EOT;
            }
            $jsonOption = json_encode($option);
            $script = <<<EOT
(function (\$) {
  var option = $jsonOption;
  if (!option.id) {
    option.id = $optionId;
  }
  \$("$selector").val(option.id).trigger('select2:select').trigger('change');
}(jQuery));
EOT;
            $I->executeJS($script, $timeout);
        } else {
            $I->fail();
        }
    }

    /**
     * Unselect an option in the given select2 component.
     *
     * @param $selector
     * @param $option
     * @param int $timeout seconds. Default to 1
     */
    public function unselectOptionForSelect2($selector, $option = null, $timeout = 1)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);
        if ($option && is_string($option)) {
            $script = <<<EOT
(function (\$) {
  var values = \$("$selector").select2("val");
  var index = values.indexOf("$option");
  if (index > -1) {
    values.splice(index, 1);
  }
  \$("$selector").select2("val", values);
  \$(\$("$selector").trigger("select2:select").trigger("change");
}(jQuery));
EOT;
            $I->executeJS($script, $timeout);
        } else {
            $I->executeJS('jQuery("'.$selector.'").select2("val", "");', $timeout);
            $I->executeJS('jQuery("'.$selector.'").trigger("select2:select").trigger("change");', $timeout);
        }
    }

    /**
     * Open the Select2 component
     * @param string $selector
     */
    public function openSelect2($selector)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector);
        $I->executeJS('jQuery("'.$selector.'").select2("open");');
    }

    /**
     * Close the Select2 component
     * @param string $selector
     */
    public function closeSelect2($selector)
    {
        $I = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector);
        $I->executeJS('jQuery("'.$selector.'").select2("close");');
    }

    protected function getSelect2Selector($selector)
    {
        return $selector;
        //return preg_replace("/^\#((?!s2id_).+)$/", '#s2id_$1', $selector);
    }

    protected function getAcceptanceModule()
    {
        if (! $this->hasModule('WebDriver')) {
            throw new \Exception("You must enable the WebDriver module", 1);
        }

        return $this->getModule('WebDriver');
    }
}

@Dasc3er
Copy link

Dasc3er commented Feb 23, 2019

The following code, extended form @tomwalsh, can help with options loaded through Ajax:

<?php // @codingStandardsIgnoreFile

namespace Helper;

/**
 * Select2 version 4.0 or greater helpers for the jQuery based replacement for select boxes (Ajax version).
 *
 * Installation:
 * - Put this file in your 'tests/_support/Helper' directory
 * - Add it in your 'tests/acceptance.suite.yml' file, like this:
 *      class_name: AcceptanceTester
 *      modules:
 *          enabled:
 *              - WebDriver:
 *              # ...
 *              - \Helper\Select2Ajax
 * - Run ./vendor/bin/codecept build
 *
 * @see http://select2.github.io/select2
 * @author Thomas Zilio
 *
 * @license MIT
 *
 */
class Select2Ajax extends Select2
{
    /**
     * Selects an option in a select2 component.
     *
     * @param $selector
     * @param $option
     * @param int $timeout seconds. Default to 1
     */
    public function selectByTextOrId($selector, $option, $timeout = 5)
    {
        $code = '
    $(options).each(function () {
        if($(this).text == "'.$option.'" || $(this).id == "'.$option.'") {
            $("'.$selector.'").selectSetNew(this.id, this.text);
        }
    });';

        $this->execute($selector, $timeout, $code);
    }

    public function selectByPosition($selector, $position, $timeout = 5)
    {
        $code = '
    var result = options['.$position.'];
    $("'.$selector.'").selectSetNew(result.id, result.text);';

        $this->execute($selector, $timeout, $code);
    }

    protected function execute($selector, $timeout, $code)
    {
        $t = $this->getAcceptanceModule();
        $selector = $this->getSelect2Selector($selector);
        $this->waitForSelect2($selector, $timeout);

        if (is_int($option)) {
            $option = (string) $option;
        }

        $results_selector = str_replace('#', '', $selector);

        $script = <<<EOT
$(document).ready(function() {
    var children = $("#select2-$results_selector-results").children();

    var options = [];
    children.each(function () {
        var data = $(this)[0];
        var output = Object.entries(data).map(([key, value]) => ({key,value}));

        if(output[0]) {
            options.push(output[0].value.data);
        }
    })

    $code
});
EOT;

        $t->executeJS($script, [$timeout]);
    }
}

And here is the way to use this class:

$I->openSelect2($selector);
$I->wait(1);
$I->selectByPosition($selector, $option_position);
$I->closeSelect2($selector);

By the moment, only the selection of one option at a time is supported and the selector must be an id.

The code uses the following utilities:

jQuery.fn.selectClear = function () {
    this.val([]).trigger("change");

    return this;
};

jQuery.fn.selectReset = function (placeholder) {
    this.selectClear();
    this.empty();

    if (placeholder != undefined) {
        this.next().find('.select2-selection__placeholder').text(placeholder);
        this.next().find('input.select2-search__field').attr('placeholder', placeholder);
    }

    return this;
};

jQuery.fn.selectSetNew = function (value, label) {
    this.selectReset();

    this.selectAdd([{
        'value': value,
        'text': label,
    }]);

    this.selectSet(value);

    return this;
};

jQuery.fn.selectSet = function (value) {
    this.val(value).trigger("change");

    return this;
};

jQuery.fn.selectAdd = function (values) {
    $this = this;

    values.forEach(function (item, index, array) {
        var option = $('<option/>', item);

        $this.append(option);
    });

    return this;
};

jQuery.fn.selectData = function () {
    var obj = $(this[0]);

    $select_obj = obj.select2('data');

    if ($select_obj[0] == undefined) {
        return undefined;
    } else {
        if ($select_obj[0].selected == false) {
            return $select_obj[0];
        } else {
            return $select_obj[0].element.dataset;
        }
    }
};

@tortuetorche
Copy link
Author

Hi folks,

Many thanks for all your contributions!
I wasn't expected this gist to be so popular!

Here an updated version which works with Codeception 4 (and certainly older version) and Select2 >= 4.0:
https://gist.github.com/tortuetorche/f4754f0867d9d0a272f1a3cea4073295

Have a good day,
Tortue Torche

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment