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