<repo_name>milieuinfo/webcomponent-vl-ui-multiselect<file_sep>/test/e2e/pages/vl-multiselect.page.js
const VlMultiSelect = require('../components/vl-multiselect');
const {VlDatepicker} = require('vl-ui-datepicker').Test;
const {Page, Config} = require('vl-ui-core').Test;
const {Key} = require('selenium-webdriver');
const {By} = require('vl-ui-core').Test.Setup;

class VlMultiSelectPage extends Page {
  async _getMultiSelect(selector) {
    return new VlMultiSelect(this.driver, selector);
  }

  async getStandardMultiselect() {
    return this._getMultiSelect('#multiselect');
  }

  async getVoorgeselecteerdeMultiselect() {
    return this._getMultiSelect('#multiselect-voorgeselecteerd');
  }

  async getGegroepeerdeMultiselect() {
    return this._getMultiSelect('#multiselect-gegroepeerd');
  }

  async getErrorMultiselect() {
    return this._getMultiSelect('#multiselect-error');
  }

  async getSuccessMultiselect() {
    return this._getMultiSelect('#multiselect-success');
  }

  async getDisabledMultiselect() {
    return this._getMultiSelect('#multiselect-disabled');
  }

  async getMultiselectMetSpecifiekAantalResultaten() {
    return this._getMultiSelect('#multiselect-specifiek');
  }

  async getMultiselectMetOnbeperkteResultaten() {
    return this._getMultiSelect('#multiselect-onbeperkt');
  }

  async getDatepickerMultiselect() {
    return this._getMultiSelect('#multiselect-datepicker');
  }

  async getChangeEventMultiselect() {
    return this._getMultiSelect('#change-listener');
  }

  async getEnableDisableMethodeMultiselect() {
    return this._getMultiSelect('#vl-multiselect-enable-disable-methode');
  }

  async getSetGetMethodeMultiselect() {
    return this._getMultiSelect('#vl-multiselect-set-get-methode');
  }

  async getDatepicker() {
    return new VlDatepicker(this.driver, '#datepicker');
  }

  async enable() {
    return this.driver.findElement(By.css('#enable')).click();
  }

  async disable() {
    return this.driver.findElement(By.css('#disable')).click();
  }

  async kiesDuitsland() {
    return this.driver.findElement(By.css('#duitsland')).click();
  }

  async kiesBelgieEnNederland() {
    return this.driver.findElement(By.css('#be-nl')).click();
  }

  async verwijderSelectie() {
    return this.driver.findElement(By.css('#verwijder')).click();
  }

  async closeAnyOpenDropdowns() {
    const body = await this.driver.findElement(By.css('body'));
    await body.sendKeys(Key.ESCAPE);
  }

  async load() {
    await super.load(Config.baseUrl + '/demo/vl-multiselect.html');
  }
}

module.exports = VlMultiSelectPage;
<file_sep>/index.js
module.exports = {
  Test: {
    VlMultiSelect: require('./test/e2e/components/vl-multiselect'),
  },
};
<file_sep>/dist/vl-multiselect.js
import {define} from '/node_modules/vl-ui-core/dist/vl-core.js';
import {VlSelect} from '/node_modules/vl-ui-select/dist/vl-select.js';

/**
 * VlMultiSelect
 * @class
 * @classdesc Gebruik een multiselect om een gebruiker toe te laten om in een lijst van vooropgestelde keuzes te zoeken, en enkele of alle passende keuzes te selecteren.
 *
 * @extends VlSelect
 *
 * @property {boolean} data-vl-block - Attribuut wordt gebruikt om ervoor te zorgen dat de textarea getoond wordt als een block element en bijgevolg de breedte van de parent zal aannemen.
 * @property {boolean} data-vl-error - Attribuut wordt gebruikt om aan te duiden dat het select element verplicht is of ongeldige tekst bevat.
 * @property {boolean} data-vl-success - Attribuut wordt gebruikt om aan te duiden dat het select element correct werd ingevuld.
 * @property {boolean} disabled - Attribuut wordt gebruikt om te voorkomen dat de gebruiker iets kan kiezen uit het select element.
 *
 * @see {@link http://www.github.com/milieuinfo/webcomponent-vl-ui-multiselect/releases/latest|Release notes}
 * @see {@link http://www.github.com/milieuinfo/webcomponent-vl-ui-multiselect/issues|Issues}
 * @see {@link https://webcomponenten.omgeving.vlaanderen.be/demo/vl-multiselect.html|Demo}
 */
export class VlMultiSelect extends VlSelect {
  static get readyEvent() {
    return 'VlMultiSelectReady';
  }

  connectedCallback() {
    this.classList.add('vl-multiselect');
    this.setAttribute('multiple', '');
    this.setAttribute('data-vl-multiselect', '');

    const hasSelected = this.hasSelected();
    if (!hasSelected) this.value = undefined;

    super.connectedCallback();
  }

