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

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