github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/job-clients-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  import { currentURL } from '@ember/test-helpers';
     8  import { module, test } from 'qunit';
     9  import { setupApplicationTest } from 'ember-qunit';
    10  import { setupMirage } from 'ember-cli-mirage/test-support';
    11  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
    12  import Clients from 'nomad-ui/tests/pages/jobs/job/clients';
    13  import setPolicy from 'nomad-ui/tests/utils/set-policy';
    14  
    15  let job;
    16  let clients;
    17  
    18  const makeSearchableClients = (server, job) => {
    19    Array(10)
    20      .fill(null)
    21      .map((_, index) => {
    22        const node = server.create('node', {
    23          id: index < 5 ? `ffffff-dddddd-${index}` : `111111-222222-${index}`,
    24          datacenter: 'dc1',
    25          status: 'ready',
    26        });
    27        server.create('allocation', { jobId: job.id, nodeId: node.id });
    28      });
    29  };
    30  
    31  module('Acceptance | job clients', function (hooks) {
    32    setupApplicationTest(hooks);
    33    setupMirage(hooks);
    34  
    35    hooks.beforeEach(function () {
    36      setPolicy({
    37        id: 'node-read',
    38        name: 'node-read',
    39        rulesJSON: {
    40          Node: {
    41            Policy: 'read',
    42          },
    43        },
    44      });
    45  
    46      server.createList('node-pool', 5);
    47      clients = server.createList('node', 12, {
    48        datacenter: 'dc1',
    49        status: 'ready',
    50      });
    51      // Job with 1 task group.
    52      job = server.create('job', {
    53        status: 'running',
    54        datacenters: ['dc1'],
    55        type: 'sysbatch',
    56        resourceSpec: ['M: 256, C: 500'],
    57        createAllocations: false,
    58      });
    59      clients.forEach((c) => {
    60        server.create('allocation', { jobId: job.id, nodeId: c.id });
    61      });
    62  
    63      // Create clients without allocations to have some 'not scheduled' job status.
    64      clients = clients.concat(
    65        server.createList('node', 3, {
    66          datacenter: 'dc1',
    67          status: 'ready',
    68        })
    69      );
    70    });
    71  
    72    test('it passes an accessibility audit', async function (assert) {
    73      await Clients.visit({ id: job.id });
    74      await a11yAudit(assert);
    75    });
    76  
    77    test('lists all clients for the job', async function (assert) {
    78      await Clients.visit({ id: job.id });
    79      assert.equal(Clients.clients.length, 15, 'Clients are shown in a table');
    80  
    81      const clientIDs = clients.sortBy('id').map((c) => c.id);
    82      const clientsInTable = Clients.clients.map((c) => c.id).sort();
    83      assert.deepEqual(clientsInTable, clientIDs);
    84  
    85      assert.equal(document.title, `Job ${job.name} clients - Nomad`);
    86    });
    87  
    88    test('dates have tooltip', async function (assert) {
    89      await Clients.visit({ id: job.id });
    90  
    91      Clients.clients.forEach((clientRow, index) => {
    92        const jobStatus = Clients.clientFor(clientRow.id).status;
    93  
    94        ['createTime', 'modifyTime'].forEach((col) => {
    95          if (jobStatus === 'not scheduled') {
    96            /* eslint-disable-next-line qunit/no-conditional-assertions */
    97            assert.equal(
    98              clientRow[col].text,
    99              '-',
   100              `row ${index} doesn't have ${col} tooltip`
   101            );
   102            /* eslint-disable-next-line qunit/no-early-return */
   103            return;
   104          }
   105  
   106          const hasTooltip = clientRow[col].tooltip.isPresent;
   107          const tooltipText = clientRow[col].tooltip.text;
   108          assert.true(hasTooltip, `row ${index} has ${col} tooltip`);
   109          assert.ok(
   110            tooltipText,
   111            `row ${index} has ${col} tooltip content ${tooltipText}`
   112          );
   113        });
   114      });
   115    });
   116  
   117    test('clients table is sortable', async function (assert) {
   118      await Clients.visit({ id: job.id });
   119      await Clients.sortBy('node.name');
   120  
   121      assert.equal(
   122        currentURL(),
   123        `/jobs/${job.id}/clients?desc=true&sort=node.name`,
   124        'the URL persists the sort parameter'
   125      );
   126  
   127      const sortedClients = clients.sortBy('name').reverse();
   128      Clients.clients.forEach((client, index) => {
   129        const shortId = sortedClients[index].id.split('-')[0];
   130        assert.equal(
   131          client.shortId,
   132          shortId,
   133          `Client ${index} is ${shortId} with name ${sortedClients[index].name}`
   134        );
   135      });
   136    });
   137  
   138    test('clients table is searchable', async function (assert) {
   139      makeSearchableClients(server, job);
   140  
   141      await Clients.visit({ id: job.id });
   142      await Clients.search('ffffff');
   143  
   144      assert.equal(Clients.clients.length, 5, 'List is filtered by search term');
   145    });
   146  
   147    test('when a search yields no results, the search box remains', async function (assert) {
   148      makeSearchableClients(server, job);
   149  
   150      await Clients.visit({ id: job.id });
   151      await Clients.search('^nothing will ever match this long regex$');
   152  
   153      assert.equal(
   154        Clients.emptyState.headline,
   155        'No Matches',
   156        'List is empty and the empty state is about search'
   157      );
   158  
   159      assert.ok(Clients.hasSearchBox, 'Search box is still shown');
   160    });
   161  
   162    test('when the job for the clients is not found, an error message is shown, but the URL persists', async function (assert) {
   163      await Clients.visit({ id: 'not-a-real-job' });
   164  
   165      assert.equal(
   166        server.pretender.handledRequests
   167          .filter((request) => !request.url.includes('policy'))
   168          .findBy('status', 404).url,
   169        '/v1/job/not-a-real-job',
   170        'A request to the nonexistent job is made'
   171      );
   172      assert.equal(
   173        currentURL(),
   174        '/jobs/not-a-real-job/clients',
   175        'The URL persists'
   176      );
   177      assert.ok(Clients.error.isPresent, 'Error message is shown');
   178      assert.equal(Clients.error.title, 'Not Found', 'Error message is for 404');
   179    });
   180  
   181    test('clicking row goes to client details', async function (assert) {
   182      const client = clients[0];
   183  
   184      await Clients.visit({ id: job.id });
   185      await Clients.clientFor(client.id).click();
   186      assert.equal(currentURL(), `/clients/${client.id}`);
   187  
   188      await Clients.visit({ id: job.id });
   189      await Clients.clientFor(client.id).visit();
   190      assert.equal(currentURL(), `/clients/${client.id}`);
   191  
   192      await Clients.visit({ id: job.id });
   193      await Clients.clientFor(client.id).visitRow();
   194      assert.equal(currentURL(), `/clients/${client.id}`);
   195    });
   196  
   197    testFacet('Job Status', {
   198      facet: Clients.facets.jobStatus,
   199      paramName: 'jobStatus',
   200      expectedOptions: [
   201        'Queued',
   202        'Not Scheduled',
   203        'Starting',
   204        'Running',
   205        'Complete',
   206        'Degraded',
   207        'Failed',
   208        'Lost',
   209        'Unknown',
   210      ],
   211      async beforeEach() {
   212        await Clients.visit({ id: job.id });
   213      },
   214    });
   215  
   216    function testFacet(label, { facet, paramName, beforeEach, expectedOptions }) {
   217      test(`the ${label} facet has the correct options`, async function (assert) {
   218        await beforeEach();
   219        await facet.toggle();
   220  
   221        let expectation;
   222        if (typeof expectedOptions === 'function') {
   223          expectation = expectedOptions();
   224        } else {
   225          expectation = expectedOptions;
   226        }
   227  
   228        assert.deepEqual(
   229          facet.options.map((option) => option.label.trim()),
   230          expectation,
   231          `Options for facet ${paramName} are as expected`
   232        );
   233      });
   234  
   235      // TODO: add facet tests for actual list filtering
   236    }
   237  });