2020-10-15 09:18:55 +00:00
import { module , test } from 'qunit' ;
2021-01-27 18:13:05 +00:00
import { triggerEvent } from '@ember/test-helpers' ;
2020-10-15 09:18:55 +00:00
import { setupRenderingTest } from 'ember-qunit' ;
import hbs from 'htmlbars-inline-precompile' ;
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit' ;
import { create } from 'ember-cli-page-object' ;
2020-10-30 21:23:59 +00:00
import { setupMirage } from 'ember-cli-mirage/test-support' ;
2020-10-15 09:18:55 +00:00
import sinon from 'sinon' ;
import faker from 'nomad-ui/mirage/faker' ;
import topoVizPageObject from 'nomad-ui/tests/pages/components/topo-viz' ;
const TopoViz = create ( topoVizPageObject ( ) ) ;
const alloc = ( nodeId , jobId , taskGroupName , memory , cpu , props = { } ) => ( {
id : faker . random . uuid ( ) ,
taskGroupName ,
isScheduled : true ,
allocatedResources : {
cpu ,
memory ,
} ,
belongsTo : type => ( {
id : ( ) => ( type === 'job' ? jobId : nodeId ) ,
} ) ,
... props ,
} ) ;
const node = ( datacenter , id , memory , cpu ) => ( {
datacenter ,
id ,
resources : { memory , cpu } ,
} ) ;
module ( 'Integration | Component | TopoViz' , function ( hooks ) {
setupRenderingTest ( hooks ) ;
2020-10-30 21:23:59 +00:00
setupMirage ( hooks ) ;
2020-10-15 09:18:55 +00:00
const commonTemplate = hbs `
< TopoViz
@ nodes = { { this . nodes } }
@ allocations = { { this . allocations } }
@ onAllocationSelect = { { this . onAllocationSelect } }
2021-01-06 01:10:11 +00:00
@ onNodeSelect = { { this . onNodeSelect } }
@ onDataError = { { this . onDataError } } / >
2020-10-15 09:18:55 +00:00
` ;
test ( 'presents as a FlexMasonry of datacenters' , async function ( assert ) {
this . setProperties ( {
nodes : [ node ( 'dc1' , 'node0' , 1000 , 500 ) , node ( 'dc2' , 'node1' , 1000 , 500 ) ] ,
allocations : [
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
] ,
} ) ;
await this . render ( commonTemplate ) ;
assert . equal ( TopoViz . datacenters . length , 2 ) ;
assert . equal ( TopoViz . datacenters [ 0 ] . nodes . length , 1 ) ;
assert . equal ( TopoViz . datacenters [ 1 ] . nodes . length , 1 ) ;
assert . equal ( TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects . length , 2 ) ;
assert . equal ( TopoViz . datacenters [ 1 ] . nodes [ 0 ] . memoryRects . length , 1 ) ;
await componentA11yAudit ( this . element , assert ) ;
} ) ;
test ( 'clicking on a node in a deeply nested TopoViz::Node will toggle node selection and call @onNodeSelect' , async function ( assert ) {
this . setProperties ( {
// TopoViz must be dense for node selection to be a feature
nodes : Array ( 55 )
. fill ( null )
. map ( ( _ , index ) => node ( 'dc1' , ` node ${ index } ` , 1000 , 500 ) ) ,
allocations : [ ] ,
onNodeSelect : sinon . spy ( ) ,
} ) ;
await this . render ( commonTemplate ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . selectNode ( ) ;
assert . ok ( this . onNodeSelect . calledOnce ) ;
assert . equal ( this . onNodeSelect . getCall ( 0 ) . args [ 0 ] . node , this . nodes [ 0 ] ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . selectNode ( ) ;
assert . ok ( this . onNodeSelect . calledTwice ) ;
assert . equal ( this . onNodeSelect . getCall ( 1 ) . args [ 0 ] , null ) ;
} ) ;
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 ) {
this . setProperties ( {
nodes : [ node ( 'dc1' , 'node0' , 1000 , 500 ) ] ,
allocations : [ alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ] ,
onNodeSelect : sinon . spy ( ) ,
onAllocationSelect : sinon . spy ( ) ,
} ) ;
await this . render ( commonTemplate ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects [ 0 ] . select ( ) ;
assert . ok ( this . onAllocationSelect . calledOnce ) ;
assert . equal ( this . onAllocationSelect . getCall ( 0 ) . args [ 0 ] , this . allocations [ 0 ] ) ;
assert . ok ( this . onNodeSelect . calledOnce ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects [ 0 ] . select ( ) ;
assert . ok ( this . onAllocationSelect . calledTwice ) ;
assert . equal ( this . onAllocationSelect . getCall ( 1 ) . args [ 0 ] , null ) ;
assert . ok ( this . onNodeSelect . calledTwice ) ;
assert . ok ( this . onNodeSelect . alwaysCalledWith ( null ) ) ;
} ) ;
test ( 'clicking on an allocation in a deeply nested TopoViz::Node will associate sibling allocations with curves' , async function ( assert ) {
this . setProperties ( {
nodes : [
node ( 'dc1' , 'node0' , 1000 , 500 ) ,
node ( 'dc1' , 'node1' , 1000 , 500 ) ,
2020-10-23 21:50:09 +00:00
node ( 'dc1' , 'node2' , 1000 , 500 ) ,
node ( 'dc2' , 'node3' , 1000 , 500 ) ,
node ( 'dc2' , 'node4' , 1000 , 500 ) ,
node ( 'dc2' , 'node5' , 1000 , 500 ) ,
2020-10-15 09:18:55 +00:00
] ,
allocations : [
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node2' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'groupTwo' , 100 , 100 ) ,
alloc ( 'node1' , 'job2' , 'group' , 100 , 100 ) ,
alloc ( 'node2' , 'job2' , 'groupTwo' , 100 , 100 ) ,
] ,
onNodeSelect : sinon . spy ( ) ,
onAllocationSelect : sinon . spy ( ) ,
} ) ;
const selectedAllocations = this . allocations . filter (
alloc => alloc . belongsTo ( 'job' ) . id ( ) === 'job1' && alloc . taskGroupName === 'group'
) ;
await this . render ( commonTemplate ) ;
assert . notOk ( TopoViz . allocationAssociationsArePresent ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects [ 0 ] . select ( ) ;
assert . ok ( TopoViz . allocationAssociationsArePresent ) ;
assert . equal ( TopoViz . allocationAssociations . length , selectedAllocations . length * 2 ) ;
2021-01-27 18:13:05 +00:00
// Lines get redrawn when the window resizes; make sure the lines persist.
await triggerEvent ( window , 'resize' ) ;
assert . equal ( TopoViz . allocationAssociations . length , selectedAllocations . length * 2 ) ;
2020-10-15 09:18:55 +00:00
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects [ 0 ] . select ( ) ;
assert . notOk ( TopoViz . allocationAssociationsArePresent ) ;
} ) ;
2020-10-23 21:50:09 +00:00
test ( 'when the count of sibling allocations is high enough relative to the node count, curves are not rendered' , async function ( assert ) {
this . setProperties ( {
nodes : [ node ( 'dc1' , 'node0' , 1000 , 500 ) , node ( 'dc1' , 'node1' , 1000 , 500 ) ] ,
allocations : [
2021-01-27 18:24:03 +00:00
// There need to be at least 10 sibling allocations to trigger this behavior
2020-10-23 21:50:09 +00:00
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
2021-01-27 18:24:03 +00:00
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
2020-10-23 21:50:09 +00:00
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'groupTwo' , 100 , 100 ) ,
] ,
onNodeSelect : sinon . spy ( ) ,
onAllocationSelect : sinon . spy ( ) ,
} ) ;
await this . render ( commonTemplate ) ;
assert . notOk ( TopoViz . allocationAssociationsArePresent ) ;
await TopoViz . datacenters [ 0 ] . nodes [ 0 ] . memoryRects [ 0 ] . select ( ) ;
assert . equal ( TopoViz . allocationAssociations . length , 0 ) ;
2021-01-27 18:13:05 +00:00
// Lines get redrawn when the window resizes; make sure that doesn't make the lines show up again
await triggerEvent ( window , 'resize' ) ;
assert . equal ( TopoViz . allocationAssociations . length , 0 ) ;
2020-10-23 21:50:09 +00:00
} ) ;
2021-01-06 01:10:11 +00:00
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 ) {
const badNode = node ( 'dc1' , 'node0' , 1000 , 500 ) ;
delete badNode . resources ;
this . setProperties ( {
nodes : [ badNode , node ( 'dc1' , 'node1' , 1000 , 500 ) ] ,
allocations : [
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node1' , 'job1' , 'group' , 100 , 100 ) ,
alloc ( 'node0' , 'job1' , 'groupTwo' , 100 , 100 ) ,
] ,
onNodeSelect : sinon . spy ( ) ,
onAllocationSelect : sinon . spy ( ) ,
onDataError : sinon . spy ( ) ,
} ) ;
await this . render ( commonTemplate ) ;
assert . ok ( this . onDataError . calledOnce ) ;
assert . deepEqual ( this . onDataError . getCall ( 0 ) . args [ 0 ] , [
{
type : 'filtered-nodes' ,
context : [ this . nodes [ 0 ] ] ,
} ,
] ) ;
assert . equal ( TopoViz . datacenters [ 0 ] . nodes . length , 1 ) ;
} ) ;
2020-10-15 09:18:55 +00:00
} ) ;