github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/ui/tests/acceptance/clients-list-test.js (about) 1 import { currentURL } from '@ember/test-helpers'; 2 import { module, test } from 'qunit'; 3 import { setupApplicationTest } from 'ember-qunit'; 4 import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; 5 import ClientsList from 'nomad-ui/tests/pages/clients/list'; 6 7 function minimumSetup() { 8 server.createList('node', 1); 9 server.createList('agent', 1); 10 } 11 12 module('Acceptance | clients list', function(hooks) { 13 setupApplicationTest(hooks); 14 setupMirage(hooks); 15 16 test('/clients should list one page of clients', async function(assert) { 17 // Make sure to make more nodes than 1 page to assert that pagination is working 18 const nodesCount = 10; 19 const pageSize = 8; 20 21 server.createList('node', nodesCount); 22 server.createList('agent', 1); 23 24 await ClientsList.visit(); 25 26 assert.equal(ClientsList.nodes.length, pageSize); 27 assert.ok(ClientsList.hasPagination, 'Pagination found on the page'); 28 29 const sortedNodes = server.db.nodes.sortBy('modifyIndex').reverse(); 30 31 ClientsList.nodes.forEach((node, index) => { 32 assert.equal(node.id, sortedNodes[index].id.split('-')[0], 'Clients are ordered'); 33 }); 34 }); 35 36 test('each client record should show high-level info of the client', async function(assert) { 37 minimumSetup(); 38 const node = server.db.nodes[0]; 39 40 await ClientsList.visit(); 41 42 const nodeRow = ClientsList.nodes.objectAt(0); 43 const allocations = server.db.allocations.where({ nodeId: node.id }); 44 45 assert.equal(nodeRow.id, node.id.split('-')[0], 'ID'); 46 assert.equal(nodeRow.name, node.name, 'Name'); 47 assert.equal(nodeRow.status, node.status, 'Status'); 48 assert.equal(nodeRow.drain, node.drain + '', 'Draining'); 49 assert.equal(nodeRow.eligibility, node.schedulingEligibility, 'Eligibility'); 50 assert.equal(nodeRow.address, node.httpAddr); 51 assert.equal(nodeRow.datacenter, node.datacenter, 'Datacenter'); 52 assert.equal(nodeRow.allocations, allocations.length, '# Allocations'); 53 }); 54 55 test('each client should link to the client detail page', async function(assert) { 56 minimumSetup(); 57 const node = server.db.nodes[0]; 58 59 await ClientsList.visit(); 60 await ClientsList.nodes.objectAt(0).clickRow(); 61 62 assert.equal(currentURL(), `/clients/${node.id}`); 63 }); 64 65 test('when there are no clients, there is an empty message', async function(assert) { 66 server.createList('agent', 1); 67 68 await ClientsList.visit(); 69 70 assert.ok(ClientsList.isEmpty); 71 assert.equal(ClientsList.empty.headline, 'No Clients'); 72 }); 73 74 test('when there are clients, but no matches for a search term, there is an empty message', async function(assert) { 75 server.createList('agent', 1); 76 server.create('node', { name: 'node' }); 77 78 await ClientsList.visit(); 79 80 await ClientsList.search('client'); 81 assert.ok(ClientsList.isEmpty); 82 assert.equal(ClientsList.empty.headline, 'No Matches'); 83 }); 84 85 test('when accessing clients is forbidden, show a message with a link to the tokens page', async function(assert) { 86 server.create('agent'); 87 server.create('node', { name: 'node' }); 88 server.pretender.get('/v1/nodes', () => [403, {}, null]); 89 90 await ClientsList.visit(); 91 92 assert.equal(ClientsList.error.title, 'Not Authorized'); 93 94 await ClientsList.error.seekHelp(); 95 96 assert.equal(currentURL(), '/settings/tokens'); 97 }); 98 99 testFacet('Class', { 100 facet: ClientsList.facets.class, 101 paramName: 'class', 102 expectedOptions(nodes) { 103 return Array.from(new Set(nodes.mapBy('nodeClass'))).sort(); 104 }, 105 async beforeEach() { 106 server.create('agent'); 107 server.createList('node', 2, { nodeClass: 'nc-one' }); 108 server.createList('node', 2, { nodeClass: 'nc-two' }); 109 server.createList('node', 2, { nodeClass: 'nc-three' }); 110 await ClientsList.visit(); 111 }, 112 filter: (node, selection) => selection.includes(node.nodeClass), 113 }); 114 115 testFacet('Status', { 116 facet: ClientsList.facets.status, 117 paramName: 'status', 118 expectedOptions: ['Initializing', 'Ready', 'Down'], 119 async beforeEach() { 120 server.create('agent'); 121 server.createList('node', 2, { status: 'initializing' }); 122 server.createList('node', 2, { status: 'ready' }); 123 server.createList('node', 2, { status: 'down' }); 124 await ClientsList.visit(); 125 }, 126 filter: (node, selection) => selection.includes(node.status), 127 }); 128 129 testFacet('Datacenters', { 130 facet: ClientsList.facets.datacenter, 131 paramName: 'dc', 132 expectedOptions(nodes) { 133 return Array.from(new Set(nodes.mapBy('datacenter'))).sort(); 134 }, 135 async beforeEach() { 136 server.create('agent'); 137 server.createList('node', 2, { datacenter: 'pdx-1' }); 138 server.createList('node', 2, { datacenter: 'nyc-1' }); 139 server.createList('node', 2, { datacenter: 'ams-1' }); 140 await ClientsList.visit(); 141 }, 142 filter: (node, selection) => selection.includes(node.datacenter), 143 }); 144 145 testFacet('Flags', { 146 facet: ClientsList.facets.flags, 147 paramName: 'flags', 148 expectedOptions: ['Ineligible', 'Draining'], 149 async beforeEach() { 150 server.create('agent'); 151 server.createList('node', 2, { schedulingEligibility: 'eligible', drain: false }); 152 server.createList('node', 2, { schedulingEligibility: 'ineligible', drain: false }); 153 server.createList('node', 2, { schedulingEligibility: 'ineligible', drain: true }); 154 await ClientsList.visit(); 155 }, 156 filter: (node, selection) => { 157 if (selection.includes('draining') && !node.drain) return false; 158 if (selection.includes('ineligible') && node.schedulingEligibility === 'eligible') 159 return false; 160 return true; 161 }, 162 }); 163 164 test('when the facet selections result in no matches, the empty state states why', async function(assert) { 165 server.create('agent'); 166 server.createList('node', 2, { status: 'ready' }); 167 168 await ClientsList.visit(); 169 170 await ClientsList.facets.status.toggle(); 171 await ClientsList.facets.status.options.objectAt(0).toggle(); 172 assert.ok(ClientsList.isEmpty, 'There is an empty message'); 173 assert.equal(ClientsList.empty.headline, 'No Matches', 'The message is appropriate'); 174 }); 175 176 test('the clients list is immediately filtered based on query params', async function(assert) { 177 server.create('agent'); 178 server.create('node', { nodeClass: 'omg-large' }); 179 server.create('node', { nodeClass: 'wtf-tiny' }); 180 181 await ClientsList.visit({ class: JSON.stringify(['wtf-tiny']) }); 182 183 assert.equal(ClientsList.nodes.length, 1, 'Only one client shown due to query param'); 184 }); 185 186 function testFacet(label, { facet, paramName, beforeEach, filter, expectedOptions }) { 187 test(`the ${label} facet has the correct options`, async function(assert) { 188 await beforeEach(); 189 await facet.toggle(); 190 191 let expectation; 192 if (typeof expectedOptions === 'function') { 193 expectation = expectedOptions(server.db.nodes); 194 } else { 195 expectation = expectedOptions; 196 } 197 198 assert.deepEqual( 199 facet.options.map(option => option.label.trim()), 200 expectation, 201 'Options for facet are as expected' 202 ); 203 }); 204 205 test(`the ${label} facet filters the nodes list by ${label}`, async function(assert) { 206 let option; 207 208 await beforeEach(); 209 210 await facet.toggle(); 211 option = facet.options.objectAt(0); 212 await option.toggle(); 213 214 const selection = [option.key]; 215 const expectedNodes = server.db.nodes 216 .filter(node => filter(node, selection)) 217 .sortBy('modifyIndex') 218 .reverse(); 219 220 ClientsList.nodes.forEach((node, index) => { 221 assert.equal( 222 node.id, 223 expectedNodes[index].id.split('-')[0], 224 `Node at ${index} is ${expectedNodes[index].id}` 225 ); 226 }); 227 }); 228 229 test(`selecting multiple options in the ${label} facet results in a broader search`, async function(assert) { 230 const selection = []; 231 232 await beforeEach(); 233 await facet.toggle(); 234 235 const option1 = facet.options.objectAt(0); 236 const option2 = facet.options.objectAt(1); 237 await option1.toggle(); 238 selection.push(option1.key); 239 await option2.toggle(); 240 selection.push(option2.key); 241 242 const expectedNodes = server.db.nodes 243 .filter(node => filter(node, selection)) 244 .sortBy('modifyIndex') 245 .reverse(); 246 247 ClientsList.nodes.forEach((node, index) => { 248 assert.equal( 249 node.id, 250 expectedNodes[index].id.split('-')[0], 251 `Node at ${index} is ${expectedNodes[index].id}` 252 ); 253 }); 254 }); 255 256 test(`selecting options in the ${label} facet updates the ${paramName} query param`, async function(assert) { 257 const selection = []; 258 259 await beforeEach(); 260 await facet.toggle(); 261 262 const option1 = facet.options.objectAt(0); 263 const option2 = facet.options.objectAt(1); 264 await option1.toggle(); 265 selection.push(option1.key); 266 await option2.toggle(); 267 selection.push(option2.key); 268 269 assert.equal( 270 currentURL(), 271 `/clients?${paramName}=${encodeURIComponent(JSON.stringify(selection))}`, 272 'URL has the correct query param key and value' 273 ); 274 }); 275 } 276 });