github.com/aminovpavel/nomad@v0.11.8/ui/tests/integration/multi-select-dropdown-test.js (about)

     1  import { findAll, find, click, focus, render, triggerKeyEvent } from '@ember/test-helpers';
     2  import { module, test } from 'qunit';
     3  import { setupRenderingTest } from 'ember-qunit';
     4  import sinon from 'sinon';
     5  import hbs from 'htmlbars-inline-precompile';
     6  
     7  const TAB = 9;
     8  const ESC = 27;
     9  const SPACE = 32;
    10  const ARROW_UP = 38;
    11  const ARROW_DOWN = 40;
    12  
    13  module('Integration | Component | multi-select dropdown', function(hooks) {
    14    setupRenderingTest(hooks);
    15  
    16    const commonProperties = () => ({
    17      label: 'This is the dropdown label',
    18      selection: [],
    19      options: [
    20        { key: 'consul', label: 'Consul' },
    21        { key: 'nomad', label: 'Nomad' },
    22        { key: 'terraform', label: 'Terraform' },
    23        { key: 'packer', label: 'Packer' },
    24        { key: 'vagrant', label: 'Vagrant' },
    25        { key: 'vault', label: 'Vault' },
    26      ],
    27      onSelect: sinon.spy(),
    28    });
    29  
    30    const commonTemplate = hbs`
    31      {{multi-select-dropdown
    32        label=label
    33        options=options
    34        selection=selection
    35        onSelect=onSelect}}
    36    `;
    37  
    38    test('component is initially closed', async function(assert) {
    39      const props = commonProperties();
    40      this.setProperties(props);
    41      await render(commonTemplate);
    42  
    43      assert.ok(find('.dropdown-trigger'), 'Trigger is shown');
    44      assert.equal(
    45        find('[data-test-dropdown-trigger]').textContent.trim(),
    46        props.label,
    47        'Trigger is appropriately labeled'
    48      );
    49      assert.notOk(find('[data-test-dropdown-options]'), 'Options are not rendered');
    50    });
    51  
    52    test('component opens the options dropdown when clicked', async function(assert) {
    53      const props = commonProperties();
    54      this.setProperties(props);
    55      await render(commonTemplate);
    56  
    57      await click('[data-test-dropdown-trigger]');
    58  
    59      await assert.ok(find('[data-test-dropdown-options]'), 'Options are shown now');
    60      await click('[data-test-dropdown-trigger]');
    61  
    62      assert.notOk(find('[data-test-dropdown-options]'), 'Options are hidden after clicking again');
    63    });
    64  
    65    test('all options are shown in the options dropdown, each with a checkbox input', async function(assert) {
    66      const props = commonProperties();
    67      this.setProperties(props);
    68      await render(commonTemplate);
    69  
    70      await click('[data-test-dropdown-trigger]');
    71  
    72      assert.equal(
    73        findAll('[data-test-dropdown-option]').length,
    74        props.options.length,
    75        'All options are shown'
    76      );
    77      findAll('[data-test-dropdown-option]').forEach((optionEl, index) => {
    78        const label = props.options[index].label;
    79        assert.equal(optionEl.textContent.trim(), label, `Correct label for ${label}`);
    80        assert.ok(optionEl.querySelector('input[type="checkbox"]'), 'Option contains a checkbox');
    81      });
    82    });
    83  
    84    test('onSelect gets called when an option is clicked', async function(assert) {
    85      const props = commonProperties();
    86      this.setProperties(props);
    87      await render(commonTemplate);
    88  
    89      await click('[data-test-dropdown-trigger]');
    90      await click('[data-test-dropdown-option] label');
    91  
    92      assert.ok(props.onSelect.called, 'onSelect was called');
    93      const newSelection = props.onSelect.getCall(0).args[0];
    94      assert.deepEqual(
    95        newSelection,
    96        [props.options[0].key],
    97        'onSelect was called with the first option key'
    98      );
    99    });
   100  
   101    test('the component trigger shows the selection count when there is a selection', async function(assert) {
   102      const props = commonProperties();
   103      props.selection = [props.options[0].key, props.options[1].key];
   104      this.setProperties(props);
   105      await render(commonTemplate);
   106  
   107      assert.ok(
   108        find('[data-test-dropdown-trigger] [data-test-dropdown-count]'),
   109        'The count is shown'
   110      );
   111      assert.equal(
   112        find('[data-test-dropdown-trigger] [data-test-dropdown-count]').textContent,
   113        props.selection.length,
   114        'The count is accurate'
   115      );
   116  
   117      await this.set('selection', []);
   118  
   119      assert.notOk(
   120        find('[data-test-dropdown-trigger] [data-test-dropdown-count]'),
   121        'The count is no longer shown when the selection is empty'
   122      );
   123    });
   124  
   125    test('pressing DOWN when the trigger has focus opens the options list', async function(assert) {
   126      const props = commonProperties();
   127      this.setProperties(props);
   128      await render(commonTemplate);
   129  
   130      await focus('[data-test-dropdown-trigger]');
   131      assert.notOk(find('[data-test-dropdown-options]'), 'Options are not shown on focus');
   132      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   133      assert.ok(find('[data-test-dropdown-options]'), 'Options are now shown');
   134      assert.equal(
   135        document.activeElement,
   136        find('[data-test-dropdown-trigger]'),
   137        'The dropdown trigger maintains focus'
   138      );
   139    });
   140  
   141    test('pressing DOWN when the trigger has focus and the options list is open focuses the first option', async function(assert) {
   142      const props = commonProperties();
   143      this.setProperties(props);
   144      await render(commonTemplate);
   145  
   146      await focus('[data-test-dropdown-trigger]');
   147      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   148      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   149      assert.equal(
   150        document.activeElement,
   151        find('[data-test-dropdown-option]'),
   152        'The first option now has focus'
   153      );
   154    });
   155  
   156    test('pressing TAB when the trigger has focus and the options list is open focuses the first option', async function(assert) {
   157      const props = commonProperties();
   158      this.setProperties(props);
   159      await render(commonTemplate);
   160  
   161      await focus('[data-test-dropdown-trigger]');
   162      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   163      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', TAB);
   164      assert.equal(
   165        document.activeElement,
   166        find('[data-test-dropdown-option]'),
   167        'The first option now has focus'
   168      );
   169    });
   170  
   171    test('pressing UP when the first list option is focused does nothing', async function(assert) {
   172      const props = commonProperties();
   173      this.setProperties(props);
   174      await render(commonTemplate);
   175  
   176      await click('[data-test-dropdown-trigger]');
   177  
   178      await focus('[data-test-dropdown-option]');
   179      await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ARROW_UP);
   180      assert.equal(
   181        document.activeElement,
   182        find('[data-test-dropdown-option]'),
   183        'The first option maintains focus'
   184      );
   185    });
   186  
   187    test('pressing DOWN when the a list option is focused moves focus to the next list option', async function(assert) {
   188      const props = commonProperties();
   189      this.setProperties(props);
   190      await render(commonTemplate);
   191  
   192      await click('[data-test-dropdown-trigger]');
   193  
   194      await focus('[data-test-dropdown-option]');
   195      await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ARROW_DOWN);
   196      assert.equal(
   197        document.activeElement,
   198        findAll('[data-test-dropdown-option]')[1],
   199        'The second option has focus'
   200      );
   201    });
   202  
   203    test('pressing DOWN when the last list option has focus does nothing', async function(assert) {
   204      const props = commonProperties();
   205      this.setProperties(props);
   206      await render(commonTemplate);
   207  
   208      await click('[data-test-dropdown-trigger]');
   209  
   210      await focus('[data-test-dropdown-option]');
   211      const optionEls = findAll('[data-test-dropdown-option]');
   212      const lastIndex = optionEls.length - 1;
   213  
   214      for (const [index, option] of optionEls.entries()) {
   215        await triggerKeyEvent(option, 'keydown', ARROW_DOWN);
   216  
   217        if (index < lastIndex) {
   218          assert.equal(document.activeElement, optionEls[index + 1], `Option ${index + 1} has focus`);
   219        }
   220      }
   221  
   222      await triggerKeyEvent(optionEls[lastIndex], 'keydown', ARROW_DOWN);
   223      assert.equal(
   224        document.activeElement,
   225        optionEls[lastIndex],
   226        `Option ${lastIndex} still has focus`
   227      );
   228    });
   229  
   230    test('onSelect gets called when pressing SPACE when a list option is focused', async function(assert) {
   231      const props = commonProperties();
   232      this.setProperties(props);
   233      await render(commonTemplate);
   234  
   235      await click('[data-test-dropdown-trigger]');
   236  
   237      await focus('[data-test-dropdown-option]');
   238      await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', SPACE);
   239  
   240      assert.ok(props.onSelect.called, 'onSelect was called');
   241      const newSelection = props.onSelect.getCall(0).args[0];
   242      assert.deepEqual(
   243        newSelection,
   244        [props.options[0].key],
   245        'onSelect was called with the first option key'
   246      );
   247    });
   248  
   249    test('list options have a positive tabindex and are therefore sequentially navigable', async function(assert) {
   250      const props = commonProperties();
   251      this.setProperties(props);
   252      await render(commonTemplate);
   253  
   254      await click('[data-test-dropdown-trigger]');
   255  
   256      findAll('[data-test-dropdown-option]').forEach(option => {
   257        assert.ok(parseInt(option.getAttribute('tabindex'), 10) > 0, 'tabindex is a positive value');
   258      });
   259    });
   260  
   261    test('the checkboxes inside list options have a negative tabindex and are therefore not sequentially navigable', async function(assert) {
   262      const props = commonProperties();
   263      this.setProperties(props);
   264      await render(commonTemplate);
   265  
   266      await click('[data-test-dropdown-trigger]');
   267  
   268      findAll('[data-test-dropdown-option]').forEach(option => {
   269        assert.ok(
   270          parseInt(option.querySelector('input[type="checkbox"]').getAttribute('tabindex'), 10) < 0,
   271          'tabindex is a negative value'
   272        );
   273      });
   274    });
   275  
   276    test('pressing ESC when the options list is open closes the list and returns focus to the dropdown trigger', async function(assert) {
   277      const props = commonProperties();
   278      this.setProperties(props);
   279      await render(commonTemplate);
   280  
   281      await focus('[data-test-dropdown-trigger]');
   282      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   283      await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
   284      await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ESC);
   285  
   286      assert.notOk(find('[data-test-dropdown-options]'), 'The options list is hidden once more');
   287      assert.equal(
   288        document.activeElement,
   289        find('[data-test-dropdown-trigger]'),
   290        'The trigger has focus'
   291      );
   292    });
   293  
   294    test('when there are no list options, an empty message is shown', async function(assert) {
   295      const props = commonProperties();
   296      props.options = [];
   297      this.setProperties(props);
   298      await render(commonTemplate);
   299  
   300      await click('[data-test-dropdown-trigger]');
   301      assert.ok(find('[data-test-dropdown-options]'), 'The dropdown is still shown');
   302      assert.ok(find('[data-test-dropdown-empty]'), 'The empty state is shown');
   303      assert.notOk(find('[data-test-dropdown-option]'), 'No options are shown');
   304    });
   305  });