ui: fix remaining linting errors
This commit is contained in:
parent
3350f3fb11
commit
52cf998e2c
|
@ -21,14 +21,14 @@ export default class Client extends AbstractAbility {
|
||||||
get policiesIncludeNodeRead() {
|
get policiesIncludeNodeRead() {
|
||||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
||||||
'read',
|
'read',
|
||||||
'write'
|
'write',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed('token.selfTokenPolicies.[]')
|
@computed('token.selfTokenPolicies.[]')
|
||||||
get policiesIncludeNodeWrite() {
|
get policiesIncludeNodeWrite() {
|
||||||
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
return policiesIncludePermissions(this.get('token.selfTokenPolicies'), [
|
||||||
'write'
|
'write',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ function policiesIncludePermissions(policies = [], permissions = []) {
|
||||||
// For each policy record, extract the Node policy
|
// For each policy record, extract the Node policy
|
||||||
const nodePolicies = policies
|
const nodePolicies = policies
|
||||||
.toArray()
|
.toArray()
|
||||||
.map(policy => get(policy, 'rulesJSON.Node.Policy'))
|
.map((policy) => get(policy, 'rulesJSON.Node.Policy'))
|
||||||
.compact();
|
.compact();
|
||||||
|
|
||||||
// Check for requested permissions
|
// Check for requested permissions
|
||||||
return nodePolicies.some(policy => permissions.includes(policy));
|
return nodePolicies.some((policy) => permissions.includes(policy));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default class Trigger extends Component {
|
||||||
this.error = null;
|
this.error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@task(function*() {
|
@task(function* () {
|
||||||
this._reset();
|
this._reset();
|
||||||
try {
|
try {
|
||||||
this.result = yield this.args.do();
|
this.result = yield this.args.do();
|
||||||
|
|
|
@ -35,7 +35,12 @@ export default class AllocationsAllocationController extends Controller {
|
||||||
{
|
{
|
||||||
title: 'Task Group',
|
title: 'Task Group',
|
||||||
label: allocation.taskGroupName,
|
label: allocation.taskGroupName,
|
||||||
args: ['jobs.job.task-group', job.plainId, allocation.taskGroupName, jobQueryParams],
|
args: [
|
||||||
|
'jobs.job.task-group',
|
||||||
|
job.plainId,
|
||||||
|
allocation.taskGroupName,
|
||||||
|
jobQueryParams,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Allocation',
|
title: 'Allocation',
|
||||||
|
|
|
@ -9,7 +9,11 @@ export default class AllocationsAllocationTaskController extends Controller {
|
||||||
return {
|
return {
|
||||||
title: 'Task',
|
title: 'Task',
|
||||||
label: this.task.get('name'),
|
label: this.task.get('name'),
|
||||||
args: ['allocations.allocation.task', this.task.get('allocation'), this.task],
|
args: [
|
||||||
|
'allocations.allocation.task',
|
||||||
|
this.task.get('allocation'),
|
||||||
|
this.task,
|
||||||
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ export default class VolumeController extends Controller {
|
||||||
label: 'Volumes',
|
label: 'Volumes',
|
||||||
args: [
|
args: [
|
||||||
'csi.volumes',
|
'csi.volumes',
|
||||||
qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }),
|
qpBuilder({
|
||||||
|
volumeNamespace: volume.get('namespace.name') || 'default',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -33,7 +35,9 @@ export default class VolumeController extends Controller {
|
||||||
args: [
|
args: [
|
||||||
'csi.volumes.volume',
|
'csi.volumes.volume',
|
||||||
volume.plainId,
|
volume.plainId,
|
||||||
qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }),
|
qpBuilder({
|
||||||
|
volumeNamespace: volume.get('namespace.name') || 'default',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -35,9 +35,7 @@ export default class Allocation extends Model {
|
||||||
@attr('string') nodeName;
|
@attr('string') nodeName;
|
||||||
@computed
|
@computed
|
||||||
get shortNodeId() {
|
get shortNodeId() {
|
||||||
return this.belongsTo('node')
|
return this.belongsTo('node').id().split('-')[0];
|
||||||
.id()
|
|
||||||
.split('-')[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@attr('number') modifyIndex;
|
@attr('number') modifyIndex;
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class AllocationRoute extends Route.extend(WithWatchers) {
|
||||||
// Preload the job for the allocation since it's required for the breadcrumb trail
|
// Preload the job for the allocation since it's required for the breadcrumb trail
|
||||||
return super
|
return super
|
||||||
.model(...arguments)
|
.model(...arguments)
|
||||||
.then(allocation =>
|
.then((allocation) =>
|
||||||
allocation
|
allocation
|
||||||
.get('job')
|
.get('job')
|
||||||
.then(() => this.store.findAll('namespace')) // namespaces belong to a job and are an asynchronous relationship so we can peak them later on
|
.then(() => this.store.findAll('namespace')) // namespaces belong to a job and are an asynchronous relationship so we can peak them later on
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default class VolumeRoute extends Route.extend(WithWatchers) {
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
controller.set('watchers', {
|
controller.set('watchers', {
|
||||||
model: this.watch.perform(model)
|
model: this.watch.perform(model),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@ export default class VolumeRoute extends Route.extend(WithWatchers) {
|
||||||
const fullId = JSON.stringify([`csi/${name}`, namespace || 'default']);
|
const fullId = JSON.stringify([`csi/${name}`, namespace || 'default']);
|
||||||
return RSVP.hash({
|
return RSVP.hash({
|
||||||
volume: this.store.findRecord('volume', fullId, { reload: true }),
|
volume: this.store.findRecord('volume', fullId, { reload: true }),
|
||||||
namespaces: this.store.findAll('namespace')
|
namespaces: this.store.findAll('namespace'),
|
||||||
})
|
})
|
||||||
.then(hash => hash.volume)
|
.then((hash) => hash.volume)
|
||||||
.catch(notifyError(this));
|
.catch(notifyError(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
watchRecord,
|
watchRecord,
|
||||||
watchRelationship,
|
watchRelationship,
|
||||||
watchAll,
|
watchAll,
|
||||||
watchQuery
|
watchQuery,
|
||||||
} from 'nomad-ui/utils/properties/watch';
|
} from 'nomad-ui/utils/properties/watch';
|
||||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||||
|
|
||||||
|
@ -43,12 +43,12 @@ export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||||
list:
|
list:
|
||||||
model.job.get('hasChildren') &&
|
model.job.get('hasChildren') &&
|
||||||
this.watchAllJobs.perform({
|
this.watchAllJobs.perform({
|
||||||
namespace: model.job.namespace.get('name')
|
namespace: model.job.namespace.get('name'),
|
||||||
}),
|
}),
|
||||||
nodes:
|
nodes:
|
||||||
this.can.can('read client') &&
|
this.can.can('read client') &&
|
||||||
model.job.get('hasClientStatus') &&
|
model.job.get('hasClientStatus') &&
|
||||||
this.watchNodes.perform()
|
this.watchNodes.perform(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||||
) {
|
) {
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
sortProperty: 'submitTime',
|
sortProperty: 'submitTime',
|
||||||
sortDescending: true
|
sortDescending: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return super.setupController(...arguments);
|
return super.setupController(...arguments);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import EmberError from '@ember/error';
|
||||||
import { resolve, all } from 'rsvp';
|
import { resolve, all } from 'rsvp';
|
||||||
import {
|
import {
|
||||||
watchRecord,
|
watchRecord,
|
||||||
watchRelationship
|
watchRelationship,
|
||||||
} from 'nomad-ui/utils/properties/watch';
|
} from 'nomad-ui/utils/properties/watch';
|
||||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||||
import notifyError from 'nomad-ui/utils/notify-error';
|
import notifyError from 'nomad-ui/utils/notify-error';
|
||||||
|
@ -34,7 +34,7 @@ export default class TaskGroupRoute extends Route.extend(WithWatchers) {
|
||||||
// Refresh job allocations before-hand (so page sort works on load)
|
// Refresh job allocations before-hand (so page sort works on load)
|
||||||
return all([
|
return all([
|
||||||
job.hasMany('allocations').reload(),
|
job.hasMany('allocations').reload(),
|
||||||
job.get('scaleState')
|
job.get('scaleState'),
|
||||||
]).then(() => taskGroup);
|
]).then(() => taskGroup);
|
||||||
})
|
})
|
||||||
.catch(notifyError(this));
|
.catch(notifyError(this));
|
||||||
|
@ -50,7 +50,7 @@ export default class TaskGroupRoute extends Route.extend(WithWatchers) {
|
||||||
allocations: this.watchAllocations.perform(job),
|
allocations: this.watchAllocations.perform(job),
|
||||||
latestDeployment:
|
latestDeployment:
|
||||||
job.get('supportsDeployments') &&
|
job.get('supportsDeployments') &&
|
||||||
this.watchLatestDeployment.perform(job)
|
this.watchLatestDeployment.perform(job),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default class BucketService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action deregisterBreadcrumb(crumb) {
|
@action deregisterBreadcrumb(crumb) {
|
||||||
const newCrumbs = this.crumbs.filter(c => c !== crumb);
|
const newCrumbs = this.crumbs.filter((c) => c !== crumb);
|
||||||
|
|
||||||
this.crumbs = newCrumbs;
|
this.crumbs = newCrumbs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,12 @@ export default function jobClientStatus(nodesKey, jobKey) {
|
||||||
return computed(
|
return computed(
|
||||||
`${nodesKey}.[]`,
|
`${nodesKey}.[]`,
|
||||||
`${jobKey}.{datacenters,status,allocations.@each.clientStatus,taskGroups}`,
|
`${jobKey}.{datacenters,status,allocations.@each.clientStatus,taskGroups}`,
|
||||||
function() {
|
function () {
|
||||||
const job = this.get(jobKey);
|
const job = this.get(jobKey);
|
||||||
const nodes = this.get(nodesKey);
|
const nodes = this.get(nodesKey);
|
||||||
|
|
||||||
// Filter nodes by the datacenters defined in the job.
|
// Filter nodes by the datacenters defined in the job.
|
||||||
const filteredNodes = nodes.filter(n => {
|
const filteredNodes = nodes.filter((n) => {
|
||||||
return job.datacenters.indexOf(n.datacenter) >= 0;
|
return job.datacenters.indexOf(n.datacenter) >= 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export default function jobClientStatus(nodesKey, jobKey) {
|
||||||
|
|
||||||
// Group the job allocations by the ID of the client that is running them.
|
// Group the job allocations by the ID of the client that is running them.
|
||||||
const allocsByNodeID = {};
|
const allocsByNodeID = {};
|
||||||
job.allocations.forEach(a => {
|
job.allocations.forEach((a) => {
|
||||||
const nodeId = a.belongsTo('node').id();
|
const nodeId = a.belongsTo('node').id();
|
||||||
if (!allocsByNodeID[nodeId]) {
|
if (!allocsByNodeID[nodeId]) {
|
||||||
allocsByNodeID[nodeId] = [];
|
allocsByNodeID[nodeId] = [];
|
||||||
|
@ -47,7 +47,7 @@ export default function jobClientStatus(nodesKey, jobKey) {
|
||||||
byStatus: {},
|
byStatus: {},
|
||||||
totalNodes: filteredNodes.length,
|
totalNodes: filteredNodes.length,
|
||||||
};
|
};
|
||||||
filteredNodes.forEach(n => {
|
filteredNodes.forEach((n) => {
|
||||||
const status = jobStatus(allocsByNodeID[n.id], job.taskGroups.length);
|
const status = jobStatus(allocsByNodeID[n.id], job.taskGroups.length);
|
||||||
result.byNode[n.id] = status;
|
result.byNode[n.id] = status;
|
||||||
|
|
||||||
|
@ -63,9 +63,9 @@ export default function jobClientStatus(nodesKey, jobKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function allQueued(nodes) {
|
function allQueued(nodes) {
|
||||||
const nodeIDs = nodes.map(n => n.id);
|
const nodeIDs = nodes.map((n) => n.id);
|
||||||
return {
|
return {
|
||||||
byNode: Object.fromEntries(nodeIDs.map(id => [id, 'queued'])),
|
byNode: Object.fromEntries(nodeIDs.map((id) => [id, 'queued'])),
|
||||||
byStatus: canonicalizeStatus({ queued: nodeIDs }),
|
byStatus: canonicalizeStatus({ queued: nodeIDs }),
|
||||||
totalNodes: nodes.length,
|
totalNodes: nodes.length,
|
||||||
};
|
};
|
||||||
|
@ -105,7 +105,7 @@ function jobStatus(allocs, expected) {
|
||||||
|
|
||||||
// Count how many allocations are in each `clientStatus` value.
|
// Count how many allocations are in each `clientStatus` value.
|
||||||
const summary = allocs
|
const summary = allocs
|
||||||
.filter(a => !a.isOld)
|
.filter((a) => !a.isOld)
|
||||||
.reduce((acc, a) => {
|
.reduce((acc, a) => {
|
||||||
const status = a.clientStatus;
|
const status = a.clientStatus;
|
||||||
if (!acc[status]) {
|
if (!acc[status]) {
|
||||||
|
|
|
@ -16,21 +16,21 @@ let job;
|
||||||
let node;
|
let node;
|
||||||
let allocation;
|
let allocation;
|
||||||
|
|
||||||
module('Acceptance | allocation detail', function(hooks) {
|
module('Acceptance | allocation detail', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
|
|
||||||
node = server.create('node');
|
node = server.create('node');
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupsCount: 1,
|
groupsCount: 1,
|
||||||
withGroupServices: true,
|
withGroupServices: true,
|
||||||
createAllocations: false
|
createAllocations: false,
|
||||||
});
|
});
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure the node has an unhealthy driver
|
// Make sure the node has an unhealthy driver
|
||||||
|
@ -38,14 +38,14 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
driver: assign(node.drivers, {
|
driver: assign(node.drivers, {
|
||||||
docker: {
|
docker: {
|
||||||
detected: true,
|
detected: true,
|
||||||
healthy: false
|
healthy: false,
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure a task for the allocation depends on the unhealthy driver
|
// Make sure a task for the allocation depends on the unhealthy driver
|
||||||
server.schema.tasks.first().update({
|
server.schema.tasks.first().update({
|
||||||
driver: 'docker'
|
driver: 'docker',
|
||||||
});
|
});
|
||||||
|
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
|
@ -53,11 +53,11 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it passes an accessibility audit', async function(assert) {
|
test('it passes an accessibility audit', async function (assert) {
|
||||||
await a11yAudit(assert);
|
await a11yAudit(assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id should name the allocation and link to the corresponding job and node', async function(assert) {
|
test('/allocation/:id should name the allocation and link to the corresponding job and node', async function (assert) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
Allocation.title.includes(allocation.name),
|
Allocation.title.includes(allocation.name),
|
||||||
'Allocation name is in the heading'
|
'Allocation name is in the heading'
|
||||||
|
@ -93,7 +93,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id should include resource utilization graphs', async function(assert) {
|
test('/allocation/:id should include resource utilization graphs', async function (assert) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Allocation.resourceCharts.length,
|
Allocation.resourceCharts.length,
|
||||||
2,
|
2,
|
||||||
|
@ -111,17 +111,17 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id should present task lifecycles', async function(assert) {
|
test('/allocation/:id should present task lifecycles', async function (assert) {
|
||||||
const job = server.create('job', {
|
const job = server.create('job', {
|
||||||
groupsCount: 1,
|
groupsCount: 1,
|
||||||
groupTaskCount: 6,
|
groupTaskCount: 6,
|
||||||
withGroupServices: true,
|
withGroupServices: true,
|
||||||
createAllocations: false
|
createAllocations: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const allocation = server.create('allocation', 'withTaskWithPorts', {
|
const allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running',
|
clientStatus: 'running',
|
||||||
jobId: job.id
|
jobId: job.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
|
@ -136,7 +136,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
const prestartEphemeralTask = server.db.taskStates
|
const prestartEphemeralTask = server.db.taskStates
|
||||||
.where({ allocationId: allocation.id })
|
.where({ allocationId: allocation.id })
|
||||||
.sortBy('name')
|
.sortBy('name')
|
||||||
.find(taskState => {
|
.find((taskState) => {
|
||||||
const task = server.db.tasks.findBy({ name: taskState.name });
|
const task = server.db.tasks.findBy({ name: taskState.name });
|
||||||
return (
|
return (
|
||||||
task.Lifecycle &&
|
task.Lifecycle &&
|
||||||
|
@ -151,7 +151,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id should list all tasks for the allocation', async function(assert) {
|
test('/allocation/:id should list all tasks for the allocation', async function (assert) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Allocation.tasks.length,
|
Allocation.tasks.length,
|
||||||
server.db.taskStates.where({ allocationId: allocation.id }).length,
|
server.db.taskStates.where({ allocationId: allocation.id }).length,
|
||||||
|
@ -160,7 +160,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
assert.notOk(Allocation.isEmpty, 'Task table empty state is not shown');
|
assert.notOk(Allocation.isEmpty, 'Task table empty state is not shown');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each task row should list high-level information for the task', async function(assert) {
|
test('each task row should list high-level information for the task', async function (assert) {
|
||||||
const task = server.db.taskStates
|
const task = server.db.taskStates
|
||||||
.where({ allocationId: allocation.id })
|
.where({ allocationId: allocation.id })
|
||||||
.sortBy('name')[0];
|
.sortBy('name')[0];
|
||||||
|
@ -169,16 +169,16 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
|
|
||||||
const taskGroup = server.schema.taskGroups.where({
|
const taskGroup = server.schema.taskGroups.where({
|
||||||
jobId: allocation.jobId,
|
jobId: allocation.jobId,
|
||||||
name: allocation.taskGroup
|
name: allocation.taskGroup,
|
||||||
}).models[0];
|
}).models[0];
|
||||||
|
|
||||||
const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
|
const jobTask = taskGroup.tasks.models.find((m) => m.name === task.name);
|
||||||
const volumes = jobTask.volumeMounts.map(volume => ({
|
const volumes = jobTask.volumeMounts.map((volume) => ({
|
||||||
name: volume.Volume,
|
name: volume.Volume,
|
||||||
source: taskGroup.volumes[volume.Volume].Source
|
source: taskGroup.volumes[volume.Volume].Source,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Allocation.tasks[0].as(taskRow => {
|
Allocation.tasks[0].as((taskRow) => {
|
||||||
assert.equal(taskRow.name, task.name, 'Name');
|
assert.equal(taskRow.name, task.name, 'Name');
|
||||||
assert.equal(taskRow.state, task.state, 'State');
|
assert.equal(taskRow.state, task.state, 'State');
|
||||||
assert.equal(taskRow.message, event.displayMessage, 'Event Message');
|
assert.equal(taskRow.message, event.displayMessage, 'Event Message');
|
||||||
|
@ -189,7 +189,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const volumesText = taskRow.volumes;
|
const volumesText = taskRow.volumes;
|
||||||
volumes.forEach(volume => {
|
volumes.forEach((volume) => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
volumesText.includes(volume.name),
|
volumesText.includes(volume.name),
|
||||||
`Found label ${volume.name}`
|
`Found label ${volume.name}`
|
||||||
|
@ -202,7 +202,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each task row should link to the task detail page', async function(assert) {
|
test('each task row should link to the task detail page', async function (assert) {
|
||||||
const task = server.db.taskStates
|
const task = server.db.taskStates
|
||||||
.where({ allocationId: allocation.id })
|
.where({ allocationId: allocation.id })
|
||||||
.sortBy('name')[0];
|
.sortBy('name')[0];
|
||||||
|
@ -226,16 +226,16 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('tasks with an unhealthy driver have a warning icon', async function(assert) {
|
test('tasks with an unhealthy driver have a warning icon', async function (assert) {
|
||||||
// Driver health status require node:read permission.
|
// Driver health status require node:read permission.
|
||||||
const policy = server.create('policy', {
|
const policy = server.create('policy', {
|
||||||
id: 'node-read',
|
id: 'node-read',
|
||||||
name: 'node-read',
|
name: 'node-read',
|
||||||
rulesJSON: {
|
rulesJSON: {
|
||||||
Node: {
|
Node: {
|
||||||
Policy: 'read'
|
Policy: 'read',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -253,17 +253,17 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('proxy task has a proxy tag', async function(assert) {
|
test('proxy task has a proxy tag', async function (assert) {
|
||||||
// Must create a new job as existing one has loaded and it contains the tasks
|
// Must create a new job as existing one has loaded and it contains the tasks
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupsCount: 1,
|
groupsCount: 1,
|
||||||
withGroupServices: true,
|
withGroupServices: true,
|
||||||
createAllocations: false
|
createAllocations: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running',
|
clientStatus: 'running',
|
||||||
jobId: job.id
|
jobId: job.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskState = allocation.taskStates.models.sortBy('name')[0];
|
const taskState = allocation.taskStates.models.sortBy('name')[0];
|
||||||
|
@ -276,24 +276,24 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
assert.ok(Allocation.tasks[0].hasProxyTag);
|
assert.ok(Allocation.tasks[0].hasProxyTag);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there are no tasks, an empty state is shown', async function(assert) {
|
test('when there are no tasks, an empty state is shown', async function (assert) {
|
||||||
// Make sure the allocation is pending in order to ensure there are no tasks
|
// Make sure the allocation is pending in order to ensure there are no tasks
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'pending'
|
clientStatus: 'pending',
|
||||||
});
|
});
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
|
|
||||||
assert.ok(Allocation.isEmpty, 'Task table empty state is shown');
|
assert.ok(Allocation.isEmpty, 'Task table empty state is shown');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation has not been rescheduled, the reschedule events section is not rendered', async function(assert) {
|
test('when the allocation has not been rescheduled, the reschedule events section is not rendered', async function (assert) {
|
||||||
assert.notOk(
|
assert.notOk(
|
||||||
Allocation.hasRescheduleEvents,
|
Allocation.hasRescheduleEvents,
|
||||||
'Reschedule Events section exists'
|
'Reschedule Events section exists'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ports are listed', async function(assert) {
|
test('ports are listed', async function (assert) {
|
||||||
const allServerPorts = allocation.taskResources.models[0].resources.Ports;
|
const allServerPorts = allocation.taskResources.models[0].resources.Ports;
|
||||||
|
|
||||||
allServerPorts.sortBy('Label').forEach((serverPort, index) => {
|
allServerPorts.sortBy('Label').forEach((serverPort, index) => {
|
||||||
|
@ -308,9 +308,9 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('services are listed', async function(assert) {
|
test('services are listed', async function (assert) {
|
||||||
const taskGroup = server.schema.taskGroups.findBy({
|
const taskGroup = server.schema.taskGroups.findBy({
|
||||||
name: allocation.taskGroup
|
name: allocation.taskGroup,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(Allocation.services.length, taskGroup.services.length);
|
assert.equal(Allocation.services.length, taskGroup.services.length);
|
||||||
|
@ -331,7 +331,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
const upstreams = serverService.Connect.SidecarService.Proxy.Upstreams;
|
const upstreams = serverService.Connect.SidecarService.Proxy.Upstreams;
|
||||||
const serverUpstreamsString = upstreams
|
const serverUpstreamsString = upstreams
|
||||||
.map(
|
.map(
|
||||||
upstream => `${upstream.DestinationName}:${upstream.LocalBindPort}`
|
(upstream) => `${upstream.DestinationName}:${upstream.LocalBindPort}`
|
||||||
)
|
)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
|
@ -339,12 +339,12 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation is not found, an error message is shown, but the URL persists', async function(assert) {
|
test('when the allocation is not found, an error message is shown, but the URL persists', async function (assert) {
|
||||||
await Allocation.visit({ id: 'not-a-real-allocation' });
|
await Allocation.visit({ id: 'not-a-real-allocation' });
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
server.pretender.handledRequests
|
server.pretender.handledRequests
|
||||||
.filter(request => !request.url.includes('policy'))
|
.filter((request) => !request.url.includes('policy'))
|
||||||
.findBy('status', 404).url,
|
.findBy('status', 404).url,
|
||||||
'/v1/allocation/not-a-real-allocation',
|
'/v1/allocation/not-a-real-allocation',
|
||||||
'A request to the nonexistent allocation is made'
|
'A request to the nonexistent allocation is made'
|
||||||
|
@ -362,20 +362,20 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allocation can be stopped', async function(assert) {
|
test('allocation can be stopped', async function (assert) {
|
||||||
await Allocation.stop.idle();
|
await Allocation.stop.idle();
|
||||||
await Allocation.stop.confirm();
|
await Allocation.stop.confirm();
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
server.pretender.handledRequests
|
server.pretender.handledRequests
|
||||||
.reject(request => request.url.includes('fuzzy'))
|
.reject((request) => request.url.includes('fuzzy'))
|
||||||
.findBy('method', 'POST').url,
|
.findBy('method', 'POST').url,
|
||||||
`/v1/allocation/${allocation.id}/stop`,
|
`/v1/allocation/${allocation.id}/stop`,
|
||||||
'Stop request is made for the allocation'
|
'Stop request is made for the allocation'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allocation can be restarted', async function(assert) {
|
test('allocation can be restarted', async function (assert) {
|
||||||
await Allocation.restart.idle();
|
await Allocation.restart.idle();
|
||||||
await Allocation.restart.confirm();
|
await Allocation.restart.confirm();
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('while an allocation is being restarted, the stop button is disabled', async function(assert) {
|
test('while an allocation is being restarted, the stop button is disabled', async function (assert) {
|
||||||
server.pretender.post('/v1/allocation/:id/stop', () => [204, {}, ''], true);
|
server.pretender.post('/v1/allocation/:id/stop', () => [204, {}, ''], true);
|
||||||
|
|
||||||
await Allocation.stop.idle();
|
await Allocation.stop.idle();
|
||||||
|
@ -400,7 +400,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
await Allocation.stop.confirm();
|
await Allocation.stop.confirm();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('if stopping or restarting fails, an error message is shown', async function(assert) {
|
test('if stopping or restarting fails, an error message is shown', async function (assert) {
|
||||||
server.pretender.post('/v1/allocation/:id/stop', () => [403, {}, '']);
|
server.pretender.post('/v1/allocation/:id/stop', () => [403, {}, '']);
|
||||||
|
|
||||||
await Allocation.stop.idle();
|
await Allocation.stop.idle();
|
||||||
|
@ -425,11 +425,11 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | allocation detail (rescheduled)', function(hooks) {
|
module('Acceptance | allocation detail (rescheduled)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
|
|
||||||
node = server.create('node');
|
node = server.create('node');
|
||||||
|
@ -439,7 +439,7 @@ module('Acceptance | allocation detail (rescheduled)', function(hooks) {
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation has been rescheduled, the reschedule events section is rendered', async function(assert) {
|
test('when the allocation has been rescheduled, the reschedule events section is rendered', async function (assert) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
Allocation.hasRescheduleEvents,
|
Allocation.hasRescheduleEvents,
|
||||||
'Reschedule Events section exists'
|
'Reschedule Events section exists'
|
||||||
|
@ -447,11 +447,11 @@ module('Acceptance | allocation detail (rescheduled)', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | allocation detail (not running)', function(hooks) {
|
module('Acceptance | allocation detail (not running)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
|
|
||||||
node = server.create('node');
|
node = server.create('node');
|
||||||
|
@ -461,7 +461,7 @@ module('Acceptance | allocation detail (not running)', function(hooks) {
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation is not running, the utilization graphs are replaced by an empty message', async function(assert) {
|
test('when the allocation is not running, the utilization graphs are replaced by an empty message', async function (assert) {
|
||||||
assert.equal(Allocation.resourceCharts.length, 0, 'No resource charts');
|
assert.equal(Allocation.resourceCharts.length, 0, 'No resource charts');
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Allocation.resourceEmptyMessage,
|
Allocation.resourceEmptyMessage,
|
||||||
|
@ -470,25 +470,25 @@ module('Acceptance | allocation detail (not running)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the exec and stop/restart buttons are absent', async function(assert) {
|
test('the exec and stop/restart buttons are absent', async function (assert) {
|
||||||
assert.notOk(Allocation.execButton.isPresent);
|
assert.notOk(Allocation.execButton.isPresent);
|
||||||
assert.notOk(Allocation.stop.isPresent);
|
assert.notOk(Allocation.stop.isPresent);
|
||||||
assert.notOk(Allocation.restart.isPresent);
|
assert.notOk(Allocation.restart.isPresent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
module('Acceptance | allocation detail (preemptions)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
node = server.create('node');
|
node = server.create('node');
|
||||||
job = server.create('job', { createAllocations: false });
|
job = server.create('job', { createAllocations: false });
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shows a dedicated section to the allocation that preempted this allocation', async function(assert) {
|
test('shows a dedicated section to the allocation that preempted this allocation', async function (assert) {
|
||||||
allocation = server.create('allocation', 'preempted');
|
allocation = server.create('allocation', 'preempted');
|
||||||
const preempter = server.schema.find(
|
const preempter = server.schema.find(
|
||||||
'allocation',
|
'allocation',
|
||||||
|
@ -539,7 +539,7 @@ module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shows a dedicated section to the allocations this allocation preempted', async function(assert) {
|
test('shows a dedicated section to the allocations this allocation preempted', async function (assert) {
|
||||||
allocation = server.create('allocation', 'preempter');
|
allocation = server.create('allocation', 'preempter');
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
@ -548,12 +548,12 @@ module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each preempted allocation in the table lists basic allocation information', async function(assert) {
|
test('each preempted allocation in the table lists basic allocation information', async function (assert) {
|
||||||
allocation = server.create('allocation', 'preempter');
|
allocation = server.create('allocation', 'preempter');
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
|
|
||||||
const preemption = allocation.preemptedAllocations
|
const preemption = allocation.preemptedAllocations
|
||||||
.map(id => server.schema.find('allocation', id))
|
.map((id) => server.schema.find('allocation', id))
|
||||||
.sortBy('modifyIndex')
|
.sortBy('modifyIndex')
|
||||||
.reverse()[0];
|
.reverse()[0];
|
||||||
const preemptionRow = Allocation.preemptions.objectAt(0);
|
const preemptionRow = Allocation.preemptions.objectAt(0);
|
||||||
|
@ -596,16 +596,16 @@ module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking the client ID in the preempted allocation row naviates to the client page', async function(assert) {
|
test('clicking the client ID in the preempted allocation row naviates to the client page', async function (assert) {
|
||||||
// Navigating to the client page requires node:read permission.
|
// Navigating to the client page requires node:read permission.
|
||||||
const policy = server.create('policy', {
|
const policy = server.create('policy', {
|
||||||
id: 'node-read',
|
id: 'node-read',
|
||||||
name: 'node-read',
|
name: 'node-read',
|
||||||
rulesJSON: {
|
rulesJSON: {
|
||||||
Node: {
|
Node: {
|
||||||
Policy: 'read'
|
Policy: 'read',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -616,7 +616,7 @@ module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
|
|
||||||
const preemption = allocation.preemptedAllocations
|
const preemption = allocation.preemptedAllocations
|
||||||
.map(id => server.schema.find('allocation', id))
|
.map((id) => server.schema.find('allocation', id))
|
||||||
.sortBy('modifyIndex')
|
.sortBy('modifyIndex')
|
||||||
.reverse()[0];
|
.reverse()[0];
|
||||||
const preemptionRow = Allocation.preemptions.objectAt(0);
|
const preemptionRow = Allocation.preemptions.objectAt(0);
|
||||||
|
@ -629,7 +629,7 @@ module('Acceptance | allocation detail (preemptions)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when an allocation both preempted allocations and was preempted itself, both preemptions sections are shown', async function(assert) {
|
test('when an allocation both preempted allocations and was preempted itself, both preemptions sections are shown', async function (assert) {
|
||||||
allocation = server.create('allocation', 'preempter', 'preempted');
|
allocation = server.create('allocation', 'preempter', 'preempted');
|
||||||
await Allocation.visit({ id: allocation.id });
|
await Allocation.visit({ id: allocation.id });
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|
|
@ -11,11 +11,11 @@ let node;
|
||||||
let managementToken;
|
let managementToken;
|
||||||
let clientToken;
|
let clientToken;
|
||||||
|
|
||||||
module('Acceptance | client monitor', function(hooks) {
|
module('Acceptance | client monitor', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(function() {
|
hooks.beforeEach(function () {
|
||||||
node = server.create('node');
|
node = server.create('node');
|
||||||
|
|
||||||
managementToken = server.create('token');
|
managementToken = server.create('token');
|
||||||
|
@ -27,12 +27,14 @@ module('Acceptance | client monitor', function(hooks) {
|
||||||
run.later(run, run.cancelTimers, 500);
|
run.later(run, run.cancelTimers, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it passes an accessibility audit', async function(assert) {
|
test('it passes an accessibility audit', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
await ClientMonitor.visit({ id: node.id });
|
await ClientMonitor.visit({ id: node.id });
|
||||||
await a11yAudit(assert);
|
await a11yAudit(assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/clients/:id/monitor should have a breadcrumb trail linking back to clients', async function(assert) {
|
test('/clients/:id/monitor should have a breadcrumb trail linking back to clients', async function (assert) {
|
||||||
await ClientMonitor.visit({ id: node.id });
|
await ClientMonitor.visit({ id: node.id });
|
||||||
|
|
||||||
assert.equal(Layout.breadcrumbFor('clients.index').text, 'Clients');
|
assert.equal(Layout.breadcrumbFor('clients.index').text, 'Clients');
|
||||||
|
@ -45,10 +47,10 @@ module('Acceptance | client monitor', function(hooks) {
|
||||||
assert.equal(currentURL(), '/clients');
|
assert.equal(currentURL(), '/clients');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the monitor page immediately streams agent monitor output at the info level', async function(assert) {
|
test('the monitor page immediately streams agent monitor output at the info level', async function (assert) {
|
||||||
await ClientMonitor.visit({ id: node.id });
|
await ClientMonitor.visit({ id: node.id });
|
||||||
|
|
||||||
const logRequest = server.pretender.handledRequests.find(req =>
|
const logRequest = server.pretender.handledRequests.find((req) =>
|
||||||
req.url.startsWith('/v1/agent/monitor')
|
req.url.startsWith('/v1/agent/monitor')
|
||||||
);
|
);
|
||||||
assert.ok(ClientMonitor.logsArePresent);
|
assert.ok(ClientMonitor.logsArePresent);
|
||||||
|
@ -56,13 +58,13 @@ module('Acceptance | client monitor', function(hooks) {
|
||||||
assert.ok(logRequest.url.includes('log_level=info'));
|
assert.ok(logRequest.url.includes('log_level=info'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('switching the log level persists the new log level as a query param', async function(assert) {
|
test('switching the log level persists the new log level as a query param', async function (assert) {
|
||||||
await ClientMonitor.visit({ id: node.id });
|
await ClientMonitor.visit({ id: node.id });
|
||||||
await ClientMonitor.selectLogLevel('Debug');
|
await ClientMonitor.selectLogLevel('Debug');
|
||||||
assert.equal(currentURL(), `/clients/${node.id}/monitor?level=debug`);
|
assert.equal(currentURL(), `/clients/${node.id}/monitor?level=debug`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the current access token does not include the agent:read rule, a descriptive error message is shown', async function(assert) {
|
test('when the current access token does not include the agent:read rule, a descriptive error message is shown', async function (assert) {
|
||||||
window.localStorage.nomadTokenSecret = clientToken.secretId;
|
window.localStorage.nomadTokenSecret = clientToken.secretId;
|
||||||
|
|
||||||
await ClientMonitor.visit({ id: node.id });
|
await ClientMonitor.visit({ id: node.id });
|
||||||
|
|
|
@ -40,7 +40,10 @@ module('Acceptance | server monitor', function (hooks) {
|
||||||
'Servers',
|
'Servers',
|
||||||
'The page should read the breadcrumb Servers'
|
'The page should read the breadcrumb Servers'
|
||||||
);
|
);
|
||||||
assert.equal(Layout.breadcrumbFor('servers.server').text, `Server ${agent.name}`);
|
assert.equal(
|
||||||
|
Layout.breadcrumbFor('servers.server').text,
|
||||||
|
`Server ${agent.name}`
|
||||||
|
);
|
||||||
|
|
||||||
await Layout.breadcrumbFor('servers.index').visit();
|
await Layout.breadcrumbFor('servers.index').visit();
|
||||||
assert.equal(currentURL(), '/servers');
|
assert.equal(currentURL(), '/servers');
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable qunit/require-expect */
|
||||||
import { currentURL, waitFor } from '@ember/test-helpers';
|
import { currentURL, waitFor } from '@ember/test-helpers';
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
|
@ -10,27 +11,29 @@ import moment from 'moment';
|
||||||
let allocation;
|
let allocation;
|
||||||
let task;
|
let task;
|
||||||
|
|
||||||
module('Acceptance | task detail', function(hooks) {
|
module('Acceptance | task detail', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node');
|
server.create('node');
|
||||||
server.create('job', { createAllocations: false });
|
server.create('job', { createAllocations: false });
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
||||||
|
|
||||||
await Task.visit({ id: allocation.id, name: task.name });
|
await Task.visit({ id: allocation.id, name: task.name });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it passes an accessibility audit', async function(assert) {
|
test('it passes an accessibility audit', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
await a11yAudit(assert);
|
await a11yAudit(assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id/:task_name should name the task and list high-level task information', async function(assert) {
|
test('/allocation/:id/:task_name should name the task and list high-level task information', async function (assert) {
|
||||||
assert.ok(Task.title.text.includes(task.name), 'Task name');
|
assert.ok(Task.title.text.includes(task.name), 'Task name');
|
||||||
assert.ok(Task.state.includes(task.state), 'Task state');
|
assert.ok(Task.state.includes(task.state), 'Task state');
|
||||||
|
|
||||||
|
@ -61,7 +64,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.equal(document.title, `Task ${task.name} - Nomad`);
|
assert.equal(document.title, `Task ${task.name} - Nomad`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
|
test('breadcrumbs match jobs / job / task group / allocation / task', async function (assert) {
|
||||||
const { jobId, taskGroup } = allocation;
|
const { jobId, taskGroup } = allocation;
|
||||||
const job = server.db.jobs.find(jobId);
|
const job = server.db.jobs.find(jobId);
|
||||||
|
|
||||||
|
@ -122,7 +125,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/allocation/:id/:task_name should include resource utilization graphs', async function(assert) {
|
test('/allocation/:id/:task_name should include resource utilization graphs', async function (assert) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Task.resourceCharts.length,
|
Task.resourceCharts.length,
|
||||||
2,
|
2,
|
||||||
|
@ -140,7 +143,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the events table lists all recent events', async function(assert) {
|
test('the events table lists all recent events', async function (assert) {
|
||||||
const events = server.db.taskEvents.where({ taskStateId: task.id });
|
const events = server.db.taskEvents.where({ taskStateId: task.id });
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@ -150,26 +153,26 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a task has volumes, the volumes table is shown', async function(assert) {
|
test('when a task has volumes, the volumes table is shown', async function (assert) {
|
||||||
const taskGroup = server.schema.taskGroups.where({
|
const taskGroup = server.schema.taskGroups.where({
|
||||||
jobId: allocation.jobId,
|
jobId: allocation.jobId,
|
||||||
name: allocation.taskGroup
|
name: allocation.taskGroup,
|
||||||
}).models[0];
|
}).models[0];
|
||||||
|
|
||||||
const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
|
const jobTask = taskGroup.tasks.models.find((m) => m.name === task.name);
|
||||||
|
|
||||||
assert.ok(Task.hasVolumes);
|
assert.ok(Task.hasVolumes);
|
||||||
assert.equal(Task.volumes.length, jobTask.volumeMounts.length);
|
assert.equal(Task.volumes.length, jobTask.volumeMounts.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a task does not have volumes, the volumes table is not shown', async function(assert) {
|
test('when a task does not have volumes, the volumes table is not shown', async function (assert) {
|
||||||
const job = server.create('job', {
|
const job = server.create('job', {
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
noHostVolumes: true
|
noHostVolumes: true,
|
||||||
});
|
});
|
||||||
allocation = server.create('allocation', {
|
allocation = server.create('allocation', {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
||||||
|
|
||||||
|
@ -177,16 +180,16 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.notOk(Task.hasVolumes);
|
assert.notOk(Task.hasVolumes);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each volume in the volumes table shows information about the volume', async function(assert) {
|
test('each volume in the volumes table shows information about the volume', async function (assert) {
|
||||||
const taskGroup = server.schema.taskGroups.where({
|
const taskGroup = server.schema.taskGroups.where({
|
||||||
jobId: allocation.jobId,
|
jobId: allocation.jobId,
|
||||||
name: allocation.taskGroup
|
name: allocation.taskGroup,
|
||||||
}).models[0];
|
}).models[0];
|
||||||
|
|
||||||
const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
|
const jobTask = taskGroup.tasks.models.find((m) => m.name === task.name);
|
||||||
const volume = jobTask.volumeMounts[0];
|
const volume = jobTask.volumeMounts[0];
|
||||||
|
|
||||||
Task.volumes[0].as(volumeRow => {
|
Task.volumes[0].as((volumeRow) => {
|
||||||
assert.equal(volumeRow.name, volume.Volume);
|
assert.equal(volumeRow.name, volume.Volume);
|
||||||
assert.equal(volumeRow.destination, volume.Destination);
|
assert.equal(volumeRow.destination, volume.Destination);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@ -200,7 +203,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each recent event should list the time, type, and description of the event', async function(assert) {
|
test('each recent event should list the time, type, and description of the event', async function (assert) {
|
||||||
const event = server.db.taskEvents.where({ taskStateId: task.id })[0];
|
const event = server.db.taskEvents.where({ taskStateId: task.id })[0];
|
||||||
const recentEvent = Task.events.objectAt(Task.events.length - 1);
|
const recentEvent = Task.events.objectAt(Task.events.length - 1);
|
||||||
|
|
||||||
|
@ -213,12 +216,12 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.equal(recentEvent.message, event.displayMessage, 'Event message');
|
assert.equal(recentEvent.message, event.displayMessage, 'Event message');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation is not found, the application errors', async function(assert) {
|
test('when the allocation is not found, the application errors', async function (assert) {
|
||||||
await Task.visit({ id: 'not-a-real-allocation', name: task.name });
|
await Task.visit({ id: 'not-a-real-allocation', name: task.name });
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
server.pretender.handledRequests
|
server.pretender.handledRequests
|
||||||
.filter(request => !request.url.includes('policy'))
|
.filter((request) => !request.url.includes('policy'))
|
||||||
.findBy('status', 404).url,
|
.findBy('status', 404).url,
|
||||||
'/v1/allocation/not-a-real-allocation',
|
'/v1/allocation/not-a-real-allocation',
|
||||||
'A request to the nonexistent allocation is made'
|
'A request to the nonexistent allocation is made'
|
||||||
|
@ -232,7 +235,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
|
assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation is found but the task is not, the application errors', async function(assert) {
|
test('when the allocation is found but the task is not, the application errors', async function (assert) {
|
||||||
await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' });
|
await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' });
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
@ -251,7 +254,7 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
|
assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('task can be restarted', async function(assert) {
|
test('task can be restarted', async function (assert) {
|
||||||
await Task.restart.idle();
|
await Task.restart.idle();
|
||||||
await Task.restart.confirm();
|
await Task.restart.confirm();
|
||||||
|
|
||||||
|
@ -269,11 +272,11 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when task restart fails (403), an ACL permissions error message is shown', async function(assert) {
|
test('when task restart fails (403), an ACL permissions error message is shown', async function (assert) {
|
||||||
server.pretender.put('/v1/client/allocation/:id/restart', () => [
|
server.pretender.put('/v1/client/allocation/:id/restart', () => [
|
||||||
403,
|
403,
|
||||||
{},
|
{},
|
||||||
''
|
'',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Task.restart.idle();
|
await Task.restart.idle();
|
||||||
|
@ -294,12 +297,12 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown');
|
assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when task restart fails (500), the error message from the API is piped through to the alert', async function(assert) {
|
test('when task restart fails (500), the error message from the API is piped through to the alert', async function (assert) {
|
||||||
const message = 'A plaintext error message';
|
const message = 'A plaintext error message';
|
||||||
server.pretender.put('/v1/client/allocation/:id/restart', () => [
|
server.pretender.put('/v1/client/allocation/:id/restart', () => [
|
||||||
500,
|
500,
|
||||||
{},
|
{},
|
||||||
message
|
message,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Task.restart.idle();
|
await Task.restart.idle();
|
||||||
|
@ -314,21 +317,21 @@ module('Acceptance | task detail', function(hooks) {
|
||||||
assert.notOk(Task.inlineError.isShown);
|
assert.notOk(Task.inlineError.isShown);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('exec button is present', async function(assert) {
|
test('exec button is present', async function (assert) {
|
||||||
assert.ok(Task.execButton.isPresent);
|
assert.ok(Task.execButton.isPresent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | task detail (no addresses)', function(hooks) {
|
module('Acceptance | task detail (no addresses)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node');
|
server.create('node');
|
||||||
server.create('job');
|
server.create('job');
|
||||||
allocation = server.create('allocation', 'withoutTaskWithPorts', {
|
allocation = server.create('allocation', 'withoutTaskWithPorts', {
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
||||||
|
|
||||||
|
@ -336,28 +339,28 @@ module('Acceptance | task detail (no addresses)', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | task detail (different namespace)', function(hooks) {
|
module('Acceptance | task detail (different namespace)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node');
|
server.create('node');
|
||||||
server.create('namespace');
|
server.create('namespace');
|
||||||
server.create('namespace', { id: 'other-namespace' });
|
server.create('namespace', { id: 'other-namespace' });
|
||||||
server.create('job', {
|
server.create('job', {
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
namespaceId: 'other-namespace'
|
namespaceId: 'other-namespace',
|
||||||
});
|
});
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
||||||
|
|
||||||
await Task.visit({ id: allocation.id, name: task.name });
|
await Task.visit({ id: allocation.id, name: task.name });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
|
test('breadcrumbs match jobs / job / task group / allocation / task', async function (assert) {
|
||||||
const { jobId, taskGroup } = allocation;
|
const { jobId, taskGroup } = allocation;
|
||||||
const job = server.db.jobs.find(jobId);
|
const job = server.db.jobs.find(jobId);
|
||||||
|
|
||||||
|
@ -394,28 +397,28 @@ module('Acceptance | task detail (different namespace)', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | task detail (not running)', function(hooks) {
|
module('Acceptance | task detail (not running)', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node');
|
server.create('node');
|
||||||
server.create('namespace');
|
server.create('namespace');
|
||||||
server.create('namespace', { id: 'other-namespace' });
|
server.create('namespace', { id: 'other-namespace' });
|
||||||
server.create('job', {
|
server.create('job', {
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
namespaceId: 'other-namespace'
|
namespaceId: 'other-namespace',
|
||||||
});
|
});
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'complete'
|
clientStatus: 'complete',
|
||||||
});
|
});
|
||||||
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
task = server.db.taskStates.where({ allocationId: allocation.id })[0];
|
||||||
|
|
||||||
await Task.visit({ id: allocation.id, name: task.name });
|
await Task.visit({ id: allocation.id, name: task.name });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function(assert) {
|
test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function (assert) {
|
||||||
assert.equal(Task.resourceCharts.length, 0, 'No resource charts');
|
assert.equal(Task.resourceCharts.length, 0, 'No resource charts');
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Task.resourceEmptyMessage,
|
Task.resourceEmptyMessage,
|
||||||
|
@ -424,21 +427,21 @@ module('Acceptance | task detail (not running)', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('exec button is absent', async function(assert) {
|
test('exec button is absent', async function (assert) {
|
||||||
assert.notOk(Task.execButton.isPresent);
|
assert.notOk(Task.execButton.isPresent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Acceptance | proxy task detail', function(hooks) {
|
module('Acceptance | proxy task detail', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node');
|
server.create('node');
|
||||||
server.create('job', { createAllocations: false });
|
server.create('job', { createAllocations: false });
|
||||||
allocation = server.create('allocation', 'withTaskWithPorts', {
|
allocation = server.create('allocation', 'withTaskWithPorts', {
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskState = allocation.taskStates.models[0];
|
const taskState = allocation.taskStates.models[0];
|
||||||
|
@ -449,7 +452,7 @@ module('Acceptance | proxy task detail', function(hooks) {
|
||||||
await Task.visit({ id: allocation.id, name: taskState.name });
|
await Task.visit({ id: allocation.id, name: taskState.name });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a proxy tag is shown', async function(assert) {
|
test('a proxy tag is shown', async function (assert) {
|
||||||
assert.ok(Task.title.proxyTag.isPresent);
|
assert.ok(Task.title.proxyTag.isPresent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
formatBytes,
|
formatBytes,
|
||||||
formatHertz,
|
formatHertz,
|
||||||
formatScheduledBytes,
|
formatScheduledBytes,
|
||||||
formatScheduledHertz
|
formatScheduledHertz,
|
||||||
} from 'nomad-ui/utils/units';
|
} from 'nomad-ui/utils/units';
|
||||||
import TaskGroup from 'nomad-ui/tests/pages/jobs/job/task-group';
|
import TaskGroup from 'nomad-ui/tests/pages/jobs/job/task-group';
|
||||||
import Layout from 'nomad-ui/tests/pages/layout';
|
import Layout from 'nomad-ui/tests/pages/layout';
|
||||||
|
@ -24,30 +24,30 @@ let managementToken;
|
||||||
|
|
||||||
const sum = (total, n) => total + n;
|
const sum = (total, n) => total + n;
|
||||||
|
|
||||||
module('Acceptance | task group detail', function(hooks) {
|
module('Acceptance | task group detail', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('agent');
|
server.create('agent');
|
||||||
server.create('node', 'forceIPv4');
|
server.create('node', 'forceIPv4');
|
||||||
|
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupsCount: 2,
|
groupsCount: 2,
|
||||||
createAllocations: false
|
createAllocations: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskGroups = server.db.taskGroups.where({ jobId: job.id });
|
const taskGroups = server.db.taskGroups.where({ jobId: job.id });
|
||||||
taskGroup = taskGroups[0];
|
taskGroup = taskGroups[0];
|
||||||
|
|
||||||
tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
tasks = taskGroup.taskIds.map((id) => server.db.tasks.find(id));
|
||||||
|
|
||||||
server.create('node', 'forceIPv4');
|
server.create('node', 'forceIPv4');
|
||||||
|
|
||||||
allocations = server.createList('allocation', 2, {
|
allocations = server.createList('allocation', 2, {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
taskGroup: taskGroup.name,
|
taskGroup: taskGroup.name,
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Allocations associated to a different task group on the job to
|
// Allocations associated to a different task group on the job to
|
||||||
|
@ -55,20 +55,20 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
server.createList('allocation', 3, {
|
server.createList('allocation', 3, {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
taskGroup: taskGroups[1].name,
|
taskGroup: taskGroups[1].name,
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set a static name to make the search test deterministic
|
// Set a static name to make the search test deterministic
|
||||||
server.db.allocations.forEach(alloc => {
|
server.db.allocations.forEach((alloc) => {
|
||||||
alloc.name = 'aaaaa';
|
alloc.name = 'aaaaa';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mark the first alloc as rescheduled
|
// Mark the first alloc as rescheduled
|
||||||
allocations[0].update({
|
allocations[0].update({
|
||||||
nextAllocation: allocations[1].id
|
nextAllocation: allocations[1].id,
|
||||||
});
|
});
|
||||||
allocations[1].update({
|
allocations[1].update({
|
||||||
previousAllocation: allocations[0].id
|
previousAllocation: allocations[0].id,
|
||||||
});
|
});
|
||||||
|
|
||||||
managementToken = server.create('token');
|
managementToken = server.create('token');
|
||||||
|
@ -76,16 +76,16 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it passes an accessibility audit', async function(assert) {
|
test('it passes an accessibility audit', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
await a11yAudit(assert);
|
await a11yAudit(assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group should list high-level metrics for the allocation', async function(assert) {
|
test('/jobs/:id/:task-group should list high-level metrics for the allocation', async function (assert) {
|
||||||
const totalCPU = tasks.mapBy('resources.CPU').reduce(sum, 0);
|
const totalCPU = tasks.mapBy('resources.CPU').reduce(sum, 0);
|
||||||
const totalMemory = tasks.mapBy('resources.MemoryMB').reduce(sum, 0);
|
const totalMemory = tasks.mapBy('resources.MemoryMB').reduce(sum, 0);
|
||||||
const totalMemoryMax = tasks
|
const totalMemoryMax = tasks
|
||||||
.map(t => t.resources.MemoryMaxMB || t.resources.MemoryMB)
|
.map((t) => t.resources.MemoryMaxMB || t.resources.MemoryMB)
|
||||||
.reduce(sum, 0);
|
.reduce(sum, 0);
|
||||||
const totalDisk = taskGroup.ephemeralDisk.SizeMB;
|
const totalDisk = taskGroup.ephemeralDisk.SizeMB;
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group should have breadcrumbs for job and jobs', async function(assert) {
|
test('/jobs/:id/:task-group should have breadcrumbs for job and jobs', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@ -147,14 +147,14 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group first breadcrumb should link to jobs', async function(assert) {
|
test('/jobs/:id/:task-group first breadcrumb should link to jobs', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
await Layout.breadcrumbFor('jobs.index').visit();
|
await Layout.breadcrumbFor('jobs.index').visit();
|
||||||
assert.equal(currentURL(), '/jobs', 'First breadcrumb links back to jobs');
|
assert.equal(currentURL(), '/jobs', 'First breadcrumb links back to jobs');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group second breadcrumb should link to the job for the task group', async function(assert) {
|
test('/jobs/:id/:task-group second breadcrumb should link to the job for the task group', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
await Layout.breadcrumbFor('jobs.job.index').visit();
|
await Layout.breadcrumbFor('jobs.job.index').visit();
|
||||||
|
@ -165,7 +165,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the user has a client token that has a namespace with a policy to run and scale a job the autoscaler options should be available', async function(assert) {
|
test('when the user has a client token that has a namespace with a policy to run and scale a job the autoscaler options should be available', async function (assert) {
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
|
|
||||||
const SCALE_AND_WRITE_NAMESPACE = 'scale-and-write-namespace';
|
const SCALE_AND_WRITE_NAMESPACE = 'scale-and-write-namespace';
|
||||||
|
@ -174,7 +174,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
|
|
||||||
server.create('namespace', { id: SCALE_AND_WRITE_NAMESPACE });
|
server.create('namespace', { id: SCALE_AND_WRITE_NAMESPACE });
|
||||||
const secondNamespace = server.create('namespace', {
|
const secondNamespace = server.create('namespace', {
|
||||||
id: READ_ONLY_NAMESPACE
|
id: READ_ONLY_NAMESPACE,
|
||||||
});
|
});
|
||||||
|
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
|
@ -182,14 +182,14 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
noActiveDeployment: true,
|
noActiveDeployment: true,
|
||||||
namespaceId: SCALE_AND_WRITE_NAMESPACE
|
namespaceId: SCALE_AND_WRITE_NAMESPACE,
|
||||||
});
|
});
|
||||||
const scalingGroup = server.create('task-group', {
|
const scalingGroup = server.create('task-group', {
|
||||||
job,
|
job,
|
||||||
name: 'scaling',
|
name: 'scaling',
|
||||||
count: 1,
|
count: 1,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
withScaling: true
|
withScaling: true,
|
||||||
});
|
});
|
||||||
job.update({ taskGroupIds: [scalingGroup.id] });
|
job.update({ taskGroupIds: [scalingGroup.id] });
|
||||||
|
|
||||||
|
@ -198,14 +198,14 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
noActiveDeployment: true,
|
noActiveDeployment: true,
|
||||||
namespaceId: READ_ONLY_NAMESPACE
|
namespaceId: READ_ONLY_NAMESPACE,
|
||||||
});
|
});
|
||||||
const scalingGroup2 = server.create('task-group', {
|
const scalingGroup2 = server.create('task-group', {
|
||||||
job: job2,
|
job: job2,
|
||||||
name: 'scaling',
|
name: 'scaling',
|
||||||
count: 1,
|
count: 1,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
withScaling: true
|
withScaling: true,
|
||||||
});
|
});
|
||||||
job2.update({ taskGroupIds: [scalingGroup2.id] });
|
job2.update({ taskGroupIds: [scalingGroup2.id] });
|
||||||
|
|
||||||
|
@ -216,14 +216,14 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
Namespaces: [
|
Namespaces: [
|
||||||
{
|
{
|
||||||
Name: SCALE_AND_WRITE_NAMESPACE,
|
Name: SCALE_AND_WRITE_NAMESPACE,
|
||||||
Capabilities: ['scale-job', 'submit-job', 'read-job', 'list-jobs']
|
Capabilities: ['scale-job', 'submit-job', 'read-job', 'list-jobs'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: READ_ONLY_NAMESPACE,
|
Name: READ_ONLY_NAMESPACE,
|
||||||
Capabilities: ['list-jobs', 'read-job']
|
Capabilities: ['list-jobs', 'read-job'],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -234,7 +234,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
await TaskGroup.visit({
|
await TaskGroup.visit({
|
||||||
id: job.id,
|
id: job.id,
|
||||||
name: scalingGroup.name,
|
name: scalingGroup.name,
|
||||||
namespace: SCALE_AND_WRITE_NAMESPACE
|
namespace: SCALE_AND_WRITE_NAMESPACE,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@ -246,7 +246,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
await TaskGroup.visit({
|
await TaskGroup.visit({
|
||||||
id: job2.id,
|
id: job2.id,
|
||||||
name: scalingGroup2.name,
|
name: scalingGroup2.name,
|
||||||
namespace: secondNamespace.name
|
namespace: secondNamespace.name,
|
||||||
});
|
});
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentURL(),
|
currentURL(),
|
||||||
|
@ -255,11 +255,11 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
assert.ok(TaskGroup.countStepper.increment.isDisabled);
|
assert.ok(TaskGroup.countStepper.increment.isDisabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group should list one page of allocations for the task group', async function(assert) {
|
test('/jobs/:id/:task-group should list one page of allocations for the task group', async function (assert) {
|
||||||
server.createList('allocation', TaskGroup.pageSize, {
|
server.createList('allocation', TaskGroup.pageSize, {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
taskGroup: taskGroup.name,
|
taskGroup: taskGroup.name,
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
@ -277,7 +277,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each allocation should show basic information about the allocation', async function(assert) {
|
test('each allocation should show basic information about the allocation', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
const allocation = allocations.sortBy('modifyIndex').reverse()[0];
|
const allocation = allocations.sortBy('modifyIndex').reverse()[0];
|
||||||
|
@ -320,16 +320,16 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking the client ID in the allocation row naviates to the client page', async function(assert) {
|
test('clicking the client ID in the allocation row naviates to the client page', async function (assert) {
|
||||||
// Navigating to the client page requires node:read permission.
|
// Navigating to the client page requires node:read permission.
|
||||||
const policy = server.create('policy', {
|
const policy = server.create('policy', {
|
||||||
id: 'node-read',
|
id: 'node-read',
|
||||||
name: 'node-read',
|
name: 'node-read',
|
||||||
rulesJSON: {
|
rulesJSON: {
|
||||||
Node: {
|
Node: {
|
||||||
Policy: 'read'
|
Policy: 'read',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -349,14 +349,14 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each allocation should show stats about the allocation', async function(assert) {
|
test('each allocation should show stats about the allocation', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
const allocation = allocations.sortBy('name')[0];
|
const allocation = allocations.sortBy('name')[0];
|
||||||
const allocationRow = TaskGroup.allocations.objectAt(0);
|
const allocationRow = TaskGroup.allocations.objectAt(0);
|
||||||
|
|
||||||
const allocStats = server.db.clientAllocationStats.find(allocation.id);
|
const allocStats = server.db.clientAllocationStats.find(allocation.id);
|
||||||
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
const tasks = taskGroup.taskIds.map((id) => server.db.tasks.find(id));
|
||||||
|
|
||||||
const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
|
const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
|
||||||
const memoryUsed = tasks.reduce(
|
const memoryUsed = tasks.reduce(
|
||||||
|
@ -395,7 +395,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation search has no matches, there is an empty message', async function(assert) {
|
test('when the allocation search has no matches, there is an empty message', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
await TaskGroup.search('zzzzzz');
|
await TaskGroup.search('zzzzzz');
|
||||||
|
@ -408,7 +408,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the allocation has reschedule events, the allocation row is denoted with an icon', async function(assert) {
|
test('when the allocation has reschedule events, the allocation row is denoted with an icon', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
const rescheduleRow = TaskGroup.allocationFor(allocations[0].id);
|
const rescheduleRow = TaskGroup.allocationFor(allocations[0].id);
|
||||||
|
@ -421,10 +421,10 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
assert.notOk(normalRow.rescheduled, 'Normal row has no reschedule icon');
|
assert.notOk(normalRow.rescheduled, 'Normal row has no reschedule icon');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/jobs/:id/:task-group should present task lifecycles', async function(assert) {
|
test('/jobs/:id/:task-group should present task lifecycles', async function (assert) {
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupsCount: 2,
|
groupsCount: 2,
|
||||||
groupTaskCount: 3
|
groupTaskCount: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskGroups = server.db.taskGroups.where({ jobId: job.id });
|
const taskGroups = server.db.taskGroups.where({ jobId: job.id });
|
||||||
|
@ -438,21 +438,21 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
'Task Lifecycle Configuration'
|
'Task Lifecycle Configuration'
|
||||||
);
|
);
|
||||||
|
|
||||||
tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
tasks = taskGroup.taskIds.map((id) => server.db.tasks.find(id));
|
||||||
const taskNames = tasks.mapBy('name');
|
const taskNames = tasks.mapBy('name');
|
||||||
|
|
||||||
// This is thoroughly tested in allocation detail tests, so this mostly checks what’s different
|
// This is thoroughly tested in allocation detail tests, so this mostly checks what’s different
|
||||||
|
|
||||||
assert.equal(TaskGroup.lifecycleChart.tasks.length, 3);
|
assert.equal(TaskGroup.lifecycleChart.tasks.length, 3);
|
||||||
|
|
||||||
TaskGroup.lifecycleChart.tasks.forEach(Task => {
|
TaskGroup.lifecycleChart.tasks.forEach((Task) => {
|
||||||
assert.ok(taskNames.includes(Task.name));
|
assert.ok(taskNames.includes(Task.name));
|
||||||
assert.notOk(Task.isActive);
|
assert.notOk(Task.isActive);
|
||||||
assert.notOk(Task.isFinished);
|
assert.notOk(Task.isFinished);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the task group depends on volumes, the volumes table is shown', async function(assert) {
|
test('when the task group depends on volumes, the volumes table is shown', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
assert.ok(TaskGroup.hasVolumes);
|
assert.ok(TaskGroup.hasVolumes);
|
||||||
|
@ -462,7 +462,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the task group does not depend on volumes, the volumes table is not shown', async function(assert) {
|
test('when the task group does not depend on volumes, the volumes table is not shown', async function (assert) {
|
||||||
job = server.create('job', { noHostVolumes: true, shallow: true });
|
job = server.create('job', { noHostVolumes: true, shallow: true });
|
||||||
taskGroup = server.db.taskGroups.where({ jobId: job.id })[0];
|
taskGroup = server.db.taskGroups.where({ jobId: job.id })[0];
|
||||||
|
|
||||||
|
@ -471,10 +471,10 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
assert.notOk(TaskGroup.hasVolumes);
|
assert.notOk(TaskGroup.hasVolumes);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('each row in the volumes table lists information about the volume', async function(assert) {
|
test('each row in the volumes table lists information about the volume', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
|
||||||
TaskGroup.volumes[0].as(volumeRow => {
|
TaskGroup.volumes[0].as((volumeRow) => {
|
||||||
const volume = taskGroup.volumes[volumeRow.name];
|
const volume = taskGroup.volumes[volumeRow.name];
|
||||||
assert.equal(volumeRow.name, volume.Name);
|
assert.equal(volumeRow.name, volume.Name);
|
||||||
assert.equal(volumeRow.type, volume.Type);
|
assert.equal(volumeRow.type, volume.Type);
|
||||||
|
@ -486,21 +486,21 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the count stepper sends the appropriate POST request', async function(assert) {
|
test('the count stepper sends the appropriate POST request', async function (assert) {
|
||||||
window.localStorage.nomadTokenSecret = managementToken.secretId;
|
window.localStorage.nomadTokenSecret = managementToken.secretId;
|
||||||
|
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupCount: 0,
|
groupCount: 0,
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
noActiveDeployment: true
|
noActiveDeployment: true,
|
||||||
});
|
});
|
||||||
const scalingGroup = server.create('task-group', {
|
const scalingGroup = server.create('task-group', {
|
||||||
job,
|
job,
|
||||||
name: 'scaling',
|
name: 'scaling',
|
||||||
count: 1,
|
count: 1,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
withScaling: true
|
withScaling: true,
|
||||||
});
|
});
|
||||||
job.update({ taskGroupIds: [scalingGroup.id] });
|
job.update({ taskGroupIds: [scalingGroup.id] });
|
||||||
|
|
||||||
|
@ -509,28 +509,28 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
await settled();
|
await settled();
|
||||||
|
|
||||||
const scaleRequest = server.pretender.handledRequests.find(
|
const scaleRequest = server.pretender.handledRequests.find(
|
||||||
req => req.method === 'POST' && req.url.endsWith('/scale')
|
(req) => req.method === 'POST' && req.url.endsWith('/scale')
|
||||||
);
|
);
|
||||||
const requestBody = JSON.parse(scaleRequest.requestBody);
|
const requestBody = JSON.parse(scaleRequest.requestBody);
|
||||||
assert.equal(requestBody.Target.Group, scalingGroup.name);
|
assert.equal(requestBody.Target.Group, scalingGroup.name);
|
||||||
assert.equal(requestBody.Count, scalingGroup.count + 1);
|
assert.equal(requestBody.Count, scalingGroup.count + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the count stepper is disabled when a deployment is running', async function(assert) {
|
test('the count stepper is disabled when a deployment is running', async function (assert) {
|
||||||
window.localStorage.nomadTokenSecret = managementToken.secretId;
|
window.localStorage.nomadTokenSecret = managementToken.secretId;
|
||||||
|
|
||||||
job = server.create('job', {
|
job = server.create('job', {
|
||||||
groupCount: 0,
|
groupCount: 0,
|
||||||
createAllocations: false,
|
createAllocations: false,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
activeDeployment: true
|
activeDeployment: true,
|
||||||
});
|
});
|
||||||
const scalingGroup = server.create('task-group', {
|
const scalingGroup = server.create('task-group', {
|
||||||
job,
|
job,
|
||||||
name: 'scaling',
|
name: 'scaling',
|
||||||
count: 1,
|
count: 1,
|
||||||
shallow: true,
|
shallow: true,
|
||||||
withScaling: true
|
withScaling: true,
|
||||||
});
|
});
|
||||||
job.update({ taskGroupIds: [scalingGroup.id] });
|
job.update({ taskGroupIds: [scalingGroup.id] });
|
||||||
|
|
||||||
|
@ -541,15 +541,15 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
assert.ok(TaskGroup.countStepper.decrement.isDisabled);
|
assert.ok(TaskGroup.countStepper.decrement.isDisabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the job for the task group is not found, an error message is shown, but the URL persists', async function(assert) {
|
test('when the job for the task group is not found, an error message is shown, but the URL persists', async function (assert) {
|
||||||
await TaskGroup.visit({
|
await TaskGroup.visit({
|
||||||
id: 'not-a-real-job',
|
id: 'not-a-real-job',
|
||||||
name: 'not-a-real-task-group'
|
name: 'not-a-real-task-group',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
server.pretender.handledRequests
|
server.pretender.handledRequests
|
||||||
.filter(request => !request.url.includes('policy'))
|
.filter((request) => !request.url.includes('policy'))
|
||||||
.findBy('status', 404).url,
|
.findBy('status', 404).url,
|
||||||
'/v1/job/not-a-real-job',
|
'/v1/job/not-a-real-job',
|
||||||
'A request to the nonexistent job is made'
|
'A request to the nonexistent job is made'
|
||||||
|
@ -567,7 +567,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the task group is not found on the job, an error message is shown, but the URL persists', async function(assert) {
|
test('when the task group is not found on the job, an error message is shown, but the URL persists', async function (assert) {
|
||||||
await TaskGroup.visit({ id: job.id, name: 'not-a-real-task-group' });
|
await TaskGroup.visit({ id: job.id, name: 'not-a-real-task-group' });
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
@ -598,16 +598,16 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
server.createList('allocation', TaskGroup.pageSize, {
|
server.createList('allocation', TaskGroup.pageSize, {
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
taskGroup: taskGroup.name,
|
taskGroup: taskGroup.name,
|
||||||
clientStatus: 'running'
|
clientStatus: 'running',
|
||||||
});
|
});
|
||||||
|
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a task group has no scaling events, there is no recent scaling events section', async function(assert) {
|
test('when a task group has no scaling events, there is no recent scaling events section', async function (assert) {
|
||||||
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
||||||
m => m.name === taskGroup.name
|
(m) => m.name === taskGroup.name
|
||||||
);
|
);
|
||||||
taskGroupScale.update({ events: [] });
|
taskGroupScale.update({ events: [] });
|
||||||
|
|
||||||
|
@ -616,9 +616,9 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
assert.notOk(TaskGroup.hasScaleEvents);
|
assert.notOk(TaskGroup.hasScaleEvents);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the recent scaling events section shows all recent scaling events in reverse chronological order', async function(assert) {
|
test('the recent scaling events section shows all recent scaling events in reverse chronological order', async function (assert) {
|
||||||
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
||||||
m => m.name === taskGroup.name
|
(m) => m.name === taskGroup.name
|
||||||
);
|
);
|
||||||
taskGroupScale.update({
|
taskGroupScale.update({
|
||||||
events: [
|
events: [
|
||||||
|
@ -627,8 +627,8 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
server.create('scale-event', { error: true }),
|
server.create('scale-event', { error: true }),
|
||||||
server.create('scale-event', { error: true }),
|
server.create('scale-event', { error: true }),
|
||||||
server.create('scale-event', { count: 3, error: false }),
|
server.create('scale-event', { count: 3, error: false }),
|
||||||
server.create('scale-event', { count: 1, error: false })
|
server.create('scale-event', { count: 1, error: false }),
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
const scaleEvents = taskGroupScale.events.models.sortBy('time').reverse();
|
const scaleEvents = taskGroupScale.events.models.sortBy('time').reverse();
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
@ -660,9 +660,9 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a task group has at least two count scaling events and the count scaling events outnumber the non-count scaling events, a timeline is shown in addition to the accordion', async function(assert) {
|
test('when a task group has at least two count scaling events and the count scaling events outnumber the non-count scaling events, a timeline is shown in addition to the accordion', async function (assert) {
|
||||||
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
const taskGroupScale = job.jobScale.taskGroupScales.models.find(
|
||||||
m => m.name === taskGroup.name
|
(m) => m.name === taskGroup.name
|
||||||
);
|
);
|
||||||
taskGroupScale.update({
|
taskGroupScale.update({
|
||||||
events: [
|
events: [
|
||||||
|
@ -674,8 +674,8 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
server.create('scale-event', { count: 3, error: false }),
|
server.create('scale-event', { count: 3, error: false }),
|
||||||
server.create('scale-event', { count: 2, error: false }),
|
server.create('scale-event', { count: 2, error: false }),
|
||||||
server.create('scale-event', { count: 9, error: false }),
|
server.create('scale-event', { count: 9, error: false }),
|
||||||
server.create('scale-event', { count: 1, error: false })
|
server.create('scale-event', { count: 1, error: false }),
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
const scaleEvents = taskGroupScale.events.models.sortBy('time').reverse();
|
const scaleEvents = taskGroupScale.events.models.sortBy('time').reverse();
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
@ -685,7 +685,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
TaskGroup.scalingAnnotations.length,
|
TaskGroup.scalingAnnotations.length,
|
||||||
scaleEvents.filter(ev => ev.count == null).length
|
scaleEvents.filter((ev) => ev.count == null).length
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -694,7 +694,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
paramName: 'status',
|
paramName: 'status',
|
||||||
expectedOptions: ['Pending', 'Running', 'Complete', 'Failed', 'Lost'],
|
expectedOptions: ['Pending', 'Running', 'Complete', 'Failed', 'Lost'],
|
||||||
async beforeEach() {
|
async beforeEach() {
|
||||||
['pending', 'running', 'complete', 'failed', 'lost'].forEach(s => {
|
['pending', 'running', 'complete', 'failed', 'lost'].forEach((s) => {
|
||||||
server.createList('allocation', 5, { clientStatus: s });
|
server.createList('allocation', 5, { clientStatus: s });
|
||||||
});
|
});
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
@ -702,7 +702,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
filter: (alloc, selection) =>
|
filter: (alloc, selection) =>
|
||||||
alloc.jobId == job.id &&
|
alloc.jobId == job.id &&
|
||||||
alloc.taskGroup == taskGroup.name &&
|
alloc.taskGroup == taskGroup.name &&
|
||||||
selection.includes(alloc.clientStatus)
|
selection.includes(alloc.clientStatus),
|
||||||
});
|
});
|
||||||
|
|
||||||
testFacet('Client', {
|
testFacet('Client', {
|
||||||
|
@ -713,21 +713,21 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
new Set(
|
new Set(
|
||||||
allocs
|
allocs
|
||||||
.filter(
|
.filter(
|
||||||
alloc =>
|
(alloc) =>
|
||||||
alloc.jobId == job.id && alloc.taskGroup == taskGroup.name
|
alloc.jobId == job.id && alloc.taskGroup == taskGroup.name
|
||||||
)
|
)
|
||||||
.mapBy('nodeId')
|
.mapBy('nodeId')
|
||||||
.map(id => id.split('-')[0])
|
.map((id) => id.split('-')[0])
|
||||||
)
|
)
|
||||||
).sort();
|
).sort();
|
||||||
},
|
},
|
||||||
async beforeEach() {
|
async beforeEach() {
|
||||||
const nodes = server.createList('node', 3, 'forceIPv4');
|
const nodes = server.createList('node', 3, 'forceIPv4');
|
||||||
nodes.forEach(node =>
|
nodes.forEach((node) =>
|
||||||
server.createList('allocation', 5, {
|
server.createList('allocation', 5, {
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
taskGroup: taskGroup.name
|
taskGroup: taskGroup.name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
await TaskGroup.visit({ id: job.id, name: taskGroup.name });
|
||||||
|
@ -735,7 +735,7 @@ module('Acceptance | task group detail', function(hooks) {
|
||||||
filter: (alloc, selection) =>
|
filter: (alloc, selection) =>
|
||||||
alloc.jobId == job.id &&
|
alloc.jobId == job.id &&
|
||||||
alloc.taskGroup == taskGroup.name &&
|
alloc.taskGroup == taskGroup.name &&
|
||||||
selection.includes(alloc.nodeId.split('-')[0])
|
selection.includes(alloc.nodeId.split('-')[0]),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -743,7 +743,7 @@ function testFacet(
|
||||||
label,
|
label,
|
||||||
{ facet, paramName, beforeEach, filter, expectedOptions }
|
{ facet, paramName, beforeEach, filter, expectedOptions }
|
||||||
) {
|
) {
|
||||||
test(`facet ${label} | the ${label} facet has the correct options`, async function(assert) {
|
test(`facet ${label} | the ${label} facet has the correct options`, async function (assert) {
|
||||||
await beforeEach();
|
await beforeEach();
|
||||||
await facet.toggle();
|
await facet.toggle();
|
||||||
|
|
||||||
|
@ -755,13 +755,13 @@ function testFacet(
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
facet.options.map(option => option.label.trim()),
|
facet.options.map((option) => option.label.trim()),
|
||||||
expectation,
|
expectation,
|
||||||
'Options for facet are as expected'
|
'Options for facet are as expected'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`facet ${label} | the ${label} facet filters the allocations list by ${label}`, async function(assert) {
|
test(`facet ${label} | the ${label} facet filters the allocations list by ${label}`, async function (assert) {
|
||||||
let option;
|
let option;
|
||||||
|
|
||||||
await beforeEach();
|
await beforeEach();
|
||||||
|
@ -772,7 +772,7 @@ function testFacet(
|
||||||
|
|
||||||
const selection = [option.key];
|
const selection = [option.key];
|
||||||
const expectedAllocs = server.db.allocations
|
const expectedAllocs = server.db.allocations
|
||||||
.filter(alloc => filter(alloc, selection))
|
.filter((alloc) => filter(alloc, selection))
|
||||||
.sortBy('modifyIndex')
|
.sortBy('modifyIndex')
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
|
@ -785,7 +785,7 @@ function testFacet(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`facet ${label} | selecting multiple options in the ${label} facet results in a broader search`, async function(assert) {
|
test(`facet ${label} | selecting multiple options in the ${label} facet results in a broader search`, async function (assert) {
|
||||||
const selection = [];
|
const selection = [];
|
||||||
|
|
||||||
await beforeEach();
|
await beforeEach();
|
||||||
|
@ -799,7 +799,7 @@ function testFacet(
|
||||||
selection.push(option2.key);
|
selection.push(option2.key);
|
||||||
|
|
||||||
const expectedAllocs = server.db.allocations
|
const expectedAllocs = server.db.allocations
|
||||||
.filter(alloc => filter(alloc, selection))
|
.filter((alloc) => filter(alloc, selection))
|
||||||
.sortBy('modifyIndex')
|
.sortBy('modifyIndex')
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
|
@ -812,7 +812,7 @@ function testFacet(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`facet ${label} | selecting options in the ${label} facet updates the ${paramName} query param`, async function(assert) {
|
test(`facet ${label} | selecting options in the ${label} facet updates the ${paramName} query param`, async function (assert) {
|
||||||
const selection = [];
|
const selection = [];
|
||||||
|
|
||||||
await beforeEach();
|
await beforeEach();
|
||||||
|
|
|
@ -16,10 +16,10 @@ export default function moduleForJob(
|
||||||
) {
|
) {
|
||||||
let job;
|
let job;
|
||||||
|
|
||||||
module(title, function(hooks) {
|
module(title, function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
hooks.before(function() {
|
hooks.before(function () {
|
||||||
if (context !== 'allocations' && context !== 'children') {
|
if (context !== 'allocations' && context !== 'children') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid context provided to moduleForJob, expected either "allocations" or "children", got ${context}`
|
`Invalid context provided to moduleForJob, expected either "allocations" or "children", got ${context}`
|
||||||
|
@ -27,7 +27,7 @@ export default function moduleForJob(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
server.create('node');
|
server.create('node');
|
||||||
job = jobFactory();
|
job = jobFactory();
|
||||||
if (!job.namespace || job.namespace === 'default') {
|
if (!job.namespace || job.namespace === 'default') {
|
||||||
|
@ -37,7 +37,7 @@ export default function moduleForJob(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting /jobs/:job_id', async function(assert) {
|
test('visiting /jobs/:job_id', async function (assert) {
|
||||||
const expectedURL = new URL(
|
const expectedURL = new URL(
|
||||||
urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}`, job.namespace),
|
urlWithNamespace(`/jobs/${encodeURIComponent(job.id)}`, job.namespace),
|
||||||
window.location
|
window.location
|
||||||
|
@ -49,7 +49,7 @@ export default function moduleForJob(
|
||||||
assert.equal(document.title, `Job ${job.name} - Nomad`);
|
assert.equal(document.title, `Job ${job.name} - Nomad`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the subnav links to overview', async function(assert) {
|
test('the subnav links to overview', async function (assert) {
|
||||||
await JobDetail.tabFor('overview').visit();
|
await JobDetail.tabFor('overview').visit();
|
||||||
|
|
||||||
const expectedURL = new URL(
|
const expectedURL = new URL(
|
||||||
|
@ -62,7 +62,7 @@ export default function moduleForJob(
|
||||||
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the subnav links to definition', async function(assert) {
|
test('the subnav links to definition', async function (assert) {
|
||||||
await JobDetail.tabFor('definition').visit();
|
await JobDetail.tabFor('definition').visit();
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentURL(),
|
currentURL(),
|
||||||
|
@ -73,7 +73,7 @@ export default function moduleForJob(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the subnav links to versions', async function(assert) {
|
test('the subnav links to versions', async function (assert) {
|
||||||
await JobDetail.tabFor('versions').visit();
|
await JobDetail.tabFor('versions').visit();
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentURL(),
|
currentURL(),
|
||||||
|
@ -84,7 +84,7 @@ export default function moduleForJob(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the subnav links to evaluations', async function(assert) {
|
test('the subnav links to evaluations', async function (assert) {
|
||||||
await JobDetail.tabFor('evaluations').visit();
|
await JobDetail.tabFor('evaluations').visit();
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentURL(),
|
currentURL(),
|
||||||
|
@ -95,7 +95,7 @@ export default function moduleForJob(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the title buttons are dependent on job status', async function(assert) {
|
test('the title buttons are dependent on job status', async function (assert) {
|
||||||
if (job.status === 'dead') {
|
if (job.status === 'dead') {
|
||||||
assert.ok(JobDetail.start.isPresent);
|
assert.ok(JobDetail.start.isPresent);
|
||||||
assert.notOk(JobDetail.stop.isPresent);
|
assert.notOk(JobDetail.stop.isPresent);
|
||||||
|
@ -108,7 +108,7 @@ export default function moduleForJob(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (context === 'allocations') {
|
if (context === 'allocations') {
|
||||||
test('allocations for the job are shown in the overview', async function(assert) {
|
test('allocations for the job are shown in the overview', async function (assert) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
JobDetail.allocationsSummary.isPresent,
|
JobDetail.allocationsSummary.isPresent,
|
||||||
'Allocations are shown in the summary section'
|
'Allocations are shown in the summary section'
|
||||||
|
@ -119,7 +119,7 @@ export default function moduleForJob(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking in an allocation row navigates to that allocation', async function(assert) {
|
test('clicking in an allocation row navigates to that allocation', async function (assert) {
|
||||||
const allocationRow = JobDetail.allocations[0];
|
const allocationRow = JobDetail.allocations[0];
|
||||||
const allocationId = allocationRow.id;
|
const allocationId = allocationRow.id;
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ export default function moduleForJob(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking legend item navigates to a pre-filtered allocations table', async function(assert) {
|
test('clicking legend item navigates to a pre-filtered allocations table', async function (assert) {
|
||||||
const legendItem =
|
const legendItem =
|
||||||
JobDetail.allocationsSummary.legend.clickableItems[1];
|
JobDetail.allocationsSummary.legend.clickableItems[1];
|
||||||
const status = legendItem.label;
|
const status = legendItem.label;
|
||||||
|
@ -151,7 +151,7 @@ export default function moduleForJob(
|
||||||
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking in a slice takes you to a pre-filtered allocations table', async function(assert) {
|
test('clicking in a slice takes you to a pre-filtered allocations table', async function (assert) {
|
||||||
const slice = JobDetail.allocationsSummary.slices[1];
|
const slice = JobDetail.allocationsSummary.slices[1];
|
||||||
const status = slice.label;
|
const status = slice.label;
|
||||||
await slice.click();
|
await slice.click();
|
||||||
|
@ -180,7 +180,7 @@ export default function moduleForJob(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context === 'children') {
|
if (context === 'children') {
|
||||||
test('children for the job are shown in the overview', async function(assert) {
|
test('children for the job are shown in the overview', async function (assert) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
JobDetail.childrenSummary.isPresent,
|
JobDetail.childrenSummary.isPresent,
|
||||||
'Children are shown in the summary section'
|
'Children are shown in the summary section'
|
||||||
|
@ -193,7 +193,7 @@ export default function moduleForJob(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var testName in additionalTests) {
|
for (var testName in additionalTests) {
|
||||||
test(testName, async function(assert) {
|
test(testName, async function (assert) {
|
||||||
await additionalTests[testName].call(this, job, assert);
|
await additionalTests[testName].call(this, job, assert);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -208,20 +208,20 @@ export function moduleForJobWithClientStatus(
|
||||||
) {
|
) {
|
||||||
let job;
|
let job;
|
||||||
|
|
||||||
module(title, function(hooks) {
|
module(title, function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
setupMirage(hooks);
|
setupMirage(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(async function() {
|
hooks.beforeEach(async function () {
|
||||||
// Displaying the job status in client requires node:read permission.
|
// Displaying the job status in client requires node:read permission.
|
||||||
const policy = server.create('policy', {
|
const policy = server.create('policy', {
|
||||||
id: 'node-read',
|
id: 'node-read',
|
||||||
name: 'node-read',
|
name: 'node-read',
|
||||||
rulesJSON: {
|
rulesJSON: {
|
||||||
Node: {
|
Node: {
|
||||||
Policy: 'read'
|
Policy: 'read',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -232,10 +232,10 @@ export function moduleForJobWithClientStatus(
|
||||||
|
|
||||||
const clients = server.createList('node', 3, {
|
const clients = server.createList('node', 3, {
|
||||||
datacenter: 'dc1',
|
datacenter: 'dc1',
|
||||||
status: 'ready'
|
status: 'ready',
|
||||||
});
|
});
|
||||||
job = jobFactory();
|
job = jobFactory();
|
||||||
clients.forEach(c => {
|
clients.forEach((c) => {
|
||||||
server.create('allocation', { jobId: job.id, nodeId: c.id });
|
server.create('allocation', { jobId: job.id, nodeId: c.id });
|
||||||
});
|
});
|
||||||
if (!job.namespace || job.namespace === 'default') {
|
if (!job.namespace || job.namespace === 'default') {
|
||||||
|
@ -245,7 +245,7 @@ export function moduleForJobWithClientStatus(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('job status summary is collapsed when not authorized', async function(assert) {
|
test('job status summary is collapsed when not authorized', async function (assert) {
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
await Tokens.visit();
|
await Tokens.visit();
|
||||||
await Tokens.secret(clientToken.secretId).submit();
|
await Tokens.secret(clientToken.secretId).submit();
|
||||||
|
@ -262,7 +262,7 @@ export function moduleForJobWithClientStatus(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the subnav links to clients', async function(assert) {
|
test('the subnav links to clients', async function (assert) {
|
||||||
await JobDetail.tabFor('clients').visit();
|
await JobDetail.tabFor('clients').visit();
|
||||||
assert.equal(
|
assert.equal(
|
||||||
currentURL(),
|
currentURL(),
|
||||||
|
@ -273,14 +273,14 @@ export function moduleForJobWithClientStatus(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('job status summary is shown in the overview', async function(assert) {
|
test('job status summary is shown in the overview', async function (assert) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
JobDetail.jobClientStatusSummary.statusBar.isPresent,
|
JobDetail.jobClientStatusSummary.statusBar.isPresent,
|
||||||
'Summary bar is displayed in the Job Status in Client summary section'
|
'Summary bar is displayed in the Job Status in Client summary section'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking legend item navigates to a pre-filtered clients table', async function(assert) {
|
test('clicking legend item navigates to a pre-filtered clients table', async function (assert) {
|
||||||
const legendItem =
|
const legendItem =
|
||||||
JobDetail.jobClientStatusSummary.statusBar.legend.clickableItems[0];
|
JobDetail.jobClientStatusSummary.statusBar.legend.clickableItems[0];
|
||||||
const status = legendItem.label;
|
const status = legendItem.label;
|
||||||
|
@ -299,7 +299,7 @@ export function moduleForJobWithClientStatus(
|
||||||
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
assert.deepEqual(gotURL.searchParams, expectedURL.searchParams);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clicking in a slice takes you to a pre-filtered clients table', async function(assert) {
|
test('clicking in a slice takes you to a pre-filtered clients table', async function (assert) {
|
||||||
const slice = JobDetail.jobClientStatusSummary.statusBar.slices[0];
|
const slice = JobDetail.jobClientStatusSummary.statusBar.slices[0];
|
||||||
const status = slice.label;
|
const status = slice.label;
|
||||||
await slice.click();
|
await slice.click();
|
||||||
|
@ -325,7 +325,7 @@ export function moduleForJobWithClientStatus(
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var testName in additionalTests) {
|
for (var testName in additionalTests) {
|
||||||
test(testName, async function(assert) {
|
test(testName, async function (assert) {
|
||||||
await additionalTests[testName].call(this, job, assert);
|
await additionalTests[testName].call(this, job, assert);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ import Response from 'ember-cli-mirage/response';
|
||||||
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
|
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
|
||||||
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
|
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
|
||||||
|
|
||||||
module('Integration | Component | allocation row', function(hooks) {
|
module('Integration | Component | allocation row', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(function() {
|
hooks.beforeEach(function () {
|
||||||
fragmentSerializerInitializer(this.owner);
|
fragmentSerializerInitializer(this.owner);
|
||||||
this.store = this.owner.lookup('service:store');
|
this.store = this.owner.lookup('service:store');
|
||||||
this.token = this.owner.lookup('service:token');
|
this.token = this.owner.lookup('service:token');
|
||||||
|
@ -22,11 +22,11 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
hooks.afterEach(function() {
|
hooks.afterEach(function () {
|
||||||
this.server.shutdown();
|
this.server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Allocation row polls for stats, even when it errors or has an invalid response', async function(assert) {
|
test('Allocation row polls for stats, even when it errors or has an invalid response', async function (assert) {
|
||||||
const component = this;
|
const component = this;
|
||||||
|
|
||||||
let currentFrame = 0;
|
let currentFrame = 0;
|
||||||
|
@ -35,10 +35,10 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
JSON.stringify({ ResourceUsage: generateResources() }),
|
JSON.stringify({ ResourceUsage: generateResources() }),
|
||||||
null,
|
null,
|
||||||
'<Not>Valid JSON</Not>',
|
'<Not>Valid JSON</Not>',
|
||||||
JSON.stringify({ ResourceUsage: generateResources() })
|
JSON.stringify({ ResourceUsage: generateResources() }),
|
||||||
];
|
];
|
||||||
|
|
||||||
this.server.get('/client/allocation/:id/stats', function() {
|
this.server.get('/client/allocation/:id/stats', function () {
|
||||||
const response = frames[++currentFrame];
|
const response = frames[++currentFrame];
|
||||||
|
|
||||||
// Disable polling to stop the EC task in the component
|
// Disable polling to stop the EC task in the component
|
||||||
|
@ -60,7 +60,7 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
allocation,
|
allocation,
|
||||||
context: 'job',
|
context: 'job',
|
||||||
enablePolling: true
|
enablePolling: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
|
@ -80,16 +80,18 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Allocation row shows warning when it requires drivers that are unhealthy on the node it is running on', async function(assert) {
|
test('Allocation row shows warning when it requires drivers that are unhealthy on the node it is running on', async function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
|
||||||
// Driver health status require node:read permission.
|
// Driver health status require node:read permission.
|
||||||
const policy = server.create('policy', {
|
const policy = server.create('policy', {
|
||||||
id: 'node-read',
|
id: 'node-read',
|
||||||
name: 'node-read',
|
name: 'node-read',
|
||||||
rulesJSON: {
|
rulesJSON: {
|
||||||
Node: {
|
Node: {
|
||||||
Policy: 'read'
|
Policy: 'read',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const clientToken = server.create('token', { type: 'client' });
|
const clientToken = server.create('token', { type: 'client' });
|
||||||
clientToken.policyIds = [policy.id];
|
clientToken.policyIds = [policy.id];
|
||||||
|
@ -101,7 +103,7 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
|
|
||||||
const node = this.server.schema.nodes.first();
|
const node = this.server.schema.nodes.first();
|
||||||
const drivers = node.drivers;
|
const drivers = node.drivers;
|
||||||
Object.values(drivers).forEach(driver => {
|
Object.values(drivers).forEach((driver) => {
|
||||||
driver.Healthy = false;
|
driver.Healthy = false;
|
||||||
driver.Detected = true;
|
driver.Detected = true;
|
||||||
});
|
});
|
||||||
|
@ -116,7 +118,7 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
allocation,
|
allocation,
|
||||||
context: 'job'
|
context: 'job',
|
||||||
});
|
});
|
||||||
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
|
@ -132,7 +134,9 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
await componentA11yAudit(this.element, assert);
|
await componentA11yAudit(this.element, assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Allocation row shows an icon indicator when it was preempted', async function(assert) {
|
test('Allocation row shows an icon indicator when it was preempted', async function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
|
||||||
const allocId = this.server.create('allocation', 'preempted').id;
|
const allocId = this.server.create('allocation', 'preempted').id;
|
||||||
const allocation = await this.store.findRecord('allocation', allocId);
|
const allocation = await this.store.findRecord('allocation', allocId);
|
||||||
|
|
||||||
|
@ -147,14 +151,16 @@ module('Integration | Component | allocation row', function(hooks) {
|
||||||
await componentA11yAudit(this.element, assert);
|
await componentA11yAudit(this.element, assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when an allocation is not running, the utilization graphs are omitted', async function(assert) {
|
test('when an allocation is not running, the utilization graphs are omitted', async function (assert) {
|
||||||
|
assert.expect(8);
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
context: 'job',
|
context: 'job',
|
||||||
enablePolling: false
|
enablePolling: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// All non-running statuses need to be tested
|
// All non-running statuses need to be tested
|
||||||
['pending', 'complete', 'failed', 'lost'].forEach(clientStatus =>
|
['pending', 'complete', 'failed', 'lost'].forEach((clientStatus) =>
|
||||||
this.server.create('allocation', { clientStatus })
|
this.server.create('allocation', { clientStatus })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
/* eslint-disable ember-a11y-testing/a11y-audit-called */
|
/* eslint-disable ember-a11y-testing/a11y-audit-called */
|
||||||
import { setComponentTemplate } from '@ember/component';
|
import { setComponentTemplate } from '@ember/component';
|
||||||
import Component from '@glimmer/component';
|
import templateOnlyComponent from '@ember/component/template-only';
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { findAll, render } from '@ember/test-helpers';
|
import { findAll, render } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
module('Integration | Component | app breadcrumbs', function(hooks) {
|
module('Integration | Component | app breadcrumbs', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
const commonCrumbs = [
|
const commonCrumbs = [
|
||||||
{ label: 'Jobs', args: ['jobs.index'] },
|
{ label: 'Jobs', args: ['jobs.index'] },
|
||||||
{ label: 'Job', args: ['jobs.job.index'] }
|
{ label: 'Job', args: ['jobs.job.index'] },
|
||||||
];
|
];
|
||||||
|
|
||||||
test('every breadcrumb is rendered correctly', async function(assert) {
|
test('every breadcrumb is rendered correctly', async function (assert) {
|
||||||
|
assert.expect(3);
|
||||||
this.set('commonCrumbs', commonCrumbs);
|
this.set('commonCrumbs', commonCrumbs);
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<AppBreadcrumbs />
|
<AppBreadcrumbs />
|
||||||
|
@ -40,21 +41,20 @@ module('Integration | Component | app breadcrumbs', function(hooks) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when we register a crumb with a type property, a dedicated breadcrumb/<type> component renders', async function(assert) {
|
test('when we register a crumb with a type property, a dedicated breadcrumb/<type> component renders', async function (assert) {
|
||||||
const crumbs = [
|
const crumbs = [
|
||||||
{ label: 'Jobs', args: ['jobs.index'] },
|
{ label: 'Jobs', args: ['jobs.index'] },
|
||||||
{ type: 'special', label: 'Job', args: ['jobs.job.index'] }
|
{ type: 'special', label: 'Job', args: ['jobs.job.index'] },
|
||||||
];
|
];
|
||||||
this.set('crumbs', crumbs);
|
this.set('crumbs', crumbs);
|
||||||
|
|
||||||
class MockComponent extends Component {}
|
|
||||||
this.owner.register(
|
this.owner.register(
|
||||||
'component:breadcrumbs/special',
|
'component:breadcrumbs/special',
|
||||||
setComponentTemplate(
|
setComponentTemplate(
|
||||||
hbs`
|
hbs`
|
||||||
<div data-test-breadcrumb-special>Test</div>
|
<div data-test-breadcrumb-special>Test</div>
|
||||||
`,
|
`,
|
||||||
MockComponent
|
templateOnlyComponent()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { click, findAll, render } from '@ember/test-helpers';
|
import { click, findAll, render } from '@ember/test-helpers';
|
||||||
import { hbs } from 'ember-cli-htmlbars';
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
|
|
||||||
module('Integration | Component | breadcrumbs', function(hooks) {
|
module('Integration | Component | breadcrumbs', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test('it declaratively renders a list of registered crumbs', async function(assert) {
|
test('it declaratively renders a list of registered crumbs', async function (assert) {
|
||||||
this.set('isRegistered', false);
|
this.set('isRegistered', false);
|
||||||
this.set('toggleCrumb', () => this.set('isRegistered', !this.isRegistered));
|
this.set('toggleCrumb', () => this.set('isRegistered', !this.isRegistered));
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
|
@ -25,8 +25,12 @@ module('Integration | Component | breadcrumbs', function(hooks) {
|
||||||
{{/if}}
|
{{/if}}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.dom('[data-test-crumb]').exists({ count: 1 }, 'We register one crumb');
|
assert
|
||||||
assert.dom('[data-test-crumb]').hasText('Zoey', 'The first registered crumb is Zoey');
|
.dom('[data-test-crumb]')
|
||||||
|
.exists({ count: 1 }, 'We register one crumb');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-crumb]')
|
||||||
|
.hasText('Zoey', 'The first registered crumb is Zoey');
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
const crumbs = await findAll('[data-test-crumb]');
|
const crumbs = await findAll('[data-test-crumb]');
|
||||||
|
@ -36,19 +40,30 @@ module('Integration | Component | breadcrumbs', function(hooks) {
|
||||||
.exists({ count: 2 }, 'The second crumb registered successfully');
|
.exists({ count: 2 }, 'The second crumb registered successfully');
|
||||||
assert
|
assert
|
||||||
.dom(crumbs[0])
|
.dom(crumbs[0])
|
||||||
.hasText('Zoey', 'Breadcrumbs maintain the order in which they are declared');
|
.hasText(
|
||||||
|
'Zoey',
|
||||||
|
'Breadcrumbs maintain the order in which they are declared'
|
||||||
|
);
|
||||||
assert
|
assert
|
||||||
.dom(crumbs[1])
|
.dom(crumbs[1])
|
||||||
.hasText('Tomster', 'Breadcrumbs maintain the order in which they are declared');
|
.hasText(
|
||||||
|
'Tomster',
|
||||||
|
'Breadcrumbs maintain the order in which they are declared'
|
||||||
|
);
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
assert.dom('[data-test-crumb]').exists({ count: 1 }, 'We deregister one crumb');
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-crumb]')
|
.dom('[data-test-crumb]')
|
||||||
.hasText('Zoey', 'Zoey remains in the template after Tomster deregisters');
|
.exists({ count: 1 }, 'We deregister one crumb');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-crumb]')
|
||||||
|
.hasText(
|
||||||
|
'Zoey',
|
||||||
|
'Zoey remains in the template after Tomster deregisters'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it can register complex crumb objects', async function(assert) {
|
test('it can register complex crumb objects', async function (assert) {
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<Breadcrumbs as |bb|>
|
<Breadcrumbs as |bb|>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -62,6 +77,9 @@ module('Integration | Component | breadcrumbs', function(hooks) {
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-crumb]')
|
.dom('[data-test-crumb]')
|
||||||
.hasText('Tomster', 'We can access the registered breadcrumbs in the template');
|
.hasText(
|
||||||
|
'Tomster',
|
||||||
|
'We can access the registered breadcrumbs in the template'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,11 @@ import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { render, click, waitFor } from '@ember/test-helpers';
|
import { render, click, waitFor } from '@ember/test-helpers';
|
||||||
import { hbs } from 'ember-cli-htmlbars';
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
|
|
||||||
module('Integration | Component | trigger', function(hooks) {
|
module('Integration | Component | trigger', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
module('Synchronous Interactions', function() {
|
module('Synchronous Interactions', function () {
|
||||||
test('it can trigger a synchronous action', async function(assert) {
|
test('it can trigger a synchronous action', async function (assert) {
|
||||||
this.set('name', 'Tomster');
|
this.set('name', 'Tomster');
|
||||||
this.set('changeName', () => this.set('name', 'Zoey'));
|
this.set('changeName', () => this.set('name', 'Zoey'));
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
|
@ -17,16 +17,21 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
<button data-test-button type="button" {{on "click" trigger.fns.do}}>Change my name.</button>
|
<button data-test-button type="button" {{on "click" trigger.fns.do}}>Change my name.</button>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
`);
|
`);
|
||||||
assert.dom('[data-test-name]').hasText('Tomster', 'Initial state renders correctly.');
|
assert
|
||||||
|
.dom('[data-test-name]')
|
||||||
|
.hasText('Tomster', 'Initial state renders correctly.');
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-name]')
|
.dom('[data-test-name]')
|
||||||
.hasText('Zoey', 'The name property changes when the button is clicked');
|
.hasText(
|
||||||
|
'Zoey',
|
||||||
|
'The name property changes when the button is clicked'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it sets the result of the action', async function(assert) {
|
test('it sets the result of the action', async function (assert) {
|
||||||
this.set('tomster', () => 'Tomster');
|
this.set('tomster', () => 'Tomster');
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<Trigger @do={{this.tomster}} as |trigger|>
|
<Trigger @do={{this.tomster}} as |trigger|>
|
||||||
|
@ -38,22 +43,27 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
`);
|
`);
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-name]')
|
.dom('[data-test-name]')
|
||||||
.doesNotExist('Initial state does not render because there is no result yet.');
|
.doesNotExist(
|
||||||
|
'Initial state does not render because there is no result yet.'
|
||||||
|
);
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-name]')
|
.dom('[data-test-name]')
|
||||||
.hasText('Tomster', 'The result state updates after the triggered action');
|
.hasText(
|
||||||
|
'Tomster',
|
||||||
|
'The result state updates after the triggered action'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Asynchronous Interactions', function() {
|
module('Asynchronous Interactions', function () {
|
||||||
test('it can trigger an asynchronous action', async function(assert) {
|
test('it can trigger an asynchronous action', async function (assert) {
|
||||||
this.set(
|
this.set(
|
||||||
'onTrigger',
|
'onTrigger',
|
||||||
() =>
|
() =>
|
||||||
new Promise(resolve => {
|
new Promise((resolve) => {
|
||||||
this.set('resolve', resolve);
|
this.set('resolve', resolve);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -72,15 +82,21 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.doesNotExist('The div does not render until after the action dispatches successfully');
|
.doesNotExist(
|
||||||
|
'The div does not render until after the action dispatches successfully'
|
||||||
|
);
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div-loading]')
|
.dom('[data-test-div-loading]')
|
||||||
.exists('Loading state is displayed when the action hasnt resolved yet');
|
.exists(
|
||||||
|
'Loading state is displayed when the action hasnt resolved yet'
|
||||||
|
);
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.doesNotExist('Success message does not display until after promise resolves');
|
.doesNotExist(
|
||||||
|
'Success message does not display until after promise resolves'
|
||||||
|
);
|
||||||
|
|
||||||
this.resolve();
|
this.resolve();
|
||||||
await waitFor('[data-test-div]');
|
await waitFor('[data-test-div]');
|
||||||
|
@ -91,15 +107,21 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
);
|
);
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.exists('Action has dispatched successfully after the promise resolves');
|
.exists(
|
||||||
|
'Action has dispatched successfully after the promise resolves'
|
||||||
|
);
|
||||||
|
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.doesNotExist('Aftering clicking the button, again, the state is reset');
|
.doesNotExist(
|
||||||
|
'Aftering clicking the button, again, the state is reset'
|
||||||
|
);
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div-loading]')
|
.dom('[data-test-div-loading]')
|
||||||
.exists('After clicking the button, again, we are back in the loading state');
|
.exists(
|
||||||
|
'After clicking the button, again, we are back in the loading state'
|
||||||
|
);
|
||||||
|
|
||||||
this.resolve();
|
this.resolve();
|
||||||
await waitFor('[data-test-div]');
|
await waitFor('[data-test-div]');
|
||||||
|
@ -111,11 +133,11 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the success state', async function(assert) {
|
test('it handles the success state', async function (assert) {
|
||||||
this.set(
|
this.set(
|
||||||
'onTrigger',
|
'onTrigger',
|
||||||
() =>
|
() =>
|
||||||
new Promise(resolve => {
|
new Promise((resolve) => {
|
||||||
this.set('resolve', resolve);
|
this.set('resolve', resolve);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -132,14 +154,16 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.doesNotExist('No text should appear until after the onSuccess callback is fired');
|
.doesNotExist(
|
||||||
|
'No text should appear until after the onSuccess callback is fired'
|
||||||
|
);
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
this.resolve();
|
this.resolve();
|
||||||
await waitFor('[data-test-div]');
|
await waitFor('[data-test-div]');
|
||||||
assert.verifySteps(['On success happened']);
|
assert.verifySteps(['On success happened']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the error state', async function(assert) {
|
test('it handles the error state', async function (assert) {
|
||||||
this.set(
|
this.set(
|
||||||
'onTrigger',
|
'onTrigger',
|
||||||
() =>
|
() =>
|
||||||
|
@ -166,11 +190,15 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button]');
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div-loading]')
|
.dom('[data-test-div-loading]')
|
||||||
.exists('Loading state is displayed when the action hasnt resolved yet');
|
.exists(
|
||||||
|
'Loading state is displayed when the action hasnt resolved yet'
|
||||||
|
);
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div]')
|
.dom('[data-test-div]')
|
||||||
.doesNotExist('No text should appear until after the onError callback is fired');
|
.doesNotExist(
|
||||||
|
'No text should appear until after the onError callback is fired'
|
||||||
|
);
|
||||||
|
|
||||||
this.reject();
|
this.reject();
|
||||||
await waitFor('[data-test-span]');
|
await waitFor('[data-test-span]');
|
||||||
|
@ -180,7 +208,9 @@ module('Integration | Component | trigger', function(hooks) {
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('[data-test-div-loading]')
|
.dom('[data-test-div-loading]')
|
||||||
.exists('The previous error state was cleared and we show loading, again.');
|
.exists(
|
||||||
|
'The previous error state was cleared and we show loading, again.'
|
||||||
|
);
|
||||||
|
|
||||||
assert.dom('[data-test-div]').doesNotExist('The error state is cleared');
|
assert.dom('[data-test-div]').doesNotExist('The error state is cleared');
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
isPresent,
|
isPresent,
|
||||||
property,
|
property,
|
||||||
text,
|
text,
|
||||||
visitable
|
visitable,
|
||||||
} from 'ember-cli-page-object';
|
} from 'ember-cli-page-object';
|
||||||
|
|
||||||
import allocations from 'nomad-ui/tests/pages/components/allocations';
|
import allocations from 'nomad-ui/tests/pages/components/allocations';
|
||||||
|
@ -22,7 +22,7 @@ export default create({
|
||||||
|
|
||||||
tabs: collection('[data-test-tab]', {
|
tabs: collection('[data-test-tab]', {
|
||||||
id: attribute('data-test-tab'),
|
id: attribute('data-test-tab'),
|
||||||
visit: clickable('a')
|
visit: clickable('a'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
tabFor(id) {
|
tabFor(id) {
|
||||||
|
@ -44,22 +44,22 @@ export default create({
|
||||||
scope: '[data-test-exec-button]',
|
scope: '[data-test-exec-button]',
|
||||||
isDisabled: property('disabled'),
|
isDisabled: property('disabled'),
|
||||||
hasTooltip: hasClass('tooltip'),
|
hasTooltip: hasClass('tooltip'),
|
||||||
tooltipText: attribute('aria-label')
|
tooltipText: attribute('aria-label'),
|
||||||
},
|
},
|
||||||
|
|
||||||
incrementButton: {
|
incrementButton: {
|
||||||
scope: '[data-test-scale-controls-increment]',
|
scope: '[data-test-scale-controls-increment]',
|
||||||
isDisabled: property('disabled')
|
isDisabled: property('disabled'),
|
||||||
},
|
},
|
||||||
|
|
||||||
dispatchButton: {
|
dispatchButton: {
|
||||||
scope: '[data-test-dispatch-button]',
|
scope: '[data-test-dispatch-button]',
|
||||||
isDisabled: property('disabled')
|
isDisabled: property('disabled'),
|
||||||
},
|
},
|
||||||
|
|
||||||
stats: collection('[data-test-job-stat]', {
|
stats: collection('[data-test-job-stat]', {
|
||||||
id: attribute('data-test-job-stat'),
|
id: attribute('data-test-job-stat'),
|
||||||
text: text()
|
text: text(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
statFor(id) {
|
statFor(id) {
|
||||||
|
@ -68,7 +68,7 @@ export default create({
|
||||||
|
|
||||||
packStats: collection('[data-test-pack-stat]', {
|
packStats: collection('[data-test-pack-stat]', {
|
||||||
id: attribute('data-test-pack-stat'),
|
id: attribute('data-test-pack-stat'),
|
||||||
text: text()
|
text: text(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
packStatFor(id) {
|
packStatFor(id) {
|
||||||
|
@ -82,8 +82,8 @@ export default create({
|
||||||
scope: '[data-test-accordion-head] [data-test-accordion-toggle]',
|
scope: '[data-test-accordion-head] [data-test-accordion-toggle]',
|
||||||
click: clickable(),
|
click: clickable(),
|
||||||
isDisabled: attribute('disabled'),
|
isDisabled: attribute('disabled'),
|
||||||
tooltip: attribute('aria-label')
|
tooltip: attribute('aria-label'),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
childrenSummary: jobClientStatusBar(
|
childrenSummary: jobClientStatusBar(
|
||||||
'[data-test-job-summary] [data-test-children-status-bar]'
|
'[data-test-job-summary] [data-test-children-status-bar]'
|
||||||
|
@ -98,7 +98,7 @@ export default create({
|
||||||
jobsHeader: {
|
jobsHeader: {
|
||||||
scope: '[data-test-jobs-header]',
|
scope: '[data-test-jobs-header]',
|
||||||
hasSubmitTime: isPresent('[data-test-jobs-submit-time-header]'),
|
hasSubmitTime: isPresent('[data-test-jobs-submit-time-header]'),
|
||||||
hasNamespace: isPresent('[data-test-jobs-namespace-header]')
|
hasNamespace: isPresent('[data-test-jobs-namespace-header]'),
|
||||||
},
|
},
|
||||||
|
|
||||||
jobs: collection('[data-test-job-row]', {
|
jobs: collection('[data-test-job-row]', {
|
||||||
|
@ -113,17 +113,17 @@ export default create({
|
||||||
taskGroups: text('[data-test-job-task-groups]'),
|
taskGroups: text('[data-test-job-task-groups]'),
|
||||||
|
|
||||||
clickRow: clickable(),
|
clickRow: clickable(),
|
||||||
clickName: clickable('[data-test-job-name] a')
|
clickName: clickable('[data-test-job-name] a'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
error: {
|
error: {
|
||||||
isPresent: isPresent('[data-test-error]'),
|
isPresent: isPresent('[data-test-error]'),
|
||||||
title: text('[data-test-error-title]'),
|
title: text('[data-test-error-title]'),
|
||||||
message: text('[data-test-error-message]'),
|
message: text('[data-test-error-message]'),
|
||||||
seekHelp: clickable('[data-test-error-message] a')
|
seekHelp: clickable('[data-test-error-message] a'),
|
||||||
},
|
},
|
||||||
|
|
||||||
recentAllocationsEmptyState: {
|
recentAllocationsEmptyState: {
|
||||||
headline: text('[data-test-empty-recent-allocations-headline]')
|
headline: text('[data-test-empty-recent-allocations-headline]'),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,11 @@ import { setupTest } from 'ember-qunit';
|
||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
import setupAbility from 'nomad-ui/tests/helpers/setup-ability';
|
import setupAbility from 'nomad-ui/tests/helpers/setup-ability';
|
||||||
|
|
||||||
module('Unit | Ability | client', function(hooks) {
|
module('Unit | Ability | client', function (hooks) {
|
||||||
setupTest(hooks);
|
setupTest(hooks);
|
||||||
setupAbility('client')(hooks);
|
setupAbility('client')(hooks);
|
||||||
|
|
||||||
test('it permits client read and write when ACLs are disabled', function(assert) {
|
test('it permits client read and write when ACLs are disabled', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: false,
|
aclEnabled: false,
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@ module('Unit | Ability | client', function(hooks) {
|
||||||
assert.ok(this.ability.canWrite);
|
assert.ok(this.ability.canWrite);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it permits client read and write for management tokens', function(assert) {
|
test('it permits client read and write for management tokens', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: true,
|
aclEnabled: true,
|
||||||
selfToken: { type: 'management' },
|
selfToken: { type: 'management' },
|
||||||
|
@ -29,7 +29,7 @@ module('Unit | Ability | client', function(hooks) {
|
||||||
assert.ok(this.ability.canWrite);
|
assert.ok(this.ability.canWrite);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it permits client read and write for tokens with a policy that has node-write', function(assert) {
|
test('it permits client read and write for tokens with a policy that has node-write', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: true,
|
aclEnabled: true,
|
||||||
selfToken: { type: 'client' },
|
selfToken: { type: 'client' },
|
||||||
|
@ -49,7 +49,7 @@ module('Unit | Ability | client', function(hooks) {
|
||||||
assert.ok(this.ability.canWrite);
|
assert.ok(this.ability.canWrite);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it permits client read and write for tokens with a policy that allows write and another policy that disallows it', function(assert) {
|
test('it permits client read and write for tokens with a policy that allows write and another policy that disallows it', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: true,
|
aclEnabled: true,
|
||||||
selfToken: { type: 'client' },
|
selfToken: { type: 'client' },
|
||||||
|
@ -76,7 +76,7 @@ module('Unit | Ability | client', function(hooks) {
|
||||||
assert.ok(this.ability.canWrite);
|
assert.ok(this.ability.canWrite);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it permits client read and blocks client write for tokens with a policy that does not allow node-write', function(assert) {
|
test('it permits client read and blocks client write for tokens with a policy that does not allow node-write', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: true,
|
aclEnabled: true,
|
||||||
selfToken: { type: 'client' },
|
selfToken: { type: 'client' },
|
||||||
|
@ -96,7 +96,7 @@ module('Unit | Ability | client', function(hooks) {
|
||||||
assert.notOk(this.ability.canWrite);
|
assert.notOk(this.ability.canWrite);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it blocks client read and write for tokens without a node policy', function(assert) {
|
test('it blocks client read and write for tokens without a node policy', function (assert) {
|
||||||
const mockToken = Service.extend({
|
const mockToken = Service.extend({
|
||||||
aclEnabled: true,
|
aclEnabled: true,
|
||||||
selfToken: { type: 'client' },
|
selfToken: { type: 'client' },
|
||||||
|
|
|
@ -51,8 +51,8 @@ class AllocationMock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module('Unit | Util | JobClientStatus', function() {
|
module('Unit | Util | JobClientStatus', function () {
|
||||||
test('it handles the case where all nodes are running', async function(assert) {
|
test('it handles the case where all nodes are running', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -84,7 +84,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the degraded case where a node has a failing allocation', async function(assert) {
|
test('it handles the degraded case where a node has a failing allocation', async function (assert) {
|
||||||
const node = new NodeMock('node-2', 'dc1');
|
const node = new NodeMock('node-2', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -120,7 +120,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the case where a node has all lost allocations', async function(assert) {
|
test('it handles the case where a node has all lost allocations', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -156,7 +156,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the case where a node has all failed allocations', async function(assert) {
|
test('it handles the case where a node has all failed allocations', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -192,7 +192,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the degraded case where the expected number of allocations doesnt match the actual number of allocations', async function(assert) {
|
test('it handles the degraded case where the expected number of allocations doesnt match the actual number of allocations', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -228,7 +228,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the not scheduled case where a node has no allocations', async function(assert) {
|
test('it handles the not scheduled case where a node has no allocations', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -260,7 +260,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it handles the queued case where the job is pending', async function(assert) {
|
test('it handles the queued case where the job is pending', async function (assert) {
|
||||||
const node = new NodeMock('node-1', 'dc1');
|
const node = new NodeMock('node-1', 'dc1');
|
||||||
const nodes = [node];
|
const nodes = [node];
|
||||||
const job = {
|
const job = {
|
||||||
|
@ -296,7 +296,7 @@ module('Unit | Util | JobClientStatus', function() {
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it filters nodes by the datacenter of the job', async function(assert) {
|
test('it filters nodes by the datacenter of the job', async function (assert) {
|
||||||
const node1 = new NodeMock('node-1', 'dc1');
|
const node1 = new NodeMock('node-1', 'dc1');
|
||||||
const node2 = new NodeMock('node-2', 'dc2');
|
const node2 = new NodeMock('node-2', 'dc2');
|
||||||
const nodes = [node1, node2];
|
const nodes = [node1, node2];
|
||||||
|
|
Loading…
Reference in a new issue