github.com/hernad/nomad@v1.6.112/ui/tests/integration/components/job-page/service-test.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  import { assign } from '@ember/polyfills';
     7  import { module, test } from 'qunit';
     8  import { setupRenderingTest } from 'ember-qunit';
     9  import { click, find, render } from '@ember/test-helpers';
    10  import hbs from 'htmlbars-inline-precompile';
    11  import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
    12  import {
    13    startJob,
    14    stopJob,
    15    purgeJob,
    16    expectError,
    17    expectDeleteRequest,
    18    expectStartRequest,
    19    expectPurgeRequest,
    20  } from './helpers';
    21  import Job from 'nomad-ui/tests/pages/jobs/detail';
    22  import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
    23  import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
    24  
    25  module('Integration | Component | job-page/service', function (hooks) {
    26    setupRenderingTest(hooks);
    27  
    28    hooks.beforeEach(function () {
    29      fragmentSerializerInitializer(this.owner);
    30      window.localStorage.clear();
    31      this.store = this.owner.lookup('service:store');
    32      this.server = startMirage();
    33      this.server.create('namespace');
    34      this.server.create('node-pool');
    35    });
    36  
    37    hooks.afterEach(function () {
    38      this.server.shutdown();
    39      window.localStorage.clear();
    40    });
    41  
    42    const commonTemplate = hbs`
    43      <JobPage::Service
    44        @job={{job}}
    45        @sortProperty={{sortProperty}}
    46        @sortDescending={{sortDescending}}
    47        @currentPage={{currentPage}}
    48        @gotoJob={{gotoJob}}
    49        @statusMode={{statusMode}}
    50        @setStatusMode={{setStatusMode}}
    51        />
    52    `;
    53  
    54    const commonProperties = (job) => ({
    55      job,
    56      sortProperty: 'name',
    57      sortDescending: true,
    58      currentPage: 1,
    59      gotoJob() {},
    60      statusMode: 'current',
    61      setStatusMode() {},
    62    });
    63  
    64    const makeMirageJob = (server, props = {}) =>
    65      server.create(
    66        'job',
    67        assign(
    68          {
    69            type: 'service',
    70            createAllocations: false,
    71            status: 'running',
    72          },
    73          props
    74        )
    75      );
    76  
    77    test('Stopping a job sends a delete request for the job', async function (assert) {
    78      assert.expect(1);
    79  
    80      const mirageJob = makeMirageJob(this.server);
    81      await this.store.findAll('job');
    82  
    83      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
    84  
    85      this.setProperties(commonProperties(job));
    86      await render(commonTemplate);
    87  
    88      await stopJob();
    89      expectDeleteRequest(assert, this.server, job);
    90    });
    91  
    92    test('Stopping a job without proper permissions shows an error message', async function (assert) {
    93      assert.expect(4);
    94  
    95      this.server.pretender.delete('/v1/job/:id', () => [403, {}, '']);
    96  
    97      const mirageJob = makeMirageJob(this.server);
    98      await this.store.findAll('job');
    99  
   100      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   101  
   102      this.setProperties(commonProperties(job));
   103      await render(commonTemplate);
   104  
   105      await stopJob();
   106      expectError(assert, 'Could Not Stop Job');
   107  
   108      await componentA11yAudit(this.element, assert);
   109    });
   110  
   111    test('Starting a job sends a post request for the job using the current definition', async function (assert) {
   112      assert.expect(2);
   113  
   114      const mirageJob = makeMirageJob(this.server, { status: 'dead' });
   115      await this.store.findAll('job');
   116  
   117      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   118  
   119      this.setProperties(commonProperties(job));
   120      await render(commonTemplate);
   121  
   122      await startJob();
   123      expectStartRequest(assert, this.server, job);
   124    });
   125  
   126    test('Starting a job without proper permissions shows an error message', async function (assert) {
   127      assert.expect(3);
   128  
   129      this.server.pretender.post('/v1/job/:id', () => [403, {}, '']);
   130  
   131      const mirageJob = makeMirageJob(this.server, { status: 'dead' });
   132      await this.store.findAll('job');
   133  
   134      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   135  
   136      this.setProperties(commonProperties(job));
   137      await render(commonTemplate);
   138  
   139      await startJob();
   140  
   141      await expectError(assert, 'Could Not Start Job');
   142    });
   143  
   144    test('Purging a job sends a purge request for the job', async function (assert) {
   145      assert.expect(1);
   146  
   147      const mirageJob = makeMirageJob(this.server, { status: 'dead' });
   148      await this.store.findAll('job');
   149  
   150      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   151  
   152      this.setProperties(commonProperties(job));
   153      await render(commonTemplate);
   154  
   155      await purgeJob();
   156      expectPurgeRequest(assert, this.server, job);
   157    });
   158  
   159    test('Recent allocations shows allocations in the job context', async function (assert) {
   160      assert.expect(3);
   161  
   162      this.server.create('node');
   163      const mirageJob = makeMirageJob(this.server, { createAllocations: true });
   164      await this.store.findAll('job');
   165  
   166      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   167  
   168      this.setProperties(commonProperties(job));
   169      await render(commonTemplate);
   170  
   171      const allocation = this.server.db.allocations
   172        .sortBy('modifyIndex')
   173        .reverse()[0];
   174      const allocationRow = Job.allocations.objectAt(0);
   175  
   176      assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'ID');
   177      assert.equal(
   178        allocationRow.taskGroup,
   179        allocation.taskGroup,
   180        'Task Group name'
   181      );
   182  
   183      await componentA11yAudit(this.element, assert);
   184    });
   185  
   186    test('Recent allocations caps out at five', async function (assert) {
   187      this.server.create('node');
   188      const mirageJob = makeMirageJob(this.server);
   189      this.server.createList('allocation', 10);
   190  
   191      await this.store.findAll('job');
   192  
   193      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   194  
   195      this.setProperties(commonProperties(job));
   196      await render(commonTemplate);
   197  
   198      assert.equal(Job.allocations.length, 5, 'Capped at 5 allocations');
   199      assert.ok(
   200        Job.viewAllAllocations.includes(job.get('allocations.length') + ''),
   201        `View link mentions ${job.get('allocations.length')} allocations`
   202      );
   203    });
   204  
   205    test('Recent allocations shows an empty message when the job has no allocations', async function (assert) {
   206      assert.expect(2);
   207  
   208      this.server.create('node');
   209      const mirageJob = makeMirageJob(this.server);
   210  
   211      await this.store.findAll('job');
   212  
   213      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   214  
   215      this.setProperties(commonProperties(job));
   216      await render(commonTemplate);
   217  
   218      assert.ok(
   219        Job.recentAllocationsEmptyState.headline.includes('No Allocations'),
   220        'No allocations empty message'
   221      );
   222  
   223      await componentA11yAudit(this.element, assert);
   224    });
   225  
   226    test('Active deployment can be promoted', async function (assert) {
   227      this.server.create('node');
   228      const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
   229  
   230      await this.store.findAll('job');
   231  
   232      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   233      const deployment = await job.get('latestDeployment');
   234  
   235      this.setProperties(commonProperties(job));
   236      await render(commonTemplate);
   237  
   238      await click('[data-test-promote-canary]');
   239  
   240      const requests = this.server.pretender.handledRequests;
   241  
   242      assert.ok(
   243        requests
   244          .filterBy('method', 'POST')
   245          .findBy('url', `/v1/deployment/promote/${deployment.get('id')}`),
   246        'A promote POST request was made'
   247      );
   248    });
   249  
   250    test('When promoting the active deployment fails, an error is shown', async function (assert) {
   251      assert.expect(4);
   252  
   253      this.server.pretender.post('/v1/deployment/promote/:id', () => [
   254        403,
   255        {},
   256        '',
   257      ]);
   258  
   259      this.server.create('node');
   260      const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
   261  
   262      await this.store.findAll('job');
   263  
   264      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   265  
   266      this.setProperties(commonProperties(job));
   267      await render(commonTemplate);
   268  
   269      await click('[data-test-promote-canary]');
   270  
   271      assert.equal(
   272        find('[data-test-job-error-title]').textContent,
   273        'Could Not Promote Deployment',
   274        'Appropriate error is shown'
   275      );
   276      assert.ok(
   277        find('[data-test-job-error-body]').textContent.includes('ACL'),
   278        'The error message mentions ACLs'
   279      );
   280  
   281      await componentA11yAudit(
   282        this.element,
   283        assert,
   284        'scrollable-region-focusable'
   285      ); //keyframe animation fades from opacity 0
   286  
   287      await click('[data-test-job-error-close]');
   288  
   289      assert.notOk(
   290        find('[data-test-job-error-title]'),
   291        'Error message is dismissable'
   292      );
   293    });
   294  
   295    test('Active deployment can be failed', async function (assert) {
   296      this.server.create('node');
   297      const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
   298  
   299      await this.store.findAll('job');
   300  
   301      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   302      const deployment = await job.get('latestDeployment');
   303  
   304      this.setProperties(commonProperties(job));
   305      await render(commonTemplate);
   306  
   307      await click('.active-deployment [data-test-fail]');
   308  
   309      const requests = this.server.pretender.handledRequests;
   310  
   311      assert.ok(
   312        requests
   313          .filterBy('method', 'POST')
   314          .findBy('url', `/v1/deployment/fail/${deployment.get('id')}`),
   315        'A fail POST request was made'
   316      );
   317    });
   318  
   319    test('When failing the active deployment fails, an error is shown', async function (assert) {
   320      assert.expect(4);
   321  
   322      this.server.pretender.post('/v1/deployment/fail/:id', () => [403, {}, '']);
   323  
   324      this.server.create('node');
   325      const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
   326  
   327      await this.store.findAll('job');
   328  
   329      const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
   330  
   331      this.setProperties(commonProperties(job));
   332      await render(commonTemplate);
   333  
   334      await click('.active-deployment [data-test-fail]');
   335  
   336      assert.equal(
   337        find('[data-test-job-error-title]').textContent,
   338        'Could Not Fail Deployment',
   339        'Appropriate error is shown'
   340      );
   341      assert.ok(
   342        find('[data-test-job-error-body]').textContent.includes('ACL'),
   343        'The error message mentions ACLs'
   344      );
   345  
   346      await componentA11yAudit(
   347        this.element,
   348        assert,
   349        'scrollable-region-focusable'
   350      ); //keyframe animation fades from opacity 0
   351  
   352      await click('[data-test-job-error-close]');
   353  
   354      assert.notOk(
   355        find('[data-test-job-error-title]'),
   356        'Error message is dismissable'
   357      );
   358    });
   359  });