open-nomad/ui/tests/acceptance/volumes-list-test.js
Jai Bhagat 3350f3fb11 ui: apply new qunit linting rules to tests
Async tests should use  in integrations tests.
Acceptance tests are using Mirage and can't use
since we can't know the number of assertions.
2022-01-20 10:01:35 -05:00

299 lines
9.8 KiB
JavaScript

/* eslint-disable qunit/require-expect */
import { currentURL, visit } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import pageSizeSelect from './behaviors/page-size-select';
import VolumesList from 'nomad-ui/tests/pages/storage/volumes/list';
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 | volumes list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
server.create('node');
server.create('csi-plugin', { createVolumes: false });
window.localStorage.clear();
});
test('it passes an accessibility audit', async function (assert) {
await VolumesList.visit();
await a11yAudit(assert);
});
test('visiting /csi redirects to /csi/volumes', async function (assert) {
await visit('/csi');
assert.equal(currentURL(), '/csi/volumes');
});
test('visiting /csi/volumes', async function (assert) {
await VolumesList.visit();
assert.equal(currentURL(), '/csi/volumes');
assert.equal(document.title, 'CSI Volumes - Nomad');
});
test('/csi/volumes should list the first page of volumes sorted by name', async function (assert) {
const volumeCount = VolumesList.pageSize + 1;
server.createList('csi-volume', volumeCount);
await VolumesList.visit();
const sortedVolumes = server.db.csiVolumes.sortBy('id');
assert.equal(VolumesList.volumes.length, VolumesList.pageSize);
VolumesList.volumes.forEach((volume, index) => {
assert.equal(volume.name, sortedVolumes[index].id, 'Volumes are ordered');
});
});
test('each volume row should contain information about the volume', async function (assert) {
const volume = server.create('csi-volume');
const readAllocs = server.createList('allocation', 2, { shallow: true });
const writeAllocs = server.createList('allocation', 3, { shallow: true });
readAllocs.forEach((alloc) => assignReadAlloc(volume, alloc));
writeAllocs.forEach((alloc) => assignWriteAlloc(volume, alloc));
await VolumesList.visit();
const volumeRow = VolumesList.volumes.objectAt(0);
let controllerHealthStr = 'Node Only';
if (volume.controllerRequired || volume.controllersExpected > 0) {
const healthy = volume.controllersHealthy;
const expected = volume.controllersExpected;
const isHealthy = healthy > 0;
controllerHealthStr = `${
isHealthy ? 'Healthy' : 'Unhealthy'
} (${healthy}/${expected})`;
}
const nodeHealthStr = volume.nodesHealthy > 0 ? 'Healthy' : 'Unhealthy';
assert.equal(volumeRow.name, volume.id);
assert.notOk(volumeRow.hasNamespace);
assert.equal(
volumeRow.schedulable,
volume.schedulable ? 'Schedulable' : 'Unschedulable'
);
assert.equal(volumeRow.controllerHealth, controllerHealthStr);
assert.equal(
volumeRow.nodeHealth,
`${nodeHealthStr} (${volume.nodesHealthy}/${volume.nodesExpected})`
);
assert.equal(volumeRow.provider, volume.provider);
assert.equal(volumeRow.allocations, readAllocs.length + writeAllocs.length);
});
test('each volume row should link to the corresponding volume', async function (assert) {
const [, secondNamespace] = server.createList('namespace', 2);
const volume = server.create('csi-volume', {
namespaceId: secondNamespace.id,
});
await VolumesList.visit({ namespace: '*' });
await VolumesList.volumes.objectAt(0).clickName();
assert.equal(
currentURL(),
`/csi/volumes/${volume.id}?namespace=${secondNamespace.id}`
);
await VolumesList.visit({ namespace: '*' });
assert.equal(currentURL(), '/csi/volumes?namespace=*');
await VolumesList.volumes.objectAt(0).clickRow();
assert.equal(
currentURL(),
`/csi/volumes/${volume.id}?namespace=${secondNamespace.id}`
);
});
test('when there are no volumes, there is an empty message', async function (assert) {
await VolumesList.visit();
assert.ok(VolumesList.isEmpty);
assert.equal(VolumesList.emptyState.headline, 'No Volumes');
});
test('when there are volumes, but no matches for a search, there is an empty message', async function (assert) {
server.create('csi-volume', { id: 'cat 1' });
server.create('csi-volume', { id: 'cat 2' });
await VolumesList.visit();
await VolumesList.search('dog');
assert.ok(VolumesList.isEmpty);
assert.equal(VolumesList.emptyState.headline, 'No Matches');
});
test('searching resets the current page', async function (assert) {
server.createList('csi-volume', VolumesList.pageSize + 1);
await VolumesList.visit();
await VolumesList.nextPage();
assert.equal(currentURL(), '/csi/volumes?page=2');
await VolumesList.search('foobar');
assert.equal(currentURL(), '/csi/volumes?search=foobar');
});
test('when the cluster has namespaces, each volume row includes the volume namespace', async function (assert) {
server.createList('namespace', 2);
const volume = server.create('csi-volume');
await VolumesList.visit({ namespace: '*' });
const volumeRow = VolumesList.volumes.objectAt(0);
assert.equal(volumeRow.namespace, volume.namespaceId);
});
test('when the namespace query param is set, only matching volumes are shown and the namespace value is forwarded to app state', async function (assert) {
server.createList('namespace', 2);
const volume1 = server.create('csi-volume', {
namespaceId: server.db.namespaces[0].id,
});
const volume2 = server.create('csi-volume', {
namespaceId: server.db.namespaces[1].id,
});
await VolumesList.visit();
assert.equal(VolumesList.volumes.length, 2);
const firstNamespace = server.db.namespaces[0];
await VolumesList.visit({ namespace: firstNamespace.id });
assert.equal(VolumesList.volumes.length, 1);
assert.equal(VolumesList.volumes.objectAt(0).name, volume1.id);
const secondNamespace = server.db.namespaces[1];
await VolumesList.visit({ namespace: secondNamespace.id });
assert.equal(VolumesList.volumes.length, 1);
assert.equal(VolumesList.volumes.objectAt(0).name, volume2.id);
});
test('the active namespace is carried over to the jobs pages', async function (assert) {
server.createList('namespace', 2);
const namespace = server.db.namespaces[1];
await VolumesList.visit();
await VolumesList.facets.namespace.toggle();
await VolumesList.facets.namespace.options.objectAt(2).select();
await Layout.gutter.visitJobs();
assert.equal(currentURL(), `/jobs?namespace=${namespace.id}`);
});
test('when accessing volumes is forbidden, a message is shown with a link to the tokens page', async function (assert) {
server.pretender.get('/v1/volumes', () => [403, {}, null]);
await VolumesList.visit();
assert.equal(VolumesList.error.title, 'Not Authorized');
await VolumesList.error.seekHelp();
assert.equal(currentURL(), '/settings/tokens');
});
pageSizeSelect({
resourceName: 'volume',
pageObject: VolumesList,
pageObjectList: VolumesList.volumes,
async setup() {
server.createList('csi-volume', VolumesList.pageSize);
await VolumesList.visit();
},
});
testSingleSelectFacet('Namespace', {
facet: VolumesList.facets.namespace,
paramName: 'namespace',
expectedOptions: ['All (*)', 'default', 'namespace-2'],
optionToSelect: 'namespace-2',
async beforeEach() {
server.create('namespace', { id: 'default' });
server.create('namespace', { id: 'namespace-2' });
server.createList('csi-volume', 2, { namespaceId: 'default' });
server.createList('csi-volume', 2, { namespaceId: 'namespace-2' });
await VolumesList.visit();
},
filter(volume, selection) {
return volume.namespaceId === selection;
},
});
function testSingleSelectFacet(
label,
{ facet, paramName, beforeEach, filter, expectedOptions, optionToSelect }
) {
test(`the ${label} facet has the correct options`, async function (assert) {
await beforeEach();
await facet.toggle();
let expectation;
if (typeof expectedOptions === 'function') {
expectation = expectedOptions(server.db.jobs);
} else {
expectation = expectedOptions;
}
assert.deepEqual(
facet.options.map((option) => option.label.trim()),
expectation,
'Options for facet are as expected'
);
});
test(`the ${label} facet filters the volumes list by ${label}`, async function (assert) {
await beforeEach();
await facet.toggle();
const option = facet.options.findOneBy('label', optionToSelect);
const selection = option.key;
await option.select();
const expectedVolumes = server.db.csiVolumes
.filter((volume) => filter(volume, selection))
.sortBy('id');
VolumesList.volumes.forEach((volume, index) => {
assert.equal(
volume.name,
expectedVolumes[index].name,
`Volume at ${index} is ${expectedVolumes[index].name}`
);
});
});
test(`selecting an option in the ${label} facet updates the ${paramName} query param`, async function (assert) {
await beforeEach();
await facet.toggle();
const option = facet.options.objectAt(1);
const selection = option.key;
await option.select();
assert.ok(
currentURL().includes(`${paramName}=${selection}`),
'URL has the correct query param key and value'
);
});
}
});