Files
nomad/ui/tests/acceptance/plugins-list-test.js
Phil Renaud 1976202cd6 Feature: Dynamic Host Volumes in the UI (#25224)
* DHV UI init

* /csi routes to /storage routes and a routeRedirector util (#25163)

* /csi routes to /storage routes and a routeRedirector util

* Tests and routes move csi/ to storage/

* Changelog added

* [ui] Storage UI overhaul + Dynamic Host Volumes UI (#25226)

* Storage index page and DHV model properties

* Naive version of a storage overview page

* Experimental fetch of alloc data dirs

* Fetch ephemeral disks and static host volumes as an ember concurrency task and nice table stylings

* Playing nice with section header labels to make eslint happy even though wcag was already cool with it

* inlined the storage type explainers and reordered things, plus tooltips and keynav

* Bones of a dynamic host volume individual page

* Woooo dynamic host volume model, adapter, and serializer with embedded alloc relationships

* Couple test fixes

* async:false relationship for dhv.hasMany('alloc') to prevent a ton of xhr requests

* DHV request type at index routemodel and better serialization

* Pagination and searching and query params oh my

* Test retrofits for csi volumes

* Really fantastic flake gets fixed

* DHV detail page acceptance test and a bunch of mirage hooks

* Seed so that the actions test has a guaranteed task

* removed ephemeral disk and static host volume manual scanning

* CapacityBytes and capabilities table added to DHV detail page

* Debugging actions flyout test

* was becoming clear that faker.seed editing was causing havoc elsewhere so might as well not boil the ocean and just tell this test to do what I want it to

* Post-create job gets taskCount instead of count

* CSI volumes now get /csi route prefix at detail level

* lazyclick method for unused keynav removed

* keyboard nav and table-watcher for DHV added

* Addressed PR comments, changed up capabilities table and id references, etc.

* Capabilities table for DHV and ID in details header

* Testfixes for pluginID and capabilities table on DHV page
2025-03-10 14:46:02 -04:00

163 lines
5.3 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
/* eslint-disable qunit/require-expect */
import { currentURL } 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 PluginsList from 'nomad-ui/tests/pages/storage/plugins/list';
module('Acceptance | plugins list', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
server.create('node-pool');
server.create('node');
window.localStorage.clear();
});
test('it passes an accessibility audit', async function (assert) {
await PluginsList.visit();
await a11yAudit(assert);
});
test('visiting /storage/plugins', async function (assert) {
await PluginsList.visit();
assert.equal(currentURL(), '/storage/plugins');
assert.equal(document.title, 'CSI Plugins - Nomad');
});
test('/storage/plugins should list the first page of plugins sorted by id', async function (assert) {
const pluginCount = PluginsList.pageSize + 1;
server.createList('csi-plugin', pluginCount, { shallow: true });
await PluginsList.visit();
const sortedPlugins = server.db.csiPlugins.sortBy('id');
assert.equal(PluginsList.plugins.length, PluginsList.pageSize);
PluginsList.plugins.forEach((plugin, index) => {
assert.equal(plugin.id, sortedPlugins[index].id, 'Plugins are ordered');
});
});
test('each plugin row should contain information about the plugin', async function (assert) {
const plugin = server.create('csi-plugin', {
shallow: true,
controllerRequired: true,
});
await PluginsList.visit();
const pluginRow = PluginsList.plugins.objectAt(0);
const controllerHealthStr =
plugin.controllersHealthy > 0 ? 'Healthy' : 'Unhealthy';
const nodeHealthStr = plugin.nodesHealthy > 0 ? 'Healthy' : 'Unhealthy';
assert.equal(pluginRow.id, plugin.id);
assert.equal(
pluginRow.controllerHealth,
`${controllerHealthStr} (${plugin.controllersHealthy}/${plugin.controllersExpected})`
);
assert.equal(
pluginRow.nodeHealth,
`${nodeHealthStr} (${plugin.nodesHealthy}/${plugin.nodesExpected})`
);
assert.equal(pluginRow.provider, plugin.provider);
});
test('node only plugins explain that there is no controller health for this plugin type', async function (assert) {
const plugin = server.create('csi-plugin', {
shallow: true,
controllerRequired: false,
});
await PluginsList.visit();
const pluginRow = PluginsList.plugins.objectAt(0);
const nodeHealthStr = plugin.nodesHealthy > 0 ? 'Healthy' : 'Unhealthy';
assert.equal(pluginRow.id, plugin.id);
assert.equal(pluginRow.controllerHealth, 'Node Only');
assert.equal(
pluginRow.nodeHealth,
`${nodeHealthStr} (${plugin.nodesHealthy}/${plugin.nodesExpected})`
);
assert.equal(pluginRow.provider, plugin.provider);
});
test('each plugin row should link to the corresponding plugin', async function (assert) {
const plugin = server.create('csi-plugin', { shallow: true });
await PluginsList.visit();
await PluginsList.plugins.objectAt(0).clickName();
assert.equal(currentURL(), `/storage/plugins/${plugin.id}`);
await PluginsList.visit();
assert.equal(currentURL(), '/storage/plugins');
await PluginsList.plugins.objectAt(0).clickRow();
assert.equal(currentURL(), `/storage/plugins/${plugin.id}`);
});
test('when there are no plugins, there is an empty message', async function (assert) {
await PluginsList.visit();
assert.ok(PluginsList.isEmpty);
assert.equal(PluginsList.emptyState.headline, 'No Plugins');
});
test('when there are plugins, but no matches for a search, there is an empty message', async function (assert) {
server.create('csi-plugin', { id: 'cat 1', shallow: true });
server.create('csi-plugin', { id: 'cat 2', shallow: true });
await PluginsList.visit();
await PluginsList.search('dog');
assert.ok(PluginsList.isEmpty);
assert.equal(PluginsList.emptyState.headline, 'No Matches');
});
test('search resets the current page', async function (assert) {
server.createList('csi-plugin', PluginsList.pageSize + 1, {
shallow: true,
});
await PluginsList.visit();
await PluginsList.nextPage();
assert.equal(currentURL(), '/storage/plugins?page=2');
await PluginsList.search('foobar');
assert.equal(currentURL(), '/storage/plugins?search=foobar');
});
test('when accessing plugins is forbidden, a message is shown with a link to the tokens page', async function (assert) {
server.pretender.get('/v1/plugins', () => [403, {}, null]);
await PluginsList.visit();
assert.equal(PluginsList.error.title, 'Not Authorized');
await PluginsList.error.seekHelp();
assert.equal(currentURL(), '/settings/tokens');
});
pageSizeSelect({
resourceName: 'plugin',
pageObject: PluginsList,
pageObjectList: PluginsList.plugins,
async setup() {
server.createList('csi-plugin', PluginsList.pageSize, { shallow: true });
await PluginsList.visit();
},
});
});