github.com/hernad/nomad@v1.6.112/ui/tests/integration/components/variable-form-test.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  import { module, test } from 'qunit';
     7  import { setupRenderingTest } from 'ember-qunit';
     8  import { hbs } from 'ember-cli-htmlbars';
     9  import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
    10  import { click, typeIn, find, findAll, render } from '@ember/test-helpers';
    11  import { setupMirage } from 'ember-cli-mirage/test-support';
    12  import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror';
    13  import { codeFillable, code } from 'nomad-ui/tests/pages/helpers/codemirror';
    14  import percySnapshot from '@percy/ember';
    15  import {
    16    selectChoose,
    17    clickTrigger,
    18  } from 'ember-power-select/test-support/helpers';
    19  import faker from 'nomad-ui/mirage/faker';
    20  
    21  module('Integration | Component | variable-form', function (hooks) {
    22    setupRenderingTest(hooks);
    23    setupMirage(hooks);
    24    setupCodeMirror(hooks);
    25  
    26    test('passes an accessibility audit', async function (assert) {
    27      assert.expect(1);
    28      this.set(
    29        'mockedModel',
    30        server.create('variable', {
    31          keyValues: [{ key: '', value: '' }],
    32        })
    33      );
    34      await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
    35      await componentA11yAudit(this.element, assert);
    36    });
    37  
    38    test('shows a single row by default and modifies on "Add More" and "Delete"', async function (assert) {
    39      this.set(
    40        'mockedModel',
    41        server.create('variable', {
    42          keyValues: [{ key: '', value: '' }],
    43        })
    44      );
    45      assert.expect(7);
    46  
    47      await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
    48      assert.equal(
    49        findAll('div.key-value').length,
    50        1,
    51        'A single KV row exists by default'
    52      );
    53  
    54      assert
    55        .dom('[data-test-add-kv]')
    56        .isDisabled(
    57          'The "Add More" button is disabled until key and value are filled'
    58        );
    59  
    60      await typeIn('.key-value label:nth-child(1) input', 'foo');
    61  
    62      assert
    63        .dom('[data-test-add-kv]')
    64        .isDisabled(
    65          'The "Add More" button is still disabled with only key filled'
    66        );
    67  
    68      await typeIn('.key-value label:nth-child(2) input', 'bar');
    69  
    70      assert
    71        .dom('[data-test-add-kv]')
    72        .isNotDisabled(
    73          'The "Add More" button is no longer disabled after key and value are filled'
    74        );
    75  
    76      await click('[data-test-add-kv]');
    77  
    78      assert.equal(
    79        findAll('div.key-value').length,
    80        2,
    81        'A second KV row exists after adding a new one'
    82      );
    83  
    84      await typeIn('.key-value:last-of-type label:nth-child(1) input', 'foo');
    85      await typeIn('.key-value:last-of-type label:nth-child(2) input', 'bar');
    86  
    87      await click('[data-test-add-kv]');
    88  
    89      assert.equal(
    90        findAll('div.key-value').length,
    91        3,
    92        'A third KV row exists after adding a new one'
    93      );
    94  
    95      await click('.key-value button.delete-row');
    96  
    97      assert.equal(
    98        findAll('div.key-value').length,
    99        2,
   100        'Back down to two rows after hitting delete'
   101      );
   102    });
   103  
   104    module('editing and creating new key/value pairs', function () {
   105      test('it should allow each key/value row to toggle password visibility', async function (assert) {
   106        faker.seed(1);
   107        this.set(
   108          'mockedModel',
   109          server.create('variable', {
   110            keyValues: [{ key: 'foo', value: 'bar' }],
   111          })
   112        );
   113  
   114        assert.expect(6);
   115  
   116        await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
   117        await click('[data-test-add-kv]'); // add a second variable
   118  
   119        findAll('input.value-input').forEach((input, iter) => {
   120          assert.equal(
   121            input.getAttribute('type'),
   122            'password',
   123            `Value ${iter + 1} is hidden by default`
   124          );
   125        });
   126  
   127        await click('.key-value button.show-hide-values');
   128        const [firstRow, secondRow] = findAll('input.value-input');
   129  
   130        assert.equal(
   131          firstRow.getAttribute('type'),
   132          'text',
   133          'Only the row that is clicked on toggles visibility'
   134        );
   135        assert.equal(
   136          secondRow.getAttribute('type'),
   137          'password',
   138          'Rows that are not clicked remain obscured'
   139        );
   140  
   141        await click('.key-value button.show-hide-values');
   142        assert.equal(
   143          firstRow.getAttribute('type'),
   144          'password',
   145          'Only the row that is clicked on toggles visibility'
   146        );
   147        assert.equal(
   148          secondRow.getAttribute('type'),
   149          'password',
   150          'Rows that are not clicked remain obscured'
   151        );
   152        await percySnapshot(assert);
   153      });
   154    });
   155  
   156    test('Existing variable shows properties by default', async function (assert) {
   157      assert.expect(13);
   158      const keyValues = [
   159        { key: 'my-completely-normal-key', value: 'never' },
   160        { key: 'another key, but with spaces', value: 'gonna' },
   161        { key: 'once/more/with/slashes', value: 'give' },
   162        { key: 'and_some_underscores', value: 'you' },
   163        { key: 'and\\now/for-something_completely@different', value: 'up' },
   164      ];
   165  
   166      this.set(
   167        'mockedModel',
   168        server.create('variable', {
   169          path: 'my/path/to',
   170          keyValues,
   171        })
   172      );
   173      await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
   174      assert.equal(
   175        findAll('div.key-value').length,
   176        5,
   177        'Shows 5 existing key values'
   178      );
   179      assert.equal(
   180        findAll('button.delete-row').length,
   181        5,
   182        'Shows "delete" for all five rows'
   183      );
   184      assert.equal(
   185        findAll('[data-test-add-kv]').length,
   186        1,
   187        'Shows "add more" only on the last row'
   188      );
   189  
   190      findAll('div.key-value').forEach((row, idx) => {
   191        assert.equal(
   192          row.querySelector(`label:nth-child(1) input`).value,
   193          keyValues[idx].key,
   194          `Key ${idx + 1} is correct`
   195        );
   196  
   197        assert.equal(
   198          row.querySelector(`label:nth-child(2) input`).value,
   199          keyValues[idx].value,
   200          keyValues[idx].value
   201        );
   202      });
   203    });
   204  
   205    test('Prevent editing path input on existing variables', async function (assert) {
   206      assert.expect(3);
   207  
   208      const variable = await this.server.create('variable', {
   209        name: 'foo',
   210        namespace: 'bar',
   211        path: '/baz/bat',
   212        keyValues: [{ key: '', value: '' }],
   213      });
   214      variable.isNew = false;
   215      this.set('variable', variable);
   216      await render(hbs`<VariableForm @model={{this.variable}} />`);
   217      assert.dom('input.path-input').hasValue('/baz/bat', 'Path is set');
   218      assert
   219        .dom('input.path-input')
   220        .isDisabled('Existing variable is in disabled state');
   221  
   222      variable.isNew = true;
   223      variable.path = '';
   224      this.set('variable', variable);
   225      await render(hbs`<VariableForm @model={{this.variable}} />`);
   226      assert
   227        .dom('input.path-input')
   228        .isNotDisabled('New variable is not in disabled state');
   229    });
   230  
   231    module('Validation', function () {
   232      test('warns when you try to create a path that already exists', async function (assert) {
   233        this.server.createList('namespace', 3);
   234  
   235        this.set(
   236          'mockedModel',
   237          server.create('variable', {
   238            path: '',
   239            keyValues: [{ key: '', value: '' }],
   240          })
   241        );
   242  
   243        server.create('variable', {
   244          path: 'baz/bat',
   245        });
   246        server.create('variable', {
   247          path: 'baz/bat/qux',
   248          namespace: server.db.namespaces[2].id,
   249        });
   250  
   251        this.set('existingVariables', server.db.variables.toArray());
   252  
   253        await render(
   254          hbs`<VariableForm @model={{this.mockedModel}} @existingVariables={{this.existingVariables}} />`
   255        );
   256  
   257        await typeIn('.path-input', 'foo/bar');
   258        assert.dom('.duplicate-path-error').doesNotExist();
   259        assert.dom('.path-input').doesNotHaveClass('error');
   260  
   261        document.querySelector('.path-input').value = ''; // clear current input
   262        await typeIn('.path-input', 'baz/bat');
   263        assert.dom('.duplicate-path-error').exists();
   264        assert.dom('.path-input').hasClass('error');
   265  
   266        await clickTrigger('[data-test-variable-namespace-filter]');
   267        await selectChoose(
   268          '[data-test-variable-namespace-filter]',
   269          server.db.namespaces[2].id
   270        );
   271        assert.dom('.duplicate-path-error').doesNotExist();
   272        assert.dom('.path-input').doesNotHaveClass('error');
   273  
   274        document.querySelector('.path-input').value = ''; // clear current input
   275        await typeIn('.path-input', 'baz/bat/qux');
   276        assert.dom('.duplicate-path-error').exists();
   277        assert.dom('.path-input').hasClass('error');
   278      });
   279  
   280      test('warns you when you set a key with . in it', async function (assert) {
   281        this.set(
   282          'mockedModel',
   283          server.create('variable', {
   284            keyValues: [{ key: '', value: '' }],
   285          })
   286        );
   287  
   288        const testCases = [
   289          {
   290            name: 'valid key',
   291            key: 'superSecret2',
   292            warn: false,
   293          },
   294          {
   295            name: 'invalid key with dot',
   296            key: 'super.secret',
   297            warn: true,
   298          },
   299          {
   300            name: 'invalid key with slash',
   301            key: 'super/secret',
   302            warn: true,
   303          },
   304          {
   305            name: 'invalid key with emoji',
   306            key: 'supersecretspy🕵️',
   307            warn: true,
   308          },
   309          {
   310            name: 'unicode letters',
   311            key: '世界',
   312            warn: false,
   313          },
   314          {
   315            name: 'unicode numbers',
   316            key: '٣٢١',
   317            warn: false,
   318          },
   319          {
   320            name: 'unicode letters and numbers',
   321            key: '世٢界١',
   322            warn: false,
   323          },
   324        ];
   325        for (const tc of testCases) {
   326          await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
   327          await typeIn('[data-test-var-key]', tc.key);
   328          if (tc.warn) {
   329            assert.dom('.key-value-error').exists(tc.name);
   330          } else {
   331            assert.dom('.key-value-error').doesNotExist(tc.name);
   332          }
   333        }
   334      });
   335  
   336      test('warns you when you create a duplicate key', async function (assert) {
   337        this.set(
   338          'mockedModel',
   339          server.create('variable', {
   340            keyValues: [{ key: 'myKey', value: 'myVal' }],
   341          })
   342        );
   343  
   344        await render(hbs`<VariableForm @model={{this.mockedModel}} />`);
   345  
   346        await click('[data-test-add-kv]');
   347  
   348        const secondKey = document.querySelectorAll('[data-test-var-key]')[1];
   349        await typeIn(secondKey, 'myWonderfulKey');
   350        assert.dom('.key-value-error').doesNotExist();
   351  
   352        secondKey.value = '';
   353  
   354        await typeIn(secondKey, 'myKey');
   355        assert.dom('.key-value-error').exists();
   356      });
   357    });
   358  
   359    module('Views', function () {
   360      test('Allows you to swap between JSON and Key/Value Views', async function (assert) {
   361        this.set(
   362          'mockedModel',
   363          server.create('variable', {
   364            path: '',
   365            keyValues: [{ key: '', value: '' }],
   366          })
   367        );
   368  
   369        this.set(
   370          'existingVariables',
   371          server.createList('variable', 1, {
   372            path: 'baz/bat',
   373          })
   374        );
   375  
   376        this.set('view', 'table');
   377  
   378        await render(
   379          hbs`<VariableForm @model={{this.mockedModel}} @existingVariables={{this.existingVariables}} @view={{this.view}} />`
   380        );
   381        assert.dom('.key-value').exists();
   382        assert.dom('.CodeMirror').doesNotExist();
   383  
   384        this.set('view', 'json');
   385        assert.dom('.key-value').doesNotExist();
   386        assert.dom('.CodeMirror').exists();
   387      });
   388  
   389      test('Persists Key/Values table data to JSON', async function (assert) {
   390        faker.seed(1);
   391        assert.expect(2);
   392        const keyValues = [
   393          { key: 'foo', value: '123' },
   394          { key: 'bar', value: '456' },
   395        ];
   396        this.set(
   397          'mockedModel',
   398          server.create('variable', {
   399            path: '',
   400            keyValues,
   401          })
   402        );
   403  
   404        this.set('view', 'json');
   405  
   406        await render(
   407          hbs`<VariableForm @model={{this.mockedModel}} @view={{this.view}} />`
   408        );
   409  
   410        await percySnapshot(assert);
   411  
   412        const keyValuesAsJSON = keyValues.reduce((acc, { key, value }) => {
   413          acc[key] = value;
   414          return acc;
   415        }, {});
   416  
   417        assert.equal(
   418          code('.editor-wrapper').get(),
   419          JSON.stringify(keyValuesAsJSON, null, 2),
   420          'JSON editor contains the key values, stringified, by default'
   421        );
   422  
   423        this.set('view', 'table');
   424  
   425        await click('[data-test-add-kv]');
   426  
   427        await typeIn('.key-value:last-of-type label:nth-child(1) input', 'howdy');
   428        await typeIn(
   429          '.key-value:last-of-type label:nth-child(2) input',
   430          'partner'
   431        );
   432  
   433        this.set('view', 'json');
   434  
   435        assert.ok(
   436          code('[data-test-json-editor]').get().includes('"howdy": "partner"'),
   437          'JSON editor contains the new key value'
   438        );
   439      });
   440  
   441      test('Persists JSON data to Key/Values table', async function (assert) {
   442        const keyValues = [{ key: '', value: '' }];
   443        this.set(
   444          'mockedModel',
   445          server.create('variable', {
   446            path: '',
   447            keyValues,
   448          })
   449        );
   450  
   451        this.set('view', 'json');
   452  
   453        await render(
   454          hbs`<VariableForm @model={{this.mockedModel}} @view={{this.view}} />`
   455        );
   456  
   457        codeFillable('[data-test-json-editor]').get()(
   458          JSON.stringify({ golden: 'gate' }, null, 2)
   459        );
   460        this.set('view', 'table');
   461        assert.equal(
   462          find(`.key-value:last-of-type label:nth-child(1) input`).value,
   463          'golden',
   464          'Key persists from JSON to Table'
   465        );
   466  
   467        assert.equal(
   468          find(`.key-value:last-of-type label:nth-child(2) input`).value,
   469          'gate',
   470          'Value persists from JSON to Table'
   471        );
   472      });
   473    });
   474  });