github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/integration/components/das/recommendation-card-test.js (about) 1 import { module, test } from 'qunit'; 2 import { setupRenderingTest } from 'ember-qunit'; 3 import { render, settled } from '@ember/test-helpers'; 4 import { hbs } from 'ember-cli-htmlbars'; 5 import Service from '@ember/service'; 6 import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; 7 8 import RecommendationCardComponent from 'nomad-ui/tests/pages/components/recommendation-card'; 9 import { create } from 'ember-cli-page-object'; 10 const RecommendationCard = create(RecommendationCardComponent); 11 12 import { tracked } from '@glimmer/tracking'; 13 import { action } from '@ember/object'; 14 import { set } from '@ember/object'; 15 16 module('Integration | Component | das/recommendation-card', function(hooks) { 17 setupRenderingTest(hooks); 18 19 hooks.beforeEach(function() { 20 const mockRouter = Service.extend({ 21 init() { 22 this._super(...arguments); 23 }, 24 25 urlFor( 26 route, 27 slug, 28 { 29 queryParams: { namespace }, 30 } 31 ) { 32 return `${route}:${slug}?namespace=${namespace}`; 33 }, 34 }); 35 36 this.owner.register('service:router', mockRouter); 37 }); 38 39 test('it renders a recommendation card', async function(assert) { 40 const task1 = { 41 name: 'jortle', 42 reservedCPU: 150, 43 reservedMemory: 128, 44 }; 45 46 const task2 = { 47 name: 'tortle', 48 reservedCPU: 125, 49 reservedMemory: 256, 50 }; 51 52 this.set( 53 'summary', 54 new MockRecommendationSummary({ 55 jobNamespace: 'namespace', 56 recommendations: [ 57 { 58 resource: 'MemoryMB', 59 stats: {}, 60 task: task1, 61 value: 192, 62 currentValue: task1.reservedMemory, 63 }, 64 { 65 resource: 'CPU', 66 stats: {}, 67 task: task1, 68 value: 50, 69 currentValue: task1.reservedCPU, 70 }, 71 { 72 resource: 'CPU', 73 stats: {}, 74 task: task2, 75 value: 150, 76 currentValue: task2.reservedCPU, 77 }, 78 { 79 resource: 'MemoryMB', 80 stats: {}, 81 task: task2, 82 value: 320, 83 currentValue: task2.reservedMemory, 84 }, 85 ], 86 87 taskGroup: { 88 count: 2, 89 name: 'group-name', 90 job: { 91 name: 'job-name', 92 namespace: { 93 name: 'namespace', 94 }, 95 }, 96 reservedCPU: task1.reservedCPU + task2.reservedCPU, 97 reservedMemory: task1.reservedMemory + task2.reservedMemory, 98 }, 99 }) 100 ); 101 102 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 103 104 assert.equal(RecommendationCard.slug.jobName, 'job-name'); 105 assert.equal(RecommendationCard.slug.groupName, 'group-name'); 106 107 assert.equal(RecommendationCard.namespace, 'namespace'); 108 109 assert.equal(RecommendationCard.totalsTable.current.cpu.text, '275 MHz'); 110 assert.equal(RecommendationCard.totalsTable.current.memory.text, '384 MiB'); 111 112 RecommendationCard.totalsTable.recommended.cpu.as(RecommendedCpu => { 113 assert.equal(RecommendedCpu.text, '200 MHz'); 114 assert.ok(RecommendedCpu.isDecrease); 115 }); 116 117 RecommendationCard.totalsTable.recommended.memory.as(RecommendedMemory => { 118 assert.equal(RecommendedMemory.text, '512 MiB'); 119 assert.ok(RecommendedMemory.isIncrease); 120 }); 121 122 assert.equal(RecommendationCard.totalsTable.unitDiff.cpu, '-75 MHz'); 123 assert.equal(RecommendationCard.totalsTable.unitDiff.memory, '+128 MiB'); 124 125 assert.equal(RecommendationCard.totalsTable.percentDiff.cpu, '-27%'); 126 assert.equal(RecommendationCard.totalsTable.percentDiff.memory, '+33%'); 127 128 assert.equal(RecommendationCard.copyButton.text, 'job-name / group-name'); 129 assert.ok( 130 RecommendationCard.copyButton.clipboardText.endsWith( 131 'optimize.summary:job-name/group-name?namespace=namespace' 132 ) 133 ); 134 135 assert.equal(RecommendationCard.activeTask.totalsTable.current.cpu.text, '150 MHz'); 136 assert.equal(RecommendationCard.activeTask.totalsTable.current.memory.text, '128 MiB'); 137 138 RecommendationCard.activeTask.totalsTable.recommended.cpu.as(RecommendedCpu => { 139 assert.equal(RecommendedCpu.text, '50 MHz'); 140 assert.ok(RecommendedCpu.isDecrease); 141 }); 142 143 RecommendationCard.activeTask.totalsTable.recommended.memory.as(RecommendedMemory => { 144 assert.equal(RecommendedMemory.text, '192 MiB'); 145 assert.ok(RecommendedMemory.isIncrease); 146 }); 147 148 assert.equal(RecommendationCard.activeTask.charts.length, 2); 149 assert.equal( 150 RecommendationCard.activeTask.charts[0].resource, 151 'CPU', 152 'CPU chart should be first when present' 153 ); 154 155 assert.ok(RecommendationCard.activeTask.cpuChart.isDecrease); 156 assert.ok(RecommendationCard.activeTask.memoryChart.isIncrease); 157 158 assert.equal(RecommendationCard.togglesTable.tasks.length, 2); 159 160 await RecommendationCard.togglesTable.tasks[0].as(async FirstTask => { 161 assert.equal(FirstTask.name, 'jortle'); 162 assert.ok(FirstTask.isActive); 163 164 assert.equal(FirstTask.cpu.title, 'CPU for jortle'); 165 assert.ok(FirstTask.cpu.isActive); 166 167 assert.equal(FirstTask.memory.title, 'Memory for jortle'); 168 assert.ok(FirstTask.memory.isActive); 169 170 await FirstTask.cpu.toggle(); 171 172 assert.notOk(FirstTask.cpu.isActive); 173 assert.ok(RecommendationCard.activeTask.cpuChart.isDisabled); 174 }); 175 176 assert.notOk(RecommendationCard.togglesTable.tasks[1].isActive); 177 178 assert.equal(RecommendationCard.activeTask.name, 'jortle task'); 179 180 RecommendationCard.totalsTable.recommended.cpu.as(RecommendedCpu => { 181 assert.equal(RecommendedCpu.text, '300 MHz'); 182 assert.ok(RecommendedCpu.isIncrease); 183 }); 184 185 RecommendationCard.activeTask.totalsTable.recommended.cpu.as(RecommendedCpu => { 186 assert.equal(RecommendedCpu.text, '150 MHz'); 187 assert.ok(RecommendedCpu.isNeutral); 188 }); 189 190 await RecommendationCard.togglesTable.toggleAllMemory.toggle(); 191 192 assert.notOk(RecommendationCard.togglesTable.tasks[0].memory.isActive); 193 assert.notOk(RecommendationCard.togglesTable.tasks[1].memory.isActive); 194 195 RecommendationCard.totalsTable.recommended.memory.as(RecommendedMemory => { 196 assert.equal(RecommendedMemory.text, '384 MiB'); 197 assert.ok(RecommendedMemory.isNeutral); 198 }); 199 200 await RecommendationCard.togglesTable.tasks[1].click(); 201 202 assert.notOk(RecommendationCard.togglesTable.tasks[0].isActive); 203 assert.ok(RecommendationCard.togglesTable.tasks[1].isActive); 204 205 assert.equal(RecommendationCard.activeTask.name, 'tortle task'); 206 assert.equal(RecommendationCard.activeTask.totalsTable.current.cpu.text, '125 MHz'); 207 208 await componentA11yAudit(this.element, assert); 209 }); 210 211 test('it doesn’t have header toggles when there’s only one task', async function(assert) { 212 const task1 = { 213 name: 'jortle', 214 reservedCPU: 150, 215 reservedMemory: 128, 216 }; 217 218 this.set( 219 'summary', 220 new MockRecommendationSummary({ 221 recommendations: [ 222 { 223 resource: 'CPU', 224 stats: {}, 225 task: task1, 226 value: 50, 227 }, 228 { 229 resource: 'MemoryMB', 230 stats: {}, 231 task: task1, 232 value: 192, 233 }, 234 ], 235 236 taskGroup: { 237 count: 1, 238 reservedCPU: task1.reservedCPU, 239 reservedMemory: task1.reservedMemory, 240 }, 241 }) 242 ); 243 244 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 245 246 assert.notOk(RecommendationCard.togglesTable.toggleAllIsPresent); 247 assert.notOk(RecommendationCard.togglesTable.toggleAllCPU.isPresent); 248 assert.notOk(RecommendationCard.togglesTable.toggleAllMemory.isPresent); 249 }); 250 251 test('it disables the accept button when all recommendations are disabled', async function(assert) { 252 const task1 = { 253 name: 'jortle', 254 reservedCPU: 150, 255 reservedMemory: 128, 256 }; 257 258 this.set( 259 'summary', 260 new MockRecommendationSummary({ 261 recommendations: [ 262 { 263 resource: 'CPU', 264 stats: {}, 265 task: task1, 266 value: 50, 267 }, 268 { 269 resource: 'MemoryMB', 270 stats: {}, 271 task: task1, 272 value: 192, 273 }, 274 ], 275 276 taskGroup: { 277 count: 1, 278 reservedCPU: task1.reservedCPU, 279 reservedMemory: task1.reservedMemory, 280 }, 281 }) 282 ); 283 284 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 285 286 await RecommendationCard.togglesTable.tasks[0].cpu.toggle(); 287 await RecommendationCard.togglesTable.tasks[0].memory.toggle(); 288 289 assert.ok(RecommendationCard.acceptButton.isDisabled); 290 }); 291 292 test('it doesn’t show a toggle or chart when there’s no recommendation for that resource', async function(assert) { 293 const task1 = { 294 name: 'jortle', 295 reservedCPU: 150, 296 reservedMemory: 128, 297 }; 298 299 this.set( 300 'summary', 301 new MockRecommendationSummary({ 302 recommendations: [ 303 { 304 resource: 'CPU', 305 stats: {}, 306 task: task1, 307 value: 50, 308 }, 309 ], 310 311 taskGroup: { 312 count: 2, 313 name: 'group-name', 314 job: { 315 name: 'job-name', 316 }, 317 reservedCPU: task1.reservedCPU, 318 reservedMemory: task1.reservedMemory, 319 }, 320 }) 321 ); 322 323 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 324 325 assert.equal(RecommendationCard.totalsTable.recommended.memory.text, '128 MiB'); 326 assert.equal(RecommendationCard.totalsTable.unitDiff.memory, '0 MiB'); 327 assert.equal(RecommendationCard.totalsTable.percentDiff.memory, '+0%'); 328 329 assert.equal( 330 RecommendationCard.narrative.trim(), 331 'Applying the selected recommendations will save an aggregate 200 MHz of CPU across 2 allocations.' 332 ); 333 334 assert.ok(RecommendationCard.togglesTable.tasks[0].memory.isDisabled); 335 assert.notOk(RecommendationCard.activeTask.memoryChart.isPresent); 336 }); 337 338 test('it disables a resource’s toggle all toggle when there are no recommendations for it', async function(assert) { 339 const task1 = { 340 name: 'jortle', 341 reservedCPU: 150, 342 reservedMemory: 128, 343 }; 344 345 const task2 = { 346 name: 'tortle', 347 reservedCPU: 150, 348 reservedMemory: 128, 349 }; 350 351 this.set( 352 'summary', 353 new MockRecommendationSummary({ 354 recommendations: [ 355 { 356 resource: 'CPU', 357 stats: {}, 358 task: task1, 359 value: 50, 360 }, 361 { 362 resource: 'CPU', 363 stats: {}, 364 task: task2, 365 value: 50, 366 }, 367 ], 368 369 taskGroup: { 370 count: 2, 371 name: 'group-name', 372 job: { 373 name: 'job-name', 374 }, 375 reservedCPU: task1.reservedCPU + task2.reservedCPU, 376 reservedMemory: task1.reservedMemory + task2.reservedMemory, 377 }, 378 }) 379 ); 380 381 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 382 383 assert.ok(RecommendationCard.togglesTable.toggleAllMemory.isDisabled); 384 assert.notOk(RecommendationCard.togglesTable.toggleAllMemory.isActive); 385 assert.notOk(RecommendationCard.activeTask.memoryChart.isPresent); 386 }); 387 388 test('it renders diff calculations in a sentence', async function(assert) { 389 const task1 = { 390 name: 'jortle', 391 reservedCPU: 150, 392 reservedMemory: 128, 393 }; 394 395 const task2 = { 396 name: 'tortle', 397 reservedCPU: 125, 398 reservedMemory: 256, 399 }; 400 401 this.set( 402 'summary', 403 new MockRecommendationSummary({ 404 recommendations: [ 405 { 406 resource: 'CPU', 407 stats: {}, 408 task: task1, 409 value: 50, 410 currentValue: task1.reservedCPU, 411 }, 412 { 413 resource: 'MemoryMB', 414 stats: {}, 415 task: task1, 416 value: 192, 417 currentValue: task1.reservedMemory, 418 }, 419 { 420 resource: 'CPU', 421 stats: {}, 422 task: task2, 423 value: 150, 424 currentValue: task2.reservedCPU, 425 }, 426 { 427 resource: 'MemoryMB', 428 stats: {}, 429 task: task2, 430 value: 320, 431 currentValue: task2.reservedMemory, 432 }, 433 ], 434 435 taskGroup: { 436 count: 10, 437 name: 'group-name', 438 job: { 439 name: 'job-name', 440 namespace: { 441 name: 'namespace', 442 }, 443 }, 444 reservedCPU: task1.reservedCPU + task2.reservedCPU, 445 reservedMemory: task1.reservedMemory + task2.reservedMemory, 446 }, 447 }) 448 ); 449 450 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 451 452 const [cpuRec1, memRec1, cpuRec2, memRec2] = this.summary.recommendations; 453 454 assert.equal( 455 RecommendationCard.narrative.trim(), 456 'Applying the selected recommendations will save an aggregate 750 MHz of CPU and add an aggregate 1.25 GiB of memory across 10 allocations.' 457 ); 458 459 this.summary.toggleRecommendation(cpuRec1); 460 await settled(); 461 462 assert.equal( 463 RecommendationCard.narrative.trim(), 464 'Applying the selected recommendations will add an aggregate 250 MHz of CPU and 1.25 GiB of memory across 10 allocations.' 465 ); 466 467 this.summary.toggleRecommendation(memRec1); 468 await settled(); 469 470 assert.equal( 471 RecommendationCard.narrative.trim(), 472 'Applying the selected recommendations will add an aggregate 250 MHz of CPU and 640 MiB of memory across 10 allocations.' 473 ); 474 475 this.summary.toggleRecommendation(cpuRec2); 476 await settled(); 477 478 assert.equal( 479 RecommendationCard.narrative.trim(), 480 'Applying the selected recommendations will add an aggregate 640 MiB of memory across 10 allocations.' 481 ); 482 483 this.summary.toggleRecommendation(cpuRec1); 484 this.summary.toggleRecommendation(memRec2); 485 await settled(); 486 487 assert.equal( 488 RecommendationCard.narrative.trim(), 489 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU across 10 allocations.' 490 ); 491 492 this.summary.toggleRecommendation(cpuRec1); 493 await settled(); 494 495 assert.equal(RecommendationCard.narrative.trim(), ''); 496 497 this.summary.toggleRecommendation(cpuRec1); 498 await settled(); 499 500 assert.equal( 501 RecommendationCard.narrative.trim(), 502 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU across 10 allocations.' 503 ); 504 505 this.summary.toggleRecommendation(memRec2); 506 set(memRec2, 'value', 128); 507 await settled(); 508 509 assert.equal( 510 RecommendationCard.narrative.trim(), 511 'Applying the selected recommendations will save an aggregate 1000 MHz of CPU and 1.25 GiB of memory across 10 allocations.' 512 ); 513 }); 514 515 test('it renders diff calculations in a sentence with no aggregation for one allocatio', async function(assert) { 516 const task1 = { 517 name: 'jortle', 518 reservedCPU: 150, 519 reservedMemory: 128, 520 }; 521 522 const task2 = { 523 name: 'tortle', 524 reservedCPU: 125, 525 reservedMemory: 256, 526 }; 527 528 this.set( 529 'summary', 530 new MockRecommendationSummary({ 531 recommendations: [ 532 { 533 resource: 'CPU', 534 stats: {}, 535 task: task1, 536 value: 50, 537 currentValue: task1.reservedCPU, 538 }, 539 { 540 resource: 'MemoryMB', 541 stats: {}, 542 task: task1, 543 value: 192, 544 currentValue: task1.reservedMemory, 545 }, 546 { 547 resource: 'CPU', 548 stats: {}, 549 task: task2, 550 value: 150, 551 currentValue: task2.reservedCPU, 552 }, 553 { 554 resource: 'MemoryMB', 555 stats: {}, 556 task: task2, 557 value: 320, 558 currentValue: task2.reservedMemory, 559 }, 560 ], 561 562 taskGroup: { 563 count: 1, 564 name: 'group-name', 565 job: { 566 name: 'job-name', 567 namespace: { 568 name: 'namespace', 569 }, 570 }, 571 reservedCPU: task1.reservedCPU + task2.reservedCPU, 572 reservedMemory: task1.reservedMemory + task2.reservedMemory, 573 }, 574 }) 575 ); 576 577 await render(hbs`<Das::RecommendationCard @summary={{summary}} />`); 578 579 assert.equal( 580 RecommendationCard.narrative.trim(), 581 'Applying the selected recommendations will save 75 MHz of CPU and add 128 MiB of memory.' 582 ); 583 }); 584 }); 585 586 class MockRecommendationSummary { 587 @tracked excludedRecommendations = []; 588 589 constructor(attributes) { 590 Object.assign(this, attributes); 591 } 592 593 get slug() { 594 return `${this.taskGroup?.job?.name}/${this.taskGroup?.name}`; 595 } 596 597 @action 598 toggleRecommendation(recommendation) { 599 if (this.excludedRecommendations.includes(recommendation)) { 600 this.excludedRecommendations.removeObject(recommendation); 601 } else { 602 this.excludedRecommendations.pushObject(recommendation); 603 } 604 } 605 606 @action 607 toggleAllRecommendationsForResource(resource, enabled) { 608 if (enabled) { 609 this.excludedRecommendations = this.excludedRecommendations.rejectBy('resource', resource); 610 } else { 611 this.excludedRecommendations.pushObjects(this.recommendations.filterBy('resource', resource)); 612 } 613 } 614 }