  hasSelected() {
    const options = Array.from(this.querySelectorAll('option'));

    return options.some((option) => {
      return option.hasAttribute('selected');
    });
  }

  /**
   * Geeft de ready event naam.
   *
   * @return {string}
   */
  get readyEvent() {
    return VlMultiSelect.readyEvent;
  }

  /**
   * Zet het geselecteerd option element op basis van de option value.
   *
   * @param {string} values - De option value van het option element dat gekozen moet worden.
   * @return {void}
   */
  set values(values) {
    values.forEach((value) => {
      super.value = value;
    });
  }

  /**
   * Geeft de waarde van de geselecteerde option elementen.
   *
   * @return {string[]}
   */
  get values() {
    return [...this.selectedOptions].map((option) => {
      return option.value || '';
    });
  }

  get _dataVlSelectAttribute() {
    return this.getAttribute('data-vl-multiselect');
  }

  get _inputElement() {
    return this.parentElement.querySelector('input');
  }

  /**
   * Geef focus aan het select input element.
   */
  focus() {
    this._inputElement.focus();
  }
}

window.customElements.whenDefined('vl-select').then(() => define('vl-multiselect', VlMultiSelect, {extends: 'select'}));
<file_sep>/test/e2e/components/pill.js
const {By} = require('vl-ui-core').Test.Setup;

class Pill {
  constructor(webElement) {
    this.webElement = webElement;
  }

  async value() {
    return this.webElement.getAttribute('data-value');
  }

  async isSelected() {
    return true;
  }

  async text() {
    const span = await this.webElement.findElement(By.css('span'));
    return span.getAttribute('innerText');
  }

  async remove() {
    const closeButton = await this.webElement.findElements(By.css('.vl-pill__close'));
    if (closeButton.length < 1) {
      throw new Error('Dit item kan niet verwijderd worden!');
    }
    return closeButton[0].click();
  }
}

module.exports = {Pill};
<file_sep>/test/e2e/multiselect.test.js

const {assert, getDriver} = require('vl-ui-core').Test.Setup;
const VlMultiSelectPage = require('./pages/vl-multiselect.page');

