7e4c746c82
Various page objects had breadcrumbs and breadcrumbFor within them, this moves those to the existing Layout page object that contains shared page objects.
214 lines
7.9 KiB
JavaScript
214 lines
7.9 KiB
JavaScript
import { module, test } from 'qunit';
|
|
import { currentURL } from '@ember/test-helpers';
|
|
import { setupApplicationTest } from 'ember-qunit';
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
|
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
|
|
import moment from 'moment';
|
|
import { formatBytes } from 'nomad-ui/helpers/format-bytes';
|
|
import VolumeDetail from 'nomad-ui/tests/pages/storage/volumes/detail';
|
|
import Layout from 'nomad-ui/tests/pages/layout';
|
|
|
|
const assignWriteAlloc = (volume, alloc) => {
|
|
volume.writeAllocs.add(alloc);
|
|
volume.allocations.add(alloc);
|
|
volume.save();
|
|
};
|
|
|
|
const assignReadAlloc = (volume, alloc) => {
|
|
volume.readAllocs.add(alloc);
|
|
volume.allocations.add(alloc);
|
|
volume.save();
|
|
};
|
|
|
|
module('Acceptance | volume detail', function(hooks) {
|
|
setupApplicationTest(hooks);
|
|
setupMirage(hooks);
|
|
|
|
let volume;
|
|
|
|
hooks.beforeEach(function() {
|
|
server.create('node');
|
|
server.create('csi-plugin', { createVolumes: false });
|
|
volume = server.create('csi-volume');
|
|
});
|
|
|
|
test('it passes an accessibility audit', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
await a11yAudit(assert);
|
|
});
|
|
|
|
test('/csi/volumes/:id should have a breadcrumb trail linking back to Volumes and Storage', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.equal(Layout.breadcrumbFor('csi.index').text, 'Storage');
|
|
assert.equal(Layout.breadcrumbFor('csi.volumes').text, 'Volumes');
|
|
assert.equal(Layout.breadcrumbFor('csi.volumes.volume').text, volume.name);
|
|
});
|
|
|
|
test('/csi/volumes/:id should show the volume name in the title', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.equal(document.title, `CSI Volume ${volume.name} - Nomad`);
|
|
assert.equal(VolumeDetail.title, volume.name);
|
|
});
|
|
|
|
test('/csi/volumes/:id should list additional details for the volume below the title', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.ok(VolumeDetail.health.includes(volume.schedulable ? 'Schedulable' : 'Unschedulable'));
|
|
assert.ok(VolumeDetail.provider.includes(volume.provider));
|
|
assert.ok(VolumeDetail.externalId.includes(volume.externalId));
|
|
assert.notOk(
|
|
VolumeDetail.hasNamespace,
|
|
'Namespace is omitted when there is only one namespace'
|
|
);
|
|
});
|
|
|
|
test('/csi/volumes/:id should list all write allocations the volume is attached to', async function(assert) {
|
|
const writeAllocations = server.createList('allocation', 2);
|
|
const readAllocations = server.createList('allocation', 3);
|
|
writeAllocations.forEach(alloc => assignWriteAlloc(volume, alloc));
|
|
readAllocations.forEach(alloc => assignReadAlloc(volume, alloc));
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.equal(VolumeDetail.writeAllocations.length, writeAllocations.length);
|
|
writeAllocations
|
|
.sortBy('modifyIndex')
|
|
.reverse()
|
|
.forEach((allocation, idx) => {
|
|
assert.equal(allocation.id, VolumeDetail.writeAllocations.objectAt(idx).id);
|
|
});
|
|
});
|
|
|
|
test('/csi/volumes/:id should list all read allocations the volume is attached to', async function(assert) {
|
|
const writeAllocations = server.createList('allocation', 2);
|
|
const readAllocations = server.createList('allocation', 3);
|
|
writeAllocations.forEach(alloc => assignWriteAlloc(volume, alloc));
|
|
readAllocations.forEach(alloc => assignReadAlloc(volume, alloc));
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.equal(VolumeDetail.readAllocations.length, readAllocations.length);
|
|
readAllocations
|
|
.sortBy('modifyIndex')
|
|
.reverse()
|
|
.forEach((allocation, idx) => {
|
|
assert.equal(allocation.id, VolumeDetail.readAllocations.objectAt(idx).id);
|
|
});
|
|
});
|
|
|
|
test('each allocation should have high-level details for the allocation', async function(assert) {
|
|
const allocation = server.create('allocation', { clientStatus: 'running' });
|
|
assignWriteAlloc(volume, allocation);
|
|
|
|
const allocStats = server.db.clientAllocationStats.find(allocation.id);
|
|
const taskGroup = server.db.taskGroups.findBy({
|
|
name: allocation.taskGroup,
|
|
jobId: allocation.jobId,
|
|
});
|
|
|
|
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
|
const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
|
|
const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
VolumeDetail.writeAllocations.objectAt(0).as(allocationRow => {
|
|
assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation short ID');
|
|
assert.equal(
|
|
allocationRow.createTime,
|
|
moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ'),
|
|
'Allocation create time'
|
|
);
|
|
assert.equal(
|
|
allocationRow.modifyTime,
|
|
moment(allocation.modifyTime / 1000000).fromNow(),
|
|
'Allocation modify time'
|
|
);
|
|
assert.equal(allocationRow.status, allocation.clientStatus, 'Client status');
|
|
assert.equal(allocationRow.job, server.db.jobs.find(allocation.jobId).name, 'Job name');
|
|
assert.ok(allocationRow.taskGroup, 'Task group name');
|
|
assert.ok(allocationRow.jobVersion, 'Job Version');
|
|
assert.equal(
|
|
allocationRow.client,
|
|
server.db.nodes.find(allocation.nodeId).id.split('-')[0],
|
|
'Node ID'
|
|
);
|
|
assert.equal(
|
|
allocationRow.cpu,
|
|
Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
|
|
'CPU %'
|
|
);
|
|
assert.equal(
|
|
allocationRow.cpuTooltip,
|
|
`${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
|
|
'Detailed CPU information is in a tooltip'
|
|
);
|
|
assert.equal(
|
|
allocationRow.mem,
|
|
allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed,
|
|
'Memory used'
|
|
);
|
|
assert.equal(
|
|
allocationRow.memTooltip,
|
|
`${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
|
|
'Detailed memory information is in a tooltip'
|
|
);
|
|
});
|
|
});
|
|
|
|
test('each allocation should link to the allocation detail page', async function(assert) {
|
|
const allocation = server.create('allocation');
|
|
assignWriteAlloc(volume, allocation);
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
await VolumeDetail.writeAllocations.objectAt(0).visit();
|
|
|
|
assert.equal(currentURL(), `/allocations/${allocation.id}`);
|
|
});
|
|
|
|
test('when there are no write allocations, the table presents an empty state', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.ok(VolumeDetail.writeTableIsEmpty);
|
|
assert.equal(VolumeDetail.writeEmptyState.headline, 'No Write Allocations');
|
|
});
|
|
|
|
test('when there are no read allocations, the table presents an empty state', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.ok(VolumeDetail.readTableIsEmpty);
|
|
assert.equal(VolumeDetail.readEmptyState.headline, 'No Read Allocations');
|
|
});
|
|
|
|
test('the constraints table shows access mode and attachment mode', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.equal(VolumeDetail.constraints.accessMode, volume.accessMode);
|
|
assert.equal(VolumeDetail.constraints.attachmentMode, volume.attachmentMode);
|
|
});
|
|
});
|
|
|
|
// Namespace test: details shows the namespace
|
|
module('Acceptance | volume detail (with namespaces)', function(hooks) {
|
|
setupApplicationTest(hooks);
|
|
setupMirage(hooks);
|
|
|
|
let volume;
|
|
|
|
hooks.beforeEach(function() {
|
|
server.createList('namespace', 2);
|
|
server.create('node');
|
|
server.create('csi-plugin', { createVolumes: false });
|
|
volume = server.create('csi-volume');
|
|
});
|
|
|
|
test('/csi/volumes/:id detail ribbon includes the namespace of the volume', async function(assert) {
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
assert.ok(VolumeDetail.hasNamespace);
|
|
assert.ok(VolumeDetail.namespace.includes(volume.namespaceId || 'default'));
|
|
});
|
|
});
|