github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/tests/integration/components/variable-form-test.js (about)

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