describe('vl-multiselect', async () => {
  let vlMultiSelectPage;

  before(async () => {
    vlMultiSelectPage = new VlMultiSelectPage(getDriver());
    return vlMultiSelectPage.load();
  });

  afterEach(async () => {
    await vlMultiSelectPage.closeAnyOpenDropdowns();
  });

  it('als gebruiker kan ik de geselecteerde items opvragen', async () => {
    const multiselect = await vlMultiSelectPage.getVoorgeselecteerdeMultiselect();
    const selectedItems = await multiselect.getSelectedItems();
    assert.isTrue(selectedItems.some((item) => item.text === 'Brugge'));
    assert.isTrue(selectedItems.some((item) => item.value === 'Bruges'));
  });

  it('als gebruiker kan ik een optie selecteren en deze zal dan in de combobox getoond worden', async () => {
    const multiselect = await vlMultiSelectPage.getStandardMultiselect();
    const selectedItems = await multiselect.getSelectedItems();
    const unselectedItems = await multiselect.getUnselectedItems();
    const itemToSelect = unselectedItems.find((item) => item.text === 'Duitsland');

    assert.isFalse(selectedItems.some((i) => i.text === 'Duitsland'));
    await multiselect.select(itemToSelect);
    const selectedItemsAfterUpdate = await multiselect.getSelectedItems();
    assert.isTrue(selectedItemsAfterUpdate.some((i) => i.text === 'Duitsland'));
  });

  it('de default multiselect heeft geen error of success attribuut', async () => {
    const multiselect = await vlMultiSelectPage.getStandardMultiselect();
    await assert.eventually.isFalse(multiselect.isError());
    await assert.eventually.isFalse(multiselect.isSuccess());
  });

  it('als gebruiker kan ik een gekozen optie verwijderen', async () => {
    const multiselect = await vlMultiSelectPage.getGegroepeerdeMultiselect();
    const selectedItems = await multiselect.getSelectedItems();
    const itemToUnSelect = selectedItems.find((item) => item.text === 'Brugge');

    assert.isTrue(selectedItems.some((i) => i.text === 'Brugge'));
    await multiselect.unselect(itemToUnSelect);
    const selectedItemsAfterUnselect = await multiselect.getSelectedItems();
    assert.isFalse(selectedItemsAfterUnselect.some((i) => i.text === 'Brugge'));
  });

  it('als gebruiker kan ik een multiselect definieren als error multiselect', async () => {
    const multiselect = await vlMultiSelectPage.getErrorMultiselect();
    await assert.eventually.isTrue(multiselect.isError());
  });

  it('als gebruiker kan ik een multiselect definieren als success multiselect', async () => {
    const multiselect = await vlMultiSelectPage.getSuccessMultiselect();
    await assert.eventually.isTrue(multiselect.isSuccess());
  });

  it('als gebruiker kan ik een multiselect definieren als disabled', async () => {
    const multiselect = await vlMultiSelectPage.getDisabledMultiselect();
    await assert.eventually.isTrue(multiselect.isDisabled());
  });

  it('het aantal resultaten van een zoekopdracht kan beperkt worden', async () => {
    const multiselect = await vlMultiSelectPage.getMultiselectMetSpecifiekAantalResultaten();
    await multiselect.searchByPartialText('straat');
    await assert.eventually.equal(multiselect.getNumberOfSearchResults(), 5);
  });

  it('het aantal resultaten van een zoekopdracht is standaard niet beperkt', async () => {
    const multiselect = await vlMultiSelectPage.getMultiselectMetOnbeperkteResultaten();
    await multiselect.searchByPartialText('straat');
    await assert.eventually.isAbove(multiselect.getNumberOfSearchResults(), 4);
  });

  it('als gebruiker kan ik luisteren naar een change event en onChange zal er een extra attribuut gezet worden', async () => {
    const multiselect = await vlMultiSelectPage.getChangeEventMultiselect();
    await assert.eventually.isFalse(multiselect.hasAttribute('changed'));
    await multiselect.selectByValue('Germany');
    await assert.eventually.isTrue(multiselect.hasAttribute('changed'));
  });

  it('als gebruiker kan ik de multiselect via enable/disable-methode in/uit-schakelen', async () => {
    const multiselect = await vlMultiSelectPage.getEnableDisableMethodeMultiselect();
    await vlMultiSelectPage.disable();
    await assert.eventually.isTrue(multiselect.isDisabled());
    await vlMultiSelectPage.enable();
    await assert.eventually.isFalse(multiselect.isDisabled());
  });

  it('als gebruiker kan ik programmatorisch (een) waarde(s) toevoegen/verwijderen in de multiselect', async () => {
    const multiselect = await vlMultiSelectPage.getSetGetMethodeMultiselect();
    await assert.eventually.isEmpty(multiselect.getSelectedItems());
    await vlMultiSelectPage.kiesDuitsland();
    const de = await multiselect.getSelectedItems();
    assert.isTrue(de.some((i) => i.text === 'Duitsland'));
    await vlMultiSelectPage.kiesBelgieEnNederland();
    const nlBe = await multiselect.getSelectedItems();
    assert.isTrue(de.some((i) => i.text === 'Duitsland'));
    assert.isTrue(nlBe.some((i) => i.text === 'België'));
    assert.isTrue(nlBe.some((i) => i.text === 'Nederland'));
    await assert.eventually.lengthOf(multiselect.getSelectedItems(), 3);
    await vlMultiSelectPage.verwijderSelectie();
    await assert.eventually.lengthOf(multiselect.getSelectedItems(), 2);
  });

  it('als een multiselect boven een datepicker gerenderd wordt, kunnen zowel de multiselect als de datepicker correct gebruikt worden', async () => {
    const multiselect = await vlMultiSelectPage.getDatepickerMultiselect();
    const datepicker = await vlMultiSelectPage.getDatepicker();

    await datepicker.selectYear(2018);
    await datepicker.selectMonth('augustus');
    await datepicker.selectDay(29);
    await assert.eventually.equal(datepicker.getInputValue(), `29.08.2018`);

    await multiselect.selectByValue('Germany');
    const selectedItems = await multiselect.getSelectedItems();
    assert.isTrue(selectedItems.some((item) => item.text === 'Duitsland'));
  });

  it('als gebruiker kan ik opties groeperen', async () => {
    const multiselect = await vlMultiSelectPage.getGegroepeerdeMultiselect();
    await assert.eventually.isTrue(multiselect.isGrouped());
    await assert.eventually.isTrue(multiselect.hasHeadings());
  });

  it('de datepicker kan niet geopend worden wanneer de multiselect geopend is', async () => {
    const multiselect = await vlMultiSelectPage.getDatepickerMultiselect();
    const datepicker = await vlMultiSelectPage.getDatepicker();
    await multiselect.open();
    await assert.isRejected(datepicker.selectDay(4));
  });

  it('als de gebruiker de multiselect opent wanneer de datepicker zichtbaar is, zal de datepicker verdwijnen', async () => {
    const multiselect = await vlMultiSelectPage.getDatepickerMultiselect();
    const datepicker = await vlMultiSelectPage.getDatepicker();

    await assert.eventually.isFalse(datepicker.isOpen());
    await datepicker.open();
    await assert.eventually.isTrue(datepicker.isOpen());
    await multiselect.open();
    await assert.eventually.isTrue(multiselect.isOpen());
    await assert.eventually.isFalse(datepicker.isOpen());
  });
});
