Merge pull request #4481 from hashicorp/f-ui-move-evals-to-tab
UI: Move evals to a tab
This commit is contained in:
commit
8b9bc7834c
|
@ -1,12 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default Component.extend({
|
||||
job: null,
|
||||
|
||||
classNames: ['boxed-section'],
|
||||
|
||||
sortedEvaluations: computed('job.evaluations.@each.modifyIndex', function() {
|
||||
return (this.get('job.evaluations') || []).sortBy('modifyIndex').reverse();
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { alias } from '@ember/object/computed';
|
||||
import Controller, { inject as controller } from '@ember/controller';
|
||||
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
|
||||
import Sortable from 'nomad-ui/mixins/sortable';
|
||||
|
||||
export default Controller.extend(WithNamespaceResetting, Sortable, {
|
||||
jobController: controller('jobs.job'),
|
||||
|
||||
queryParams: {
|
||||
sortProperty: 'sort',
|
||||
sortDescending: 'desc',
|
||||
},
|
||||
|
||||
sortProperty: 'modifyIndex',
|
||||
sortDescending: true,
|
||||
|
||||
job: alias('model'),
|
||||
evaluations: alias('model.evaluations'),
|
||||
|
||||
breadcrumbs: alias('jobController.breadcrumbs'),
|
||||
|
||||
listToSort: alias('evaluations'),
|
||||
sortedEvaluations: alias('listSorted'),
|
||||
});
|
|
@ -13,6 +13,7 @@ Router.map(function() {
|
|||
this.route('definition');
|
||||
this.route('versions');
|
||||
this.route('deployments');
|
||||
this.route('evaluations');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import Route from '@ember/routing/route';
|
||||
import { collect } from '@ember/object/computed';
|
||||
import { watchRelationship } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
|
||||
export default Route.extend(WithWatchers, {
|
||||
model() {
|
||||
const job = this.modelFor('jobs.job');
|
||||
return job.get('evaluations').then(() => job);
|
||||
},
|
||||
|
||||
startWatchers(controller, model) {
|
||||
controller.set('watchEvaluations', this.get('watchEvaluations').perform(model));
|
||||
},
|
||||
|
||||
watchEvaluations: watchRelationship('evaluations'),
|
||||
|
||||
watchers: collect('watchEvaluations'),
|
||||
});
|
|
@ -29,6 +29,4 @@
|
|||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
gotoTaskGroup=gotoTaskGroup}}
|
||||
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
{{/job-page/parts/body}}
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
sortDescending=sortDescending
|
||||
gotoTaskGroup=gotoTaskGroup}}
|
||||
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">Payload</div>
|
||||
<div class="boxed-section-body is-dark">
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<div class="boxed-section-head">
|
||||
Evaluations
|
||||
</div>
|
||||
<div class="boxed-section-body {{if sortedEvaluations.length "is-full-bleed"}} evaluations">
|
||||
{{#if sortedEvaluations.length}}
|
||||
{{#list-table source=sortedEvaluations as |t|}}
|
||||
{{#t.head}}
|
||||
<th>ID</th>
|
||||
<th>Priority</th>
|
||||
<th>Triggered By</th>
|
||||
<th>Status</th>
|
||||
<th>Placement Failures</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-evaluation="{{row.model.shortId}}">
|
||||
<td data-test-id>{{row.model.shortId}}</td>
|
||||
<td data-test-priority>{{row.model.priority}}</td>
|
||||
<td data-test-triggered-by>{{row.model.triggeredBy}}</td>
|
||||
<td data-test-status>{{row.model.status}}</td>
|
||||
<td data-test-blocked>
|
||||
{{#if (eq row.model.status "blocked")}}
|
||||
N/A - In Progress
|
||||
{{else if row.model.hasPlacementFailures}}
|
||||
True
|
||||
{{else}}
|
||||
False
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
{{else}}
|
||||
<div data-test-empty-evaluations-list class="empty-message">
|
||||
<h3 data-test-empty-evaluations-list-headline class="empty-message-headline">No Evaluations</h3>
|
||||
<p class="empty-message-body">This is most likely due to garbage collection.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -35,6 +35,4 @@
|
|||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
gotoTaskGroup=gotoTaskGroup}}
|
||||
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
{{/job-page/parts/body}}
|
||||
|
|
|
@ -31,6 +31,4 @@
|
|||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
gotoTaskGroup=gotoTaskGroup}}
|
||||
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
{{/job-page/parts/body}}
|
||||
|
|
|
@ -29,6 +29,4 @@
|
|||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
gotoTaskGroup=gotoTaskGroup}}
|
||||
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
{{/job-page/parts/body}}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#link-to (query-params sortProperty=prop sortDescending=shouldSortDescending)}}
|
||||
{{#link-to
|
||||
(query-params sortProperty=prop sortDescending=shouldSortDescending)
|
||||
data-test-sort-by=prop}}
|
||||
{{yield}}
|
||||
{{/link-to}}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{{#global-header class="page-header"}}
|
||||
{{#each breadcrumbs as |breadcrumb index|}}
|
||||
<li class="{{if (eq (inc index) breadcrumbs.length) "is-active"}}">
|
||||
{{#link-to params=breadcrumb.args}}{{breadcrumb.label}}{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Evaluations
|
||||
</div>
|
||||
<div class="boxed-section-body {{if sortedEvaluations.length "is-full-bleed"}}">
|
||||
{{#if sortedEvaluations.length}}
|
||||
{{#list-table
|
||||
source=sortedEvaluations
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending as |t|}}
|
||||
{{#t.head}}
|
||||
<th>ID</th>
|
||||
{{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
|
||||
{{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}}
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-evaluation="{{row.model.shortId}}">
|
||||
<td data-test-id>{{row.model.shortId}}</td>
|
||||
<td data-test-priority>{{row.model.priority}}</td>
|
||||
<td data-test-triggered-by>{{row.model.triggeredBy}}</td>
|
||||
<td data-test-status>{{row.model.status}}</td>
|
||||
<td data-test-blocked>
|
||||
{{#if (eq row.model.status "blocked")}}
|
||||
N/A - In Progress
|
||||
{{else if row.model.hasPlacementFailures}}
|
||||
True
|
||||
{{else}}
|
||||
False
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
{{else}}
|
||||
<div data-test-empty-evaluations-list class="empty-message">
|
||||
<h3 data-test-empty-evaluations-list-headline class="empty-message-headline">No Evaluations</h3>
|
||||
<p class="empty-message-body">This is most likely due to garbage collection.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
|
|
@ -6,5 +6,6 @@
|
|||
{{#if job.supportsDeployments}}
|
||||
<li data-test-tab="deployments">{{#link-to "jobs.job.deployments" job activeClass="is-active"}}Deployments{{/link-to}}</li>
|
||||
{{/if}}
|
||||
<li data-test-tab="evaluations">{{#link-to "jobs.job.evaluations" job activeClass="is-active"}}Evaluations{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import { test } from 'qunit';
|
||||
import { findAll, click } from 'ember-native-dom-helpers';
|
||||
import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
|
||||
|
||||
let job;
|
||||
let evaluations;
|
||||
|
||||
moduleForAcceptance('Acceptance | job evaluations', {
|
||||
beforeEach() {
|
||||
job = server.create('job', { noFailedPlacements: true, createAllocations: false });
|
||||
evaluations = server.db.evaluations.where({ jobId: job.id });
|
||||
|
||||
visit(`/jobs/${job.id}/evaluations`);
|
||||
},
|
||||
});
|
||||
|
||||
test('lists all evaluations for the job', function(assert) {
|
||||
const evaluationRows = findAll('[data-test-evaluation]');
|
||||
assert.equal(evaluationRows.length, evaluations.length, 'All evaluations are listed');
|
||||
|
||||
evaluations
|
||||
.sortBy('modifyIndex')
|
||||
.reverse()
|
||||
.forEach((evaluation, index) => {
|
||||
const shortId = evaluation.id.split('-')[0];
|
||||
assert.equal(
|
||||
evaluationRows[index].querySelector('[data-test-id]').textContent.trim(),
|
||||
shortId,
|
||||
`Evaluation ${index} is ${shortId}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('evaluations table is sortable', function(assert) {
|
||||
click('[data-test-sort-by="priority"]');
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
`/jobs/${job.id}/evaluations?sort=priority`,
|
||||
'the URL persists the sort parameter'
|
||||
);
|
||||
const evaluationRows = findAll('[data-test-evaluation]');
|
||||
evaluations
|
||||
.sortBy('priority')
|
||||
.reverse()
|
||||
.forEach((evaluation, index) => {
|
||||
const shortId = evaluation.id.split('-')[0];
|
||||
assert.equal(
|
||||
evaluationRows[index].querySelector('[data-test-id]').textContent.trim(),
|
||||
shortId,
|
||||
`Evaluation ${index} is ${shortId} with priority ${evaluation.priority}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,65 +0,0 @@
|
|||
import { run } from '@ember/runloop';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { test, moduleForComponent } from 'ember-qunit';
|
||||
import { findAll } from 'ember-native-dom-helpers';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
|
||||
|
||||
moduleForComponent(
|
||||
'job-page/parts/evaluations',
|
||||
'Integration | Component | job-page/parts/evaluations',
|
||||
{
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
window.localStorage.clear();
|
||||
this.store = getOwner(this).lookup('service:store');
|
||||
this.server = startMirage();
|
||||
this.server.create('namespace');
|
||||
},
|
||||
afterEach() {
|
||||
this.server.shutdown();
|
||||
window.localStorage.clear();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
test('lists all evaluations for the job', function(assert) {
|
||||
let job;
|
||||
|
||||
this.server.create('job', { noFailedPlacements: true, createAllocations: false });
|
||||
this.store.findAll('job');
|
||||
|
||||
return wait().then(() => {
|
||||
run(() => {
|
||||
job = this.store.peekAll('job').get('firstObject');
|
||||
});
|
||||
|
||||
this.setProperties({ job });
|
||||
|
||||
this.render(hbs`
|
||||
{{job-page/parts/evaluations job=job}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
const evaluationRows = findAll('[data-test-evaluation]');
|
||||
assert.equal(
|
||||
evaluationRows.length,
|
||||
job.get('evaluations.length'),
|
||||
'All evaluations are listed'
|
||||
);
|
||||
|
||||
job
|
||||
.get('evaluations')
|
||||
.sortBy('modifyIndex')
|
||||
.reverse()
|
||||
.forEach((evaluation, index) => {
|
||||
assert.equal(
|
||||
evaluationRows[index].querySelector('[data-test-id]').textContent.trim(),
|
||||
evaluation.get('shortId'),
|
||||
`Evaluation ${index} is ${evaluation.get('shortId')}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue