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 });