github.com/hernad/nomad@v1.6.112/ui/tests/integration/components/topo-viz-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import { module, test } from 'qunit'; 7 import { render, triggerEvent } from '@ember/test-helpers'; 8 import { setupRenderingTest } from 'ember-qunit'; 9 import hbs from 'htmlbars-inline-precompile'; 10 import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; 11 import { create } from 'ember-cli-page-object'; 12 import { setupMirage } from 'ember-cli-mirage/test-support'; 13 import sinon from 'sinon'; 14 import faker from 'nomad-ui/mirage/faker'; 15 import topoVizPageObject from 'nomad-ui/tests/pages/components/topo-viz'; 16 import { HOSTS } from '../../../mirage/common'; 17 18 const TopoViz = create(topoVizPageObject()); 19 20 const alloc = (nodeId, jobId, taskGroupName, memory, cpu, props = {}) => ({ 21 id: faker.random.uuid(), 22 taskGroupName, 23 isScheduled: true, 24 allocatedResources: { 25 cpu, 26 memory, 27 }, 28 belongsTo: (type) => ({ 29 id: () => (type === 'job' ? jobId : nodeId), 30 }), 31 ...props, 32 }); 33 34 const node = (datacenter, id, memory, cpu) => ({ 35 datacenter, 36 id, 37 name: `nomad@${HOSTS[Math.floor(Math.random() * 10) % HOSTS.length]}`, 38 resources: { memory, cpu }, 39 }); 40 41 module('Integration | Component | TopoViz', function (hooks) { 42 setupRenderingTest(hooks); 43 setupMirage(hooks); 44 45 const commonTemplate = hbs` 46 <TopoViz 47 @nodes={{this.nodes}} 48 @allocations={{this.allocations}} 49 @onAllocationSelect={{this.onAllocationSelect}} 50 @onNodeSelect={{this.onNodeSelect}} 51 @onDataError={{this.onDataError}} /> 52 `; 53 54 test('presents as a FlexMasonry of datacenters', async function (assert) { 55 assert.expect(6); 56 57 this.setProperties({ 58 nodes: [node('dc1', 'node0', 1000, 500), node('dc2', 'node1', 1000, 500)], 59 60 allocations: [ 61 alloc('node0', 'job1', 'group', 100, 100), 62 alloc('node0', 'job1', 'group', 100, 100), 63 alloc('node1', 'job1', 'group', 100, 100), 64 ], 65 }); 66 67 await render(commonTemplate); 68 69 assert.equal(TopoViz.datacenters.length, 2); 70 assert.equal(TopoViz.datacenters[0].nodes.length, 1); 71 assert.equal(TopoViz.datacenters[1].nodes.length, 1); 72 assert.equal(TopoViz.datacenters[0].nodes[0].memoryRects.length, 2); 73 assert.equal(TopoViz.datacenters[1].nodes[0].memoryRects.length, 1); 74 75 await componentA11yAudit(this.element, assert); 76 }); 77 78 test('clicking on a node in a deeply nested TopoViz::Node will toggle node selection and call @onNodeSelect', async function (assert) { 79 this.setProperties({ 80 // TopoViz must be dense for node selection to be a feature 81 nodes: Array(55) 82 .fill(null) 83 .map((_, index) => node('dc1', `node${index}`, 1000, 500)), 84 allocations: [], 85 onNodeSelect: sinon.spy(), 86 }); 87 88 await render(commonTemplate); 89 90 await TopoViz.datacenters[0].nodes[0].selectNode(); 91 assert.ok(this.onNodeSelect.calledOnce); 92 assert.equal(this.onNodeSelect.getCall(0).args[0].node, this.nodes[0]); 93 94 await TopoViz.datacenters[0].nodes[0].selectNode(); 95 assert.ok(this.onNodeSelect.calledTwice); 96 assert.equal(this.onNodeSelect.getCall(1).args[0], null); 97 }); 98 99 test('clicking on an allocation in a deeply nested TopoViz::Node will update the topology object with selections and call @onAllocationSelect and @onNodeSelect', async function (assert) { 100 this.setProperties({ 101 nodes: [node('dc1', 'node0', 1000, 500)], 102 allocations: [alloc('node0', 'job1', 'group', 100, 100)], 103 onNodeSelect: sinon.spy(), 104 onAllocationSelect: sinon.spy(), 105 }); 106 107 await render(commonTemplate); 108 109 await TopoViz.datacenters[0].nodes[0].memoryRects[0].select(); 110 assert.ok(this.onAllocationSelect.calledOnce); 111 assert.equal( 112 this.onAllocationSelect.getCall(0).args[0], 113 this.allocations[0] 114 ); 115 assert.ok(this.onNodeSelect.calledOnce); 116 117 await TopoViz.datacenters[0].nodes[0].memoryRects[0].select(); 118 assert.ok(this.onAllocationSelect.calledTwice); 119 assert.equal(this.onAllocationSelect.getCall(1).args[0], null); 120 assert.ok(this.onNodeSelect.calledTwice); 121 assert.ok(this.onNodeSelect.alwaysCalledWith(null)); 122 }); 123 124 test('clicking on an allocation in a deeply nested TopoViz::Node will associate sibling allocations with curves', async function (assert) { 125 this.setProperties({ 126 nodes: [ 127 node('dc1', 'node0', 1000, 500), 128 node('dc1', 'node1', 1000, 500), 129 node('dc1', 'node2', 1000, 500), 130 node('dc2', 'node3', 1000, 500), 131 node('dc2', 'node4', 1000, 500), 132 node('dc2', 'node5', 1000, 500), 133 ], 134 allocations: [ 135 alloc('node0', 'job1', 'group', 100, 100), 136 alloc('node0', 'job1', 'group', 100, 100), 137 alloc('node1', 'job1', 'group', 100, 100), 138 alloc('node2', 'job1', 'group', 100, 100), 139 alloc('node0', 'job1', 'groupTwo', 100, 100), 140 alloc('node1', 'job2', 'group', 100, 100), 141 alloc('node2', 'job2', 'groupTwo', 100, 100), 142 ], 143 onNodeSelect: sinon.spy(), 144 onAllocationSelect: sinon.spy(), 145 }); 146 147 const selectedAllocations = this.allocations.filter( 148 (alloc) => 149 alloc.belongsTo('job').id() === 'job1' && 150 alloc.taskGroupName === 'group' 151 ); 152 153 await render(commonTemplate); 154 155 assert.notOk(TopoViz.allocationAssociationsArePresent); 156 157 await TopoViz.datacenters[0].nodes[0].memoryRects[0].select(); 158 159 assert.ok(TopoViz.allocationAssociationsArePresent); 160 assert.equal( 161 TopoViz.allocationAssociations.length, 162 selectedAllocations.length * 2 163 ); 164 165 // Lines get redrawn when the window resizes; make sure the lines persist. 166 await triggerEvent(window, 'resize'); 167 assert.equal( 168 TopoViz.allocationAssociations.length, 169 selectedAllocations.length * 2 170 ); 171 172 await TopoViz.datacenters[0].nodes[0].memoryRects[0].select(); 173 assert.notOk(TopoViz.allocationAssociationsArePresent); 174 }); 175 176 test('when the count of sibling allocations is high enough relative to the node count, curves are not rendered', async function (assert) { 177 this.setProperties({ 178 nodes: [node('dc1', 'node0', 1000, 500), node('dc1', 'node1', 1000, 500)], 179 allocations: [ 180 // There need to be at least 10 sibling allocations to trigger this behavior 181 alloc('node0', 'job1', 'group', 100, 100), 182 alloc('node0', 'job1', 'group', 100, 100), 183 alloc('node0', 'job1', 'group', 100, 100), 184 alloc('node0', 'job1', 'group', 100, 100), 185 alloc('node0', 'job1', 'group', 100, 100), 186 alloc('node0', 'job1', 'group', 100, 100), 187 alloc('node1', 'job1', 'group', 100, 100), 188 alloc('node1', 'job1', 'group', 100, 100), 189 alloc('node1', 'job1', 'group', 100, 100), 190 alloc('node1', 'job1', 'group', 100, 100), 191 alloc('node1', 'job1', 'group', 100, 100), 192 alloc('node1', 'job1', 'group', 100, 100), 193 alloc('node0', 'job1', 'groupTwo', 100, 100), 194 ], 195 onNodeSelect: sinon.spy(), 196 onAllocationSelect: sinon.spy(), 197 }); 198 199 await render(commonTemplate); 200 assert.notOk(TopoViz.allocationAssociationsArePresent); 201 202 await TopoViz.datacenters[0].nodes[0].memoryRects[0].select(); 203 assert.equal(TopoViz.allocationAssociations.length, 0); 204 205 // Lines get redrawn when the window resizes; make sure that doesn't make the lines show up again 206 await triggerEvent(window, 'resize'); 207 assert.equal(TopoViz.allocationAssociations.length, 0); 208 }); 209 210 test('when one or more nodes are missing the resources property, those nodes are filtered out of the topology view and onDataError is called', async function (assert) { 211 const badNode = node('dc1', 'node0', 1000, 500); 212 delete badNode.resources; 213 214 this.setProperties({ 215 nodes: [badNode, node('dc1', 'node1', 1000, 500)], 216 allocations: [ 217 alloc('node0', 'job1', 'group', 100, 100), 218 alloc('node0', 'job1', 'group', 100, 100), 219 alloc('node1', 'job1', 'group', 100, 100), 220 alloc('node1', 'job1', 'group', 100, 100), 221 alloc('node0', 'job1', 'groupTwo', 100, 100), 222 ], 223 onNodeSelect: sinon.spy(), 224 onAllocationSelect: sinon.spy(), 225 onDataError: sinon.spy(), 226 }); 227 228 await render(commonTemplate); 229 230 assert.ok(this.onDataError.calledOnce); 231 assert.deepEqual(this.onDataError.getCall(0).args[0], [ 232 { 233 type: 'filtered-nodes', 234 context: [this.nodes[0]], 235 }, 236 ]); 237 238 assert.equal(TopoViz.datacenters[0].nodes.length, 1); 239 }); 240 });