open-nomad/ui/tests/acceptance/job-dispatch-test.js

247 lines
7.7 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
/* eslint-disable ember/no-test-module-for */
/* eslint-disable qunit/require-expect */
/* eslint-disable qunit/no-conditional-assertions */
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror';
import JobDispatch from 'nomad-ui/tests/pages/jobs/dispatch';
import JobDetail from 'nomad-ui/tests/pages/jobs/detail';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import { currentURL } from '@ember/test-helpers';
const REQUIRED_INDICATOR = '*';
moduleForJobDispatch('Acceptance | job dispatch', () => {
server.createList('namespace', 2);
const namespace = server.db.namespaces[0];
return server.create('job', 'parameterized', {
status: 'running',
namespaceId: namespace.name,
});
});
moduleForJobDispatch('Acceptance | job dispatch (with namespace)', () => {
server.createList('namespace', 2);
const namespace = server.db.namespaces[1];
return server.create('job', 'parameterized', {
status: 'running',
namespaceId: namespace.name,
});
});
function moduleForJobDispatch(title, jobFactory) {
let job, namespace, managementToken, clientToken;
module(title, function (hooks) {
setupApplicationTest(hooks);
setupCodeMirror(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
// Required for placing allocations (a result of dispatching jobs)
server.create('node');
job = jobFactory();
namespace = server.db.namespaces.find(job.namespaceId);
managementToken = server.create('token');
clientToken = server.create('token');
window.localStorage.nomadTokenSecret = managementToken.secretId;
});
test('it passes an accessibility audit', async function (assert) {
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
await a11yAudit(assert);
});
test('the dispatch button is displayed with management token', async function (assert) {
await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
assert.notOk(JobDetail.dispatchButton.isDisabled);
});
test('the dispatch button is displayed when allowed', async function (assert) {
window.localStorage.nomadTokenSecret = clientToken.secretId;
const policy = server.create('policy', {
id: 'dispatch',
name: 'dispatch',
rulesJSON: {
Namespaces: [
{
Name: namespace.name,
Capabilities: ['list-jobs', 'dispatch-job'],
},
],
},
});
clientToken.policyIds = [policy.id];
clientToken.save();
await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
assert.notOk(JobDetail.dispatchButton.isDisabled);
// Reset clientToken policies.
clientToken.policyIds = [];
clientToken.save();
});
test('the dispatch button is disabled when not allowed', async function (assert) {
window.localStorage.nomadTokenSecret = clientToken.secretId;
await JobDetail.visit({ id: `${job.id}@${namespace.name}` });
assert.ok(JobDetail.dispatchButton.isDisabled);
});
test('all meta fields are displayed', async function (assert) {
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
assert.equal(
JobDispatch.metaFields.length,
job.parameterizedJob.MetaOptional.length +
job.parameterizedJob.MetaRequired.length
);
});
test('required meta fields are properly indicated', async function (assert) {
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
JobDispatch.metaFields.forEach((f) => {
const hasIndicator = f.label.includes(REQUIRED_INDICATOR);
const isRequired = job.parameterizedJob.MetaRequired.includes(
f.field.id
);
if (isRequired) {
assert.ok(hasIndicator, `${f.label} contains required indicator.`);
} else {
assert.notOk(
hasIndicator,
`${f.label} doesn't contain required indicator.`
);
}
});
});
test('job without meta fields', async function (assert) {
const jobWithoutMeta = server.create('job', 'parameterized', {
status: 'running',
namespaceId: namespace.name,
parameterizedJob: {
MetaRequired: null,
MetaOptional: null,
},
});
await JobDispatch.visit({ id: `${jobWithoutMeta.id}@${namespace.name}` });
assert.ok(JobDispatch.dispatchButton.isPresent);
});
test('payload text area is hidden when forbidden', async function (assert) {
job.parameterizedJob.Payload = 'forbidden';
job.save();
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
assert.ok(JobDispatch.payload.emptyMessage.isPresent);
assert.notOk(JobDispatch.payload.editor.isPresent);
});
test('payload is indicated as required', async function (assert) {
const jobPayloadRequired = server.create('job', 'parameterized', {
status: 'running',
namespaceId: namespace.name,
parameterizedJob: {
Payload: 'required',
},
});
const jobPayloadOptional = server.create('job', 'parameterized', {
status: 'running',
namespaceId: namespace.name,
parameterizedJob: {
Payload: 'optional',
},
});
await JobDispatch.visit({
id: `${jobPayloadRequired.id}@${namespace.name}`,
});
let payloadTitle = JobDispatch.payload.title;
assert.ok(
payloadTitle.includes(REQUIRED_INDICATOR),
`${payloadTitle} contains required indicator.`
);
await JobDispatch.visit({
id: `${jobPayloadOptional.id}@${namespace.name}`,
});
payloadTitle = JobDispatch.payload.title;
assert.notOk(
payloadTitle.includes(REQUIRED_INDICATOR),
`${payloadTitle} doesn't contain required indicator.`
);
});
test('dispatch a job', async function (assert) {
function countDispatchChildren() {
return server.db.jobs.where((j) =>
j.id.startsWith(`${job.id}/`)
).length;
}
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
// Fill form.
JobDispatch.metaFields.map((f) => f.field.input('meta value'));
JobDispatch.payload.editor.fillIn('payload');
const childrenCountBefore = countDispatchChildren();
await JobDispatch.dispatchButton.click();
const childrenCountAfter = countDispatchChildren();
assert.equal(childrenCountAfter, childrenCountBefore + 1);
assert.ok(
currentURL().startsWith(`/jobs/${encodeURIComponent(`${job.id}/`)}`)
);
assert.ok(JobDetail.jobName);
});
test('fail when required meta field is empty', async function (assert) {
// Make sure we have a required meta param.
job.parameterizedJob.MetaRequired = ['required'];
job.parameterizedJob.Payload = 'forbidden';
job.save();
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
// Fill only optional meta params.
JobDispatch.optionalMetaFields.map((f) => f.field.input('meta value'));
await JobDispatch.dispatchButton.click();
assert.ok(JobDispatch.hasError, 'Dispatch error message is shown');
});
test('fail when required payload is empty', async function (assert) {
job.parameterizedJob.MetaRequired = [];
job.parameterizedJob.Payload = 'required';
job.save();
await JobDispatch.visit({ id: `${job.id}@${namespace.name}` });
await JobDispatch.dispatchButton.click();
assert.ok(JobDispatch.hasError, 'Dispatch error message is shown');
});
});
}