github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/job-versions-test.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  /* eslint-disable qunit/require-expect */
     7  /* eslint-disable qunit/no-conditional-assertions */
     8  import { currentURL } from '@ember/test-helpers';
     9  import { module, test } from 'qunit';
    10  import { setupApplicationTest } from 'ember-qunit';
    11  import { setupMirage } from 'ember-cli-mirage/test-support';
    12  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
    13  import Versions from 'nomad-ui/tests/pages/jobs/job/versions';
    14  import Layout from 'nomad-ui/tests/pages/layout';
    15  import moment from 'moment';
    16  
    17  let job;
    18  let namespace;
    19  let versions;
    20  
    21  module('Acceptance | job versions', function (hooks) {
    22    setupApplicationTest(hooks);
    23    setupMirage(hooks);
    24  
    25    hooks.beforeEach(async function () {
    26      server.create('node-pool');
    27      server.create('namespace');
    28      namespace = server.create('namespace');
    29  
    30      job = server.create('job', {
    31        namespaceId: namespace.id,
    32        createAllocations: false,
    33      });
    34      versions = server.db.jobVersions.where({ jobId: job.id });
    35  
    36      const managementToken = server.create('token');
    37      window.localStorage.nomadTokenSecret = managementToken.secretId;
    38  
    39      await Versions.visit({ id: `${job.id}@${namespace.id}` });
    40    });
    41  
    42    test('it passes an accessibility audit', async function (assert) {
    43      await a11yAudit(assert);
    44    });
    45  
    46    test('/jobs/:id/versions should list all job versions', async function (assert) {
    47      assert.equal(
    48        Versions.versions.length,
    49        versions.length,
    50        'Each version gets a row in the timeline'
    51      );
    52      assert.equal(document.title, `Job ${job.name} versions - Nomad`);
    53    });
    54  
    55    test('each version mentions the version number, the stability, and the submitted time', async function (assert) {
    56      const version = versions.sortBy('submitTime').reverse()[0];
    57      const formattedSubmitTime = moment(version.submitTime / 1000000).format(
    58        "MMM DD, 'YY HH:mm:ss ZZ"
    59      );
    60      const versionRow = Versions.versions.objectAt(0);
    61  
    62      assert.ok(
    63        versionRow.text.includes(`Version #${version.version}`),
    64        'Version #'
    65      );
    66      assert.equal(versionRow.stability, version.stable.toString(), 'Stability');
    67      assert.equal(versionRow.submitTime, formattedSubmitTime, 'Submit time');
    68    });
    69  
    70    test('all versions but the current one have a button to revert to that version', async function (assert) {
    71      let versionRowToRevertTo;
    72  
    73      Versions.versions.forEach((versionRow) => {
    74        if (versionRow.number === job.version) {
    75          assert.ok(versionRow.revertToButton.isHidden);
    76        } else {
    77          assert.ok(versionRow.revertToButton.isPresent);
    78  
    79          versionRowToRevertTo = versionRow;
    80        }
    81      });
    82  
    83      if (versionRowToRevertTo) {
    84        const versionNumberRevertingTo = versionRowToRevertTo.number;
    85        await versionRowToRevertTo.revertToButton.idle();
    86        await versionRowToRevertTo.revertToButton.confirm();
    87  
    88        const revertRequest = this.server.pretender.handledRequests.find(
    89          (request) => request.url.includes('revert')
    90        );
    91  
    92        assert.equal(
    93          revertRequest.url,
    94          `/v1/job/${job.id}/revert?namespace=${namespace.id}`
    95        );
    96  
    97        assert.deepEqual(JSON.parse(revertRequest.requestBody), {
    98          JobID: job.id,
    99          JobVersion: versionNumberRevertingTo,
   100        });
   101  
   102        assert.equal(currentURL(), `/jobs/${job.id}@${namespace.id}`);
   103      }
   104    });
   105  
   106    test('when reversion fails, the error message from the API is piped through to the alert', async function (assert) {
   107      const versionRowToRevertTo = Versions.versions.filter(
   108        (versionRow) => versionRow.revertToButton.isPresent
   109      )[0];
   110  
   111      if (versionRowToRevertTo) {
   112        const message = 'A plaintext error message';
   113        server.pretender.post('/v1/job/:id/revert', () => [500, {}, message]);
   114  
   115        await versionRowToRevertTo.revertToButton.idle();
   116        await versionRowToRevertTo.revertToButton.confirm();
   117  
   118        assert.ok(Layout.inlineError.isShown);
   119        assert.ok(Layout.inlineError.isDanger);
   120        assert.ok(Layout.inlineError.title.includes('Could Not Revert'));
   121        assert.equal(Layout.inlineError.message, message);
   122  
   123        await Layout.inlineError.dismiss();
   124  
   125        assert.notOk(Layout.inlineError.isShown);
   126      } else {
   127        assert.expect(0);
   128      }
   129    });
   130  
   131    test('when reversion has no effect, the error message explains', async function (assert) {
   132      const versionRowToRevertTo = Versions.versions.filter(
   133        (versionRow) => versionRow.revertToButton.isPresent
   134      )[0];
   135  
   136      if (versionRowToRevertTo) {
   137        // The default Mirage implementation updates the job version as passed in, this does nothing
   138        server.pretender.post('/v1/job/:id/revert', () => [200, {}, '{}']);
   139  
   140        await versionRowToRevertTo.revertToButton.idle();
   141        await versionRowToRevertTo.revertToButton.confirm();
   142  
   143        assert.ok(Layout.inlineError.isShown);
   144        assert.ok(Layout.inlineError.isWarning);
   145        assert.ok(Layout.inlineError.title.includes('Reversion Had No Effect'));
   146        assert.equal(
   147          Layout.inlineError.message,
   148          'Reverting to an identical older version doesn’t produce a new version'
   149        );
   150      } else {
   151        assert.expect(0);
   152      }
   153    });
   154  
   155    test('when the job for the versions is not found, an error message is shown, but the URL persists', async function (assert) {
   156      await Versions.visit({ id: 'not-a-real-job' });
   157  
   158      assert.equal(
   159        server.pretender.handledRequests
   160          .filter((request) => !request.url.includes('policy'))
   161          .findBy('status', 404).url,
   162        '/v1/job/not-a-real-job',
   163        'A request to the nonexistent job is made'
   164      );
   165      assert.equal(
   166        currentURL(),
   167        '/jobs/not-a-real-job/versions',
   168        'The URL persists'
   169      );
   170      assert.ok(Versions.error.isPresent, 'Error message is shown');
   171      assert.equal(Versions.error.title, 'Not Found', 'Error message is for 404');
   172    });
   173  });
   174  
   175  module('Acceptance | job versions (with client token)', function (hooks) {
   176    setupApplicationTest(hooks);
   177    setupMirage(hooks);
   178  
   179    hooks.beforeEach(async function () {
   180      server.create('node-pool');
   181      job = server.create('job', { createAllocations: false });
   182      versions = server.db.jobVersions.where({ jobId: job.id });
   183  
   184      server.create('token');
   185      const clientToken = server.create('token');
   186      window.localStorage.nomadTokenSecret = clientToken.secretId;
   187  
   188      await Versions.visit({ id: job.id });
   189    });
   190  
   191    test('reversion buttons are disabled when the token lacks permissions', async function (assert) {
   192      const versionRowWithReversion = Versions.versions.filter(
   193        (versionRow) => versionRow.revertToButton.isPresent
   194      )[0];
   195  
   196      if (versionRowWithReversion) {
   197        assert.ok(versionRowWithReversion.revertToButtonIsDisabled);
   198      } else {
   199        assert.expect(0);
   200      }
   201  
   202      window.localStorage.clear();
   203    });
   204  
   205    test('reversion buttons are available when the client token has permissions', async function (assert) {
   206      const REVERT_NAMESPACE = 'revert-namespace';
   207      window.localStorage.clear();
   208      const clientToken = server.create('token');
   209  
   210      server.create('namespace', { id: REVERT_NAMESPACE });
   211  
   212      const job = server.create('job', {
   213        groupCount: 0,
   214        createAllocations: false,
   215        shallow: true,
   216        noActiveDeployment: true,
   217        namespaceId: REVERT_NAMESPACE,
   218      });
   219  
   220      const policy = server.create('policy', {
   221        id: 'something',
   222        name: 'something',
   223        rulesJSON: {
   224          Namespaces: [
   225            {
   226              Name: REVERT_NAMESPACE,
   227              Capabilities: ['submit-job'],
   228            },
   229          ],
   230        },
   231      });
   232  
   233      clientToken.policyIds = [policy.id];
   234      clientToken.save();
   235  
   236      window.localStorage.nomadTokenSecret = clientToken.secretId;
   237  
   238      versions = server.db.jobVersions.where({ jobId: job.id });
   239      await Versions.visit({ id: job.id, namespace: REVERT_NAMESPACE });
   240      const versionRowWithReversion = Versions.versions.filter(
   241        (versionRow) => versionRow.revertToButton.isPresent
   242      )[0];
   243  
   244      if (versionRowWithReversion) {
   245        assert.ok(versionRowWithReversion.revertToButtonIsDisabled);
   246      } else {
   247        assert.expect(0);
   248      }
   249    });
   250  });