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  }