github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/acceptance/job-detail-test.js (about)

     1  /* eslint-disable ember/no-test-module-for */
     2  import { currentURL } from '@ember/test-helpers';
     3  import { module, test } from 'qunit';
     4  import { setupApplicationTest } from 'ember-qunit';
     5  import { selectChoose } from 'ember-power-select/test-support';
     6  import { setupMirage } from 'ember-cli-mirage/test-support';
     7  import moment from 'moment';
     8  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
     9  import moduleForJob from 'nomad-ui/tests/helpers/module-for-job';
    10  import JobDetail from 'nomad-ui/tests/pages/jobs/detail';
    11  import JobsList from 'nomad-ui/tests/pages/jobs/list';
    12  
    13  moduleForJob('Acceptance | job detail (batch)', 'allocations', () =>
    14    server.create('job', { type: 'batch', shallow: true })
    15  );
    16  moduleForJob('Acceptance | job detail (system)', 'allocations', () =>
    17    server.create('job', { type: 'system', shallow: true })
    18  );
    19  moduleForJob(
    20    'Acceptance | job detail (periodic)',
    21    'children',
    22    () => server.create('job', 'periodic', { shallow: true }),
    23    {
    24      'the default sort is submitTime descending': async function(job, assert) {
    25        const mostRecentLaunch = server.db.jobs
    26          .where({ parentId: job.id })
    27          .sortBy('submitTime')
    28          .reverse()[0];
    29  
    30        assert.equal(
    31          JobDetail.jobs[0].submitTime,
    32          moment(mostRecentLaunch.submitTime / 1000000).format('MMM DD HH:mm:ss ZZ')
    33        );
    34      },
    35    }
    36  );
    37  
    38  moduleForJob(
    39    'Acceptance | job detail (parameterized)',
    40    'children',
    41    () => server.create('job', 'parameterized', { shallow: true }),
    42    {
    43      'the default sort is submitTime descending': async (job, assert) => {
    44        const mostRecentLaunch = server.db.jobs
    45          .where({ parentId: job.id })
    46          .sortBy('submitTime')
    47          .reverse()[0];
    48  
    49        assert.equal(
    50          JobDetail.jobs[0].submitTime,
    51          moment(mostRecentLaunch.submitTime / 1000000).format('MMM DD HH:mm:ss ZZ')
    52        );
    53      },
    54    }
    55  );
    56  
    57  moduleForJob('Acceptance | job detail (periodic child)', 'allocations', () => {
    58    const parent = server.create('job', 'periodic', { childrenCount: 1, shallow: true });
    59    return server.db.jobs.where({ parentId: parent.id })[0];
    60  });
    61  
    62  moduleForJob('Acceptance | job detail (parameterized child)', 'allocations', () => {
    63    const parent = server.create('job', 'parameterized', { childrenCount: 1, shallow: true });
    64    return server.db.jobs.where({ parentId: parent.id })[0];
    65  });
    66  
    67  moduleForJob(
    68    'Acceptance | job detail (service)',
    69    'allocations',
    70    () => server.create('job', { type: 'service' }),
    71    {
    72      'the subnav links to deployment': async (job, assert) => {
    73        await JobDetail.tabFor('deployments').visit();
    74        assert.equal(currentURL(), `/jobs/${job.id}/deployments`);
    75      },
    76      'when the job is not found, an error message is shown, but the URL persists': async (
    77        job,
    78        assert
    79      ) => {
    80        await JobDetail.visit({ id: 'not-a-real-job' });
    81  
    82        assert.equal(
    83          server.pretender.handledRequests
    84            .filter(request => !request.url.includes('policy'))
    85            .findBy('status', 404).url,
    86          '/v1/job/not-a-real-job',
    87          'A request to the nonexistent job is made'
    88        );
    89        assert.equal(currentURL(), '/jobs/not-a-real-job', 'The URL persists');
    90        assert.ok(JobDetail.error.isPresent, 'Error message is shown');
    91        assert.equal(JobDetail.error.title, 'Not Found', 'Error message is for 404');
    92      },
    93    }
    94  );
    95  
    96  module('Acceptance | job detail (with namespaces)', function(hooks) {
    97    setupApplicationTest(hooks);
    98    setupMirage(hooks);
    99  
   100    let job, managementToken, clientToken;
   101  
   102    hooks.beforeEach(function() {
   103      server.createList('namespace', 2);
   104      server.create('node');
   105      job = server.create('job', {
   106        type: 'service',
   107        status: 'running',
   108        namespaceId: server.db.namespaces[1].name,
   109      });
   110      server.createList('job', 3, {
   111        namespaceId: server.db.namespaces[0].name,
   112      });
   113  
   114      managementToken = server.create('token');
   115      clientToken = server.create('token');
   116    });
   117  
   118    test('it passes an accessibility audit', async function(assert) {
   119      const namespace = server.db.namespaces.find(job.namespaceId);
   120      await JobDetail.visit({ id: job.id, namespace: namespace.name });
   121      await a11yAudit(assert);
   122    });
   123  
   124    test('when there are namespaces, the job detail page states the namespace for the job', async function(assert) {
   125      const namespace = server.db.namespaces.find(job.namespaceId);
   126      await JobDetail.visit({ id: job.id, namespace: namespace.name });
   127  
   128      assert.ok(JobDetail.statFor('namespace').text, 'Namespace included in stats');
   129    });
   130  
   131    test('when switching namespaces, the app redirects to /jobs with the new namespace', async function(assert) {
   132      const namespace = server.db.namespaces.find(job.namespaceId);
   133      const otherNamespace = server.db.namespaces.toArray().find(ns => ns !== namespace).name;
   134  
   135      await JobDetail.visit({ id: job.id, namespace: namespace.name });
   136  
   137      // TODO: Migrate to Page Objects
   138      await selectChoose('[data-test-namespace-switcher]', otherNamespace);
   139      assert.equal(currentURL().split('?')[0], '/jobs', 'Navigated to /jobs');
   140  
   141      const jobs = server.db.jobs
   142        .where({ namespace: otherNamespace })
   143        .sortBy('modifyIndex')
   144        .reverse();
   145  
   146      assert.equal(JobsList.jobs.length, jobs.length, 'Shows the right number of jobs');
   147      JobsList.jobs.forEach((jobRow, index) => {
   148        assert.equal(jobRow.name, jobs[index].name, `Job ${index} is right`);
   149      });
   150    });
   151  
   152    test('the exec button state can change between namespaces', async function(assert) {
   153      const job1 = server.create('job', {
   154        status: 'running',
   155        namespaceId: server.db.namespaces[0].id,
   156      });
   157      const job2 = server.create('job', {
   158        status: 'running',
   159        namespaceId: server.db.namespaces[1].id,
   160      });
   161  
   162      window.localStorage.nomadTokenSecret = clientToken.secretId;
   163  
   164      const policy = server.create('policy', {
   165        id: 'something',
   166        name: 'something',
   167        rulesJSON: {
   168          Namespaces: [
   169            {
   170              Name: job1.namespaceId,
   171              Capabilities: ['list-jobs', 'alloc-exec'],
   172            },
   173            {
   174              Name: job2.namespaceId,
   175              Capabilities: ['list-jobs'],
   176            },
   177          ],
   178        },
   179      });
   180  
   181      clientToken.policyIds = [policy.id];
   182      clientToken.save();
   183  
   184      await JobDetail.visit({ id: job1.id });
   185      assert.notOk(JobDetail.execButton.isDisabled);
   186  
   187      const secondNamespace = server.db.namespaces[1];
   188      await JobDetail.visit({ id: job2.id, namespace: secondNamespace.name });
   189      assert.ok(JobDetail.execButton.isDisabled);
   190    });
   191  
   192    test('the anonymous policy is fetched to check whether to show the exec button', async function(assert) {
   193      window.localStorage.removeItem('nomadTokenSecret');
   194  
   195      server.create('policy', {
   196        id: 'anonymous',
   197        name: 'anonymous',
   198        rulesJSON: {
   199          Namespaces: [
   200            {
   201              Name: 'default',
   202              Capabilities: ['list-jobs', 'alloc-exec'],
   203            },
   204          ],
   205        },
   206      });
   207  
   208      await JobDetail.visit({ id: job.id, namespace: server.db.namespaces[1].name });
   209      assert.notOk(JobDetail.execButton.isDisabled);
   210    });
   211  
   212    test('resource recommendations show when they exist and can be expanded, collapsed, and processed', async function(assert) {
   213      server.create('feature', { name: 'Dynamic Application Sizing' });
   214  
   215      job = server.create('job', {
   216        type: 'service',
   217        status: 'running',
   218        namespaceId: server.db.namespaces[1].name,
   219        groupsCount: 3,
   220        createRecommendations: true,
   221      });
   222  
   223      window.localStorage.nomadTokenSecret = managementToken.secretId;
   224      await JobDetail.visit({ id: job.id, namespace: server.db.namespaces[1].name });
   225  
   226      const groupsWithRecommendations = job.taskGroups.filter(group =>
   227        group.tasks.models.any(task => task.recommendations.models.length)
   228      );
   229      const jobRecommendationCount = groupsWithRecommendations.length;
   230  
   231      const firstRecommendationGroup = groupsWithRecommendations.models[0];
   232  
   233      assert.equal(JobDetail.recommendations.length, jobRecommendationCount);
   234  
   235      const recommendation = JobDetail.recommendations[0];
   236  
   237      assert.equal(recommendation.group, firstRecommendationGroup.name);
   238      assert.ok(recommendation.card.isHidden);
   239  
   240      const toggle = recommendation.toggleButton;
   241  
   242      assert.equal(toggle.text, 'Show');
   243  
   244      await toggle.click();
   245  
   246      assert.ok(recommendation.card.isPresent);
   247      assert.equal(toggle.text, 'Collapse');
   248  
   249      await toggle.click();
   250  
   251      assert.ok(recommendation.card.isHidden);
   252  
   253      await toggle.click();
   254  
   255      assert.equal(recommendation.card.slug.groupName, firstRecommendationGroup.name);
   256  
   257      await recommendation.card.acceptButton.click();
   258  
   259      assert.equal(JobDetail.recommendations.length, jobRecommendationCount - 1);
   260  
   261      await JobDetail.tabFor('definition').visit();
   262      await JobDetail.tabFor('overview').visit();
   263  
   264      assert.equal(JobDetail.recommendations.length, jobRecommendationCount - 1);
   265    });
   266  
   267    test('resource recommendations are not fetched when the feature doesn’t exist', async function(assert) {
   268      window.localStorage.nomadTokenSecret = managementToken.secretId;
   269      await JobDetail.visit({ id: job.id, namespace: server.db.namespaces[1].name });
   270  
   271      assert.equal(JobDetail.recommendations.length, 0);
   272  
   273      assert.equal(
   274        server.pretender.handledRequests.filter(request => request.url.includes('recommendations'))
   275          .length,
   276        0
   277      );
   278    });
   279  });