github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/ui/tests/acceptance/client-detail-test.js (about) 1 import Ember from 'ember'; 2 import { click, find, findAll, currentURL, visit } from 'ember-native-dom-helpers'; 3 import { test } from 'qunit'; 4 import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; 5 import { formatBytes } from 'nomad-ui/helpers/format-bytes'; 6 import moment from 'moment'; 7 8 const { $ } = Ember; 9 10 let node; 11 12 moduleForAcceptance('Acceptance | client detail', { 13 beforeEach() { 14 server.create('node', 'forceIPv4'); 15 node = server.db.nodes[0]; 16 17 // Related models 18 server.create('agent'); 19 server.create('job', { createAllocations: false }); 20 server.createList('allocation', 3, { nodeId: node.id }); 21 }, 22 }); 23 24 test('/clients/:id should have a breadrcumb trail linking back to clients', function(assert) { 25 visit(`/clients/${node.id}`); 26 27 andThen(() => { 28 assert.equal(findAll('.breadcrumb')[0].textContent, 'Clients', 'First breadcrumb says clients'); 29 assert.equal( 30 findAll('.breadcrumb')[1].textContent, 31 node.id.split('-')[0], 32 'Second breadcrumb says the node short id' 33 ); 34 }); 35 36 andThen(() => { 37 click(findAll('.breadcrumb')[0]); 38 }); 39 40 andThen(() => { 41 assert.equal(currentURL(), '/clients', 'First breadcrumb links back to clients'); 42 }); 43 }); 44 45 test('/clients/:id should list immediate details for the node in the title', function(assert) { 46 visit(`/clients/${node.id}`); 47 48 andThen(() => { 49 assert.ok(find('.title').textContent.includes(node.name), 'Title includes name'); 50 assert.ok(find('.title').textContent.includes(node.id), 'Title includes id'); 51 assert.ok( 52 findAll(`.title .node-status-light.${node.status}`).length, 53 'Title includes status light' 54 ); 55 }); 56 }); 57 58 test('/clients/:id should list additional detail for the node below the title', function(assert) { 59 visit(`/clients/${node.id}`); 60 61 andThen(() => { 62 assert.equal( 63 findAll('.inline-definitions .pair')[0].textContent, 64 `Status ${node.status}`, 65 'Status is in additional details' 66 ); 67 assert.ok( 68 $('.inline-definitions .pair:eq(0) .status-text').hasClass(`node-${node.status}`), 69 'Status is decorated with a status class' 70 ); 71 assert.equal( 72 findAll('.inline-definitions .pair')[1].textContent, 73 `Address ${node.httpAddr}`, 74 'Address is in additional detals' 75 ); 76 assert.equal( 77 findAll('.inline-definitions .pair')[2].textContent, 78 `Datacenter ${node.datacenter}`, 79 'Datacenter is in additional details' 80 ); 81 }); 82 }); 83 84 test('/clients/:id should list all allocations on the node', function(assert) { 85 const allocationsCount = server.db.allocations.where({ nodeId: node.id }).length; 86 87 visit(`/clients/${node.id}`); 88 89 andThen(() => { 90 assert.equal( 91 findAll('.allocations tbody tr').length, 92 allocationsCount, 93 `Allocations table lists all ${allocationsCount} associated allocations` 94 ); 95 }); 96 }); 97 98 test('each allocation should have high-level details for the allocation', function(assert) { 99 const allocation = server.db.allocations 100 .where({ nodeId: node.id }) 101 .sortBy('modifyIndex') 102 .reverse()[0]; 103 104 const allocStats = server.db.clientAllocationStats.find(allocation.id); 105 const taskGroup = server.db.taskGroups.findBy({ 106 name: allocation.taskGroup, 107 jobId: allocation.jobId, 108 }); 109 110 const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id)); 111 const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0); 112 const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0); 113 114 visit(`/clients/${node.id}`); 115 116 andThen(() => { 117 const allocationRow = $(findAll('.allocations tbody tr')[0]); 118 assert.equal( 119 allocationRow 120 .find('td:eq(0)') 121 .text() 122 .trim(), 123 allocation.id.split('-')[0], 124 'Allocation short ID' 125 ); 126 assert.equal( 127 allocationRow 128 .find('td:eq(1)') 129 .text() 130 .trim(), 131 moment(allocation.modifyTime / 1000000).format('MM/DD HH:mm:ss'), 132 'Allocation modify time' 133 ); 134 assert.equal( 135 allocationRow 136 .find('td:eq(2)') 137 .text() 138 .trim(), 139 allocation.name, 140 'Allocation name' 141 ); 142 assert.equal( 143 allocationRow 144 .find('td:eq(3)') 145 .text() 146 .trim(), 147 allocation.clientStatus, 148 'Client status' 149 ); 150 assert.ok( 151 allocationRow 152 .find('td:eq(4)') 153 .text() 154 .includes(server.db.jobs.find(allocation.jobId).name), 155 'Job name' 156 ); 157 assert.ok( 158 allocationRow 159 .find('td:eq(4) .is-faded') 160 .text() 161 .includes(allocation.taskGroup), 162 'Task group name' 163 ); 164 assert.ok( 165 allocationRow 166 .find('td:eq(5)') 167 .text() 168 .includes(allocation.jobVersion), 169 'Job Version' 170 ); 171 assert.equal( 172 allocationRow 173 .find('td:eq(6)') 174 .text() 175 .trim(), 176 Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed, 177 'CPU %' 178 ); 179 assert.equal( 180 allocationRow.find('td:eq(6) .tooltip').attr('aria-label'), 181 `${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`, 182 'Detailed CPU information is in a tooltip' 183 ); 184 assert.equal( 185 allocationRow 186 .find('td:eq(7)') 187 .text() 188 .trim(), 189 allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed, 190 'Memory used' 191 ); 192 assert.equal( 193 allocationRow.find('td:eq(7) .tooltip').attr('aria-label'), 194 `${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`, 195 'Detailed memory information is in a tooltip' 196 ); 197 }); 198 }); 199 200 test('each allocation should show job information even if the job is incomplete and already in the store', function( 201 assert 202 ) { 203 // First, visit clients to load the allocations for each visible node. 204 // Don't load the job belongsTo of the allocation! Leave it unfulfilled. 205 206 visit('/clients'); 207 208 // Then, visit jobs to load all jobs, which should implicitly fulfill 209 // the job belongsTo of each allocation pointed at each job. 210 211 visit('/jobs'); 212 213 // Finally, visit a node to assert that the job name and task group name are 214 // present. This will require reloading the job, since task groups aren't a 215 // part of the jobs list response. 216 217 visit(`/clients/${node.id}`); 218 219 andThen(() => { 220 const allocationRow = $(findAll('.allocations tbody tr')[0]); 221 const allocation = server.db.allocations 222 .where({ nodeId: node.id }) 223 .sortBy('modifyIndex') 224 .reverse()[0]; 225 226 assert.ok( 227 allocationRow 228 .find('td:eq(4)') 229 .text() 230 .includes(server.db.jobs.find(allocation.jobId).name), 231 'Job name' 232 ); 233 assert.ok( 234 allocationRow 235 .find('td:eq(4) .is-faded') 236 .text() 237 .includes(allocation.taskGroup), 238 'Task group name' 239 ); 240 }); 241 }); 242 243 test('each allocation should link to the allocation detail page', function(assert) { 244 const allocation = server.db.allocations 245 .where({ nodeId: node.id }) 246 .sortBy('modifyIndex') 247 .reverse()[0]; 248 249 visit(`/clients/${node.id}`); 250 251 andThen(() => { 252 click($('.allocations tbody tr:eq(0) td:eq(0) a').get(0)); 253 }); 254 255 andThen(() => { 256 assert.equal( 257 currentURL(), 258 `/allocations/${allocation.id}`, 259 'Allocation rows link to allocation detail pages' 260 ); 261 }); 262 }); 263 264 test('each allocation should link to the job the allocation belongs to', function(assert) { 265 visit(`/clients/${node.id}`); 266 267 const allocation = server.db.allocations.where({ nodeId: node.id })[0]; 268 const job = server.db.jobs.find(allocation.jobId); 269 270 andThen(() => { 271 click($('.allocations tbody tr:eq(0) td:eq(4) a').get(0)); 272 }); 273 274 andThen(() => { 275 assert.equal( 276 currentURL(), 277 `/jobs/${job.id}`, 278 'Allocation rows link to the job detail page for the allocation' 279 ); 280 }); 281 }); 282 283 test('/clients/:id should list all attributes for the node', function(assert) { 284 visit(`/clients/${node.id}`); 285 286 andThen(() => { 287 assert.ok(find('.attributes-table'), 'Attributes table is on the page'); 288 }); 289 }); 290 291 test('when the node is not found, an error message is shown, but the URL persists', function( 292 assert 293 ) { 294 visit('/clients/not-a-real-node'); 295 296 andThen(() => { 297 assert.equal( 298 server.pretender.handledRequests.findBy('status', 404).url, 299 '/v1/node/not-a-real-node', 300 'A request to the non-existent node is made' 301 ); 302 assert.equal(currentURL(), '/clients/not-a-real-node', 'The URL persists'); 303 assert.ok(find('.error-message'), 'Error message is shown'); 304 assert.equal( 305 find('.error-message .title').textContent, 306 'Not Found', 307 'Error message is for 404' 308 ); 309 }); 310 });