github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/tests/acceptance/job-dispatch-test.js (about)

     1  /* eslint-disable ember/no-test-module-for */
     2  /* eslint-disable qunit/require-expect */
     3  /* eslint-disable qunit/no-conditional-assertions */
     4  import { module, test } from 'qunit';
     5  import { setupApplicationTest } from 'ember-qunit';
     6  import { setupMirage } from 'ember-cli-mirage/test-support';
     7  import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror';
     8  import JobDispatch from 'nomad-ui/tests/pages/jobs/dispatch';
     9  import JobDetail from 'nomad-ui/tests/pages/jobs/detail';
    10  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
    11  import { currentURL } from '@ember/test-helpers';
    12  
    13  const REQUIRED_INDICATOR = '*';
    14  
    15  moduleForJobDispatch('Acceptance | job dispatch', () => {
    16    server.createList('namespace', 2);
    17    const namespace = server.db.namespaces[0];
    18  
    19    return server.create('job', 'parameterized', {
    20      status: 'running',
    21      namespaceId: namespace.name,
    22    });
    23  });
    24  
    25  moduleForJobDispatch('Acceptance | job dispatch (with namespace)', () => {
    26    server.createList('namespace', 2);
    27    const namespace = server.db.namespaces[1];
    28  
    29    return server.create('job', 'parameterized', {
    30      status: 'running',
    31      namespaceId: namespace.name,
    32    });
    33  });
    34  
    35  function moduleForJobDispatch(title, jobFactory) {
    36    let job, namespace, managementToken, clientToken;
    37  
    38    module(title, function (hooks) {
    39      setupApplicationTest(hooks);
    40      setupCodeMirror(hooks);
    41      setupMirage(hooks);
    42  
    43      hooks.beforeEach(function () {
    44        // Required for placing allocations (a result of dispatching jobs)
    45        server.create('node');
    46  
    47        job = jobFactory();
    48        namespace = server.db.namespaces.find(job.namespaceId);
    49  
    50        managementToken = server.create('token');
    51        clientToken = server.create('token');
    52  
    53        window.localStorage.nomadTokenSecret = managementToken.secretId;
    54      });
    55  
    56      test('it passes an accessibility audit', async function (assert) {
    57        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
    58        await a11yAudit(assert);
    59      });
    60  
    61      test('the dispatch button is displayed with management token', async function (assert) {
    62        await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
    63        assert.notOk(JobDetail.dispatchButton.isDisabled);
    64      });
    65  
    66      test('the dispatch button is displayed when allowed', async function (assert) {
    67        window.localStorage.nomadTokenSecret = clientToken.secretId;
    68  
    69        const policy = server.create('policy', {
    70          id: 'dispatch',
    71          name: 'dispatch',
    72          rulesJSON: {
    73            Namespaces: [
    74              {
    75                Name: namespace.name,
    76                Capabilities: ['list-jobs', 'dispatch-job'],
    77              },
    78            ],
    79          },
    80        });
    81  
    82        clientToken.policyIds = [policy.id];
    83        clientToken.save();
    84  
    85        await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
    86        assert.notOk(JobDetail.dispatchButton.isDisabled);
    87  
    88        // Reset clientToken policies.
    89        clientToken.policyIds = [];
    90        clientToken.save();
    91      });
    92  
    93      test('the dispatch button is disabled when not allowed', async function (assert) {
    94        window.localStorage.nomadTokenSecret = clientToken.secretId;
    95  
    96        await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
    97        assert.ok(JobDetail.dispatchButton.isDisabled);
    98      });
    99  
   100      test('all meta fields are displayed', async function (assert) {
   101        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   102        assert.equal(
   103          JobDispatch.metaFields.length,
   104          job.parameterizedJob.MetaOptional.length +
   105            job.parameterizedJob.MetaRequired.length
   106        );
   107      });
   108  
   109      test('required meta fields are properly indicated', async function (assert) {
   110        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   111  
   112        JobDispatch.metaFields.forEach((f) => {
   113          const hasIndicator = f.label.includes(REQUIRED_INDICATOR);
   114          const isRequired = job.parameterizedJob.MetaRequired.includes(
   115            f.field.id
   116          );
   117  
   118          if (isRequired) {
   119            assert.ok(hasIndicator, `${f.label} contains required indicator.`);
   120          } else {
   121            assert.notOk(
   122              hasIndicator,
   123              `${f.label} doesn't contain required indicator.`
   124            );
   125          }
   126        });
   127      });
   128  
   129      test('job without meta fields', async function (assert) {
   130        const jobWithoutMeta = server.create('job', 'parameterized', {
   131          status: 'running',
   132          namespaceId: namespace.name,
   133          parameterizedJob: {
   134            MetaRequired: null,
   135            MetaOptional: null,
   136          },
   137        });
   138  
   139        await JobDispatch.visit({ id: `${jobWithoutMeta.id}@${namespace.name}` });
   140        assert.ok(JobDispatch.dispatchButton.isPresent);
   141      });
   142  
   143      test('payload text area is hidden when forbidden', async function (assert) {
   144        job.parameterizedJob.Payload = 'forbidden';
   145        job.save();
   146  
   147        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   148  
   149        assert.ok(JobDispatch.payload.emptyMessage.isPresent);
   150        assert.notOk(JobDispatch.payload.editor.isPresent);
   151      });
   152  
   153      test('payload is indicated as required', async function (assert) {
   154        const jobPayloadRequired = server.create('job', 'parameterized', {
   155          status: 'running',
   156          namespaceId: namespace.name,
   157          parameterizedJob: {
   158            Payload: 'required',
   159          },
   160        });
   161        const jobPayloadOptional = server.create('job', 'parameterized', {
   162          status: 'running',
   163          namespaceId: namespace.name,
   164          parameterizedJob: {
   165            Payload: 'optional',
   166          },
   167        });
   168  
   169        await JobDispatch.visit({
   170          id: `${jobPayloadRequired.id}@${namespace.name}`,
   171        });
   172  
   173        let payloadTitle = JobDispatch.payload.title;
   174        assert.ok(
   175          payloadTitle.includes(REQUIRED_INDICATOR),
   176          `${payloadTitle} contains required indicator.`
   177        );
   178  
   179        await JobDispatch.visit({
   180          id: `${jobPayloadOptional.id}@${namespace.name}`,
   181        });
   182  
   183        payloadTitle = JobDispatch.payload.title;
   184        assert.notOk(
   185          payloadTitle.includes(REQUIRED_INDICATOR),
   186          `${payloadTitle} doesn't contain required indicator.`
   187        );
   188      });
   189  
   190      test('dispatch a job', async function (assert) {
   191        function countDispatchChildren() {
   192          return server.db.jobs.where((j) =>
   193            j.id.startsWith(`${job.id}/`)
   194          ).length;
   195        }
   196  
   197        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   198  
   199        // Fill form.
   200        JobDispatch.metaFields.map((f) => f.field.input('meta value'));
   201        JobDispatch.payload.editor.fillIn('payload');
   202  
   203        const childrenCountBefore = countDispatchChildren();
   204        await JobDispatch.dispatchButton.click();
   205        const childrenCountAfter = countDispatchChildren();
   206  
   207        assert.equal(childrenCountAfter, childrenCountBefore + 1);
   208        assert.ok(
   209          currentURL().startsWith(`/jobs/${encodeURIComponent(`${job.id}/`)}`)
   210        );
   211        assert.ok(JobDetail.jobName);
   212      });
   213  
   214      test('fail when required meta field is empty', async function (assert) {
   215        // Make sure we have a required meta param.
   216        job.parameterizedJob.MetaRequired = ['required'];
   217        job.parameterizedJob.Payload = 'forbidden';
   218        job.save();
   219  
   220        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   221  
   222        // Fill only optional meta params.
   223        JobDispatch.optionalMetaFields.map((f) => f.field.input('meta value'));
   224  
   225        await JobDispatch.dispatchButton.click();
   226  
   227        assert.ok(JobDispatch.hasError, 'Dispatch error message is shown');
   228      });
   229  
   230      test('fail when required payload is empty', async function (assert) {
   231        job.parameterizedJob.MetaRequired = [];
   232        job.parameterizedJob.Payload = 'required';
   233        job.save();
   234  
   235        await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
   236        await JobDispatch.dispatchButton.click();
   237  
   238        assert.ok(JobDispatch.hasError, 'Dispatch error message is shown');
   239      });
   240    });
   241  }