feat: add pagination and filtering to evaluations view
This commit is contained in:
parent
1f80081c9d
commit
e5b154e295
|
@ -1,9 +1,12 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class EvaluationsController extends Controller {
|
||||
queryParams = ['nextToken', 'pageSize'];
|
||||
@service userSettings;
|
||||
|
||||
queryParams = ['nextToken', 'pageSize', 'status'];
|
||||
|
||||
get shouldDisableNext() {
|
||||
return !this.model.meta?.nextToken;
|
||||
|
@ -13,9 +16,21 @@ export default class EvaluationsController extends Controller {
|
|||
return !this.previousTokens.length;
|
||||
}
|
||||
|
||||
@tracked pageSize = 25;
|
||||
get optionsEvaluationsStatus() {
|
||||
return [
|
||||
{ key: null, label: 'All' },
|
||||
{ key: 'blocked', label: 'Blocked' },
|
||||
{ key: 'pending', label: 'Pending' },
|
||||
{ key: 'complete', label: 'Complete' },
|
||||
{ key: 'failed', label: 'Failed' },
|
||||
{ key: 'canceled', label: 'Canceled' },
|
||||
];
|
||||
}
|
||||
|
||||
@tracked pageSize = this.userSettings.pageSize;
|
||||
@tracked nextToken = null;
|
||||
@tracked previousTokens = [];
|
||||
@tracked status = null;
|
||||
|
||||
@action
|
||||
onChange(newPageSize) {
|
||||
|
@ -29,14 +44,26 @@ export default class EvaluationsController extends Controller {
|
|||
}
|
||||
|
||||
@action
|
||||
onPrev(lastToken) {
|
||||
this.previousTokens.pop();
|
||||
onPrev() {
|
||||
const lastToken = this.previousTokens.pop();
|
||||
this.previousTokens = [...this.previousTokens];
|
||||
this.nextToken = lastToken;
|
||||
}
|
||||
|
||||
@action
|
||||
refresh() {
|
||||
this._resetTokens();
|
||||
this.status = null;
|
||||
this.pageSize = this.userSettings.pageSize;
|
||||
}
|
||||
|
||||
@action
|
||||
setStatus(selection) {
|
||||
this._resetTokens();
|
||||
this.status = selection;
|
||||
}
|
||||
|
||||
_resetTokens() {
|
||||
this.nextToken = null;
|
||||
this.previousTokens = [];
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import shortUUIDProperty from '../utils/properties/short-uuid';
|
|||
|
||||
export default class Evaluation extends Model {
|
||||
@shortUUIDProperty('id') shortId;
|
||||
@shortUUIDProperty('nodeId') shortNodeId;
|
||||
@attr('number') priority;
|
||||
@attr('string') type;
|
||||
@attr('string') triggeredBy;
|
||||
|
@ -18,6 +19,7 @@ export default class Evaluation extends Model {
|
|||
@equal('status', 'blocked') isBlocked;
|
||||
|
||||
@belongsTo('job') job;
|
||||
@belongsTo('node') node;
|
||||
|
||||
@attr('number') modifyIndex;
|
||||
@attr('date') modifyTime;
|
||||
|
@ -26,4 +28,18 @@ export default class Evaluation extends Model {
|
|||
@attr('date') createTime;
|
||||
|
||||
@attr('date') waitUntil;
|
||||
@attr('string') namespace;
|
||||
@attr('string') plainJobId;
|
||||
|
||||
get hasJob() {
|
||||
return !!this.plainJobId;
|
||||
}
|
||||
|
||||
get hasNode() {
|
||||
return !!this.belongsTo('node').id();
|
||||
}
|
||||
|
||||
get nodeId() {
|
||||
return this.belongsTo('node').id();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,17 @@ export default class EvaluationsIndexRoute extends Route {
|
|||
nextToken: {
|
||||
refreshModel: true,
|
||||
},
|
||||
status: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
model({ pageSize, nextToken }) {
|
||||
model({ pageSize, status, nextToken }) {
|
||||
return this.store.query('evaluation', {
|
||||
namespace: ALL_NAMESPACE_WILDCARD,
|
||||
per_page: pageSize,
|
||||
next_token: nextToken,
|
||||
status,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<div class="field is-horizontal" data-test-page-size-select-parent>
|
||||
<span class="field-label is-small">Per page</span>
|
||||
<div class="field is-horizontal" data-test-page-size-select-parent ...attributes>
|
||||
<span class="field-label is-small">
|
||||
Per page
|
||||
</span>
|
||||
<PowerSelect
|
||||
@tagName="div"
|
||||
class="field-body"
|
||||
data-test-page-size-select
|
||||
@options={{this.pageSizeOptions}}
|
||||
@selected={{this.userSettings.pageSize}}
|
||||
@onChange={{action (queue
|
||||
(action (mut this.userSettings.pageSize))
|
||||
(action this.onChange)
|
||||
)}} as |option|>
|
||||
@onChange={{action (queue (action (mut this.userSettings.pageSize)) (action this.onChange))
|
||||
}} as |option|
|
||||
>
|
||||
{{option}}
|
||||
</PowerSelect>
|
||||
</div>
|
|
@ -1,6 +1,18 @@
|
|||
{{page-title "Evaluations"}}
|
||||
<section class="section">
|
||||
<ListTable @source={{@model}} as |t|>
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-item">
|
||||
<SingleSelectDropdown
|
||||
data-test-evaluation-status-facet
|
||||
@label="Status"
|
||||
@options={{this.optionsEvaluationsStatus}}
|
||||
@selection={{this.status}}
|
||||
@onSelect={{action this.setStatus}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{#if @model.length}}
|
||||
<ListTable data-test-eval-table @source={{@model}} as |t|>
|
||||
<t.head>
|
||||
<th>
|
||||
Evaluation ID
|
||||
|
@ -30,7 +42,24 @@
|
|||
{{row.model.shortId}}
|
||||
</td>
|
||||
<td data-test-id>
|
||||
Resource Placeholder
|
||||
{{#if row.model.hasJob}}
|
||||
<LinkTo
|
||||
data-test-evaluation-resource
|
||||
@model={{row.model.plainJobId}}
|
||||
@route="jobs.job"
|
||||
@query={{hash namespace=row.model.namespace}}
|
||||
>
|
||||
{{row.model.plainJobId}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
data-test-evaluation-resource
|
||||
@model={{row.model.nodeId}}
|
||||
@route="clients.client"
|
||||
>
|
||||
{{row.model.shortNodeId}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-priority>
|
||||
{{row.model.priority}}
|
||||
|
@ -41,7 +70,7 @@
|
|||
<td data-test-triggered-by>
|
||||
{{row.model.triggeredBy}}
|
||||
</td>
|
||||
<td data-test-status>
|
||||
<td data-test-status class="is-one-line">
|
||||
<StatusCell @status={{row.model.status}} />
|
||||
</td>
|
||||
<td data-test-blocked>
|
||||
|
@ -57,11 +86,12 @@
|
|||
</t.body>
|
||||
</ListTable>
|
||||
<div class="table-foot">
|
||||
<PageSizeSelect @onChange={{this.onChange}} />
|
||||
<button type="button" {{on "click" this.refresh}}>
|
||||
<PageSizeSelect data-test-per-page @onChange={{this.onChange}} />
|
||||
<button data-test-eval-refresh type="button" {{on "click" this.refresh}}>
|
||||
Refresh
|
||||
</button>
|
||||
<button
|
||||
data-test-eval-pagination-prev
|
||||
type="button"
|
||||
disabled={{this.shouldDisablePrev}}
|
||||
{{on "click" (fn this.onPrev this.lastToken)}}
|
||||
|
@ -69,6 +99,7 @@
|
|||
Prev
|
||||
</button>
|
||||
<button
|
||||
data-test-eval-pagination-next
|
||||
type="button"
|
||||
disabled={{this.shouldDisableNext}}
|
||||
{{on "click" (fn this.onNext @model.meta.nextToken)}}
|
||||
|
@ -76,4 +107,30 @@
|
|||
Next >
|
||||
</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message" data-test-empty-evaluations-list>
|
||||
<h3 class="empty-message-headline" data-test-empty-evalations-list-headline>
|
||||
No Matches
|
||||
</h3>
|
||||
<p class="empty-message-body">
|
||||
{{#if this.status}}
|
||||
<span data-test-no-eval-match>
|
||||
No evaluations match the status
|
||||
<strong>
|
||||
{{this.status}}
|
||||
</strong>
|
||||
</span>
|
||||
<button type="button" {{on "click" this.refresh}}>
|
||||
Show all evaluations
|
||||
</button>
|
||||
{{else}}
|
||||
<span data-test-no-eval>
|
||||
There are no evaluations
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
|
@ -261,6 +261,7 @@ export default function () {
|
|||
return this.serialize(evaluations.where({ jobId: params.id }));
|
||||
});
|
||||
|
||||
this.get('/evaluations');
|
||||
this.get('/evaluation/:id');
|
||||
|
||||
this.get('/deployment/allocations/:id', function (schema, { params }) {
|
||||
|
|
|
@ -91,7 +91,9 @@ export default Factory.extend({
|
|||
}),
|
||||
|
||||
afterCreate(evaluation, server) {
|
||||
if (!evaluation.nodeId) {
|
||||
assignJob(evaluation, server);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
import { click, currentRouteName, visit } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import { Response } from 'ember-cli-mirage';
|
||||
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
|
||||
import { selectChoose, clickTrigger } from 'ember-power-select/test-support/helpers';
|
||||
|
||||
const getStandardRes = () => [
|
||||
{
|
||||
CreateIndex: 1249,
|
||||
CreateTime: 1640181894162724000,
|
||||
DeploymentID: '12efbb28-840e-7794-b215-a7b112e40a4f',
|
||||
ID: '5fb1b8cd-00f8-fff8-de0c-197dc37f5053',
|
||||
JobID: 'cores-example',
|
||||
JobModifyIndex: 694,
|
||||
ModifyIndex: 1251,
|
||||
ModifyTime: 1640181894167194000,
|
||||
Namespace: 'ted-lasso',
|
||||
Priority: 50,
|
||||
QueuedAllocations: {
|
||||
lb: 0,
|
||||
webapp: 0,
|
||||
},
|
||||
SnapshotIndex: 1249,
|
||||
Status: 'complete',
|
||||
TriggeredBy: 'job-register',
|
||||
Type: 'service',
|
||||
},
|
||||
{
|
||||
CreateIndex: 1304,
|
||||
CreateTime: 1640183201719510000,
|
||||
DeploymentID: '878435bf-7265-62b1-7902-d45c44b23b79',
|
||||
ID: '66cb98a6-7740-d5ef-37e4-fa0f8b1de44b',
|
||||
JobID: 'cores-example',
|
||||
JobModifyIndex: 1304,
|
||||
ModifyIndex: 1306,
|
||||
ModifyTime: 1640183201721418000,
|
||||
Namespace: 'default',
|
||||
Priority: 50,
|
||||
QueuedAllocations: {
|
||||
webapp: 0,
|
||||
lb: 0,
|
||||
},
|
||||
SnapshotIndex: 1304,
|
||||
Status: 'complete',
|
||||
TriggeredBy: 'job-register',
|
||||
Type: 'service',
|
||||
},
|
||||
{
|
||||
CreateIndex: 1267,
|
||||
CreateTime: 1640182198255685000,
|
||||
DeploymentID: '12efbb28-840e-7794-b215-a7b112e40a4f',
|
||||
ID: '78009518-574d-eee6-919a-e83879175dd3',
|
||||
JobID: 'cores-example',
|
||||
JobModifyIndex: 1250,
|
||||
ModifyIndex: 1274,
|
||||
ModifyTime: 1640182228112823000,
|
||||
Namespace: 'ted-lasso',
|
||||
PreviousEval: '84f1082f-3e6e-034d-6df4-c6a321e7bd63',
|
||||
Priority: 50,
|
||||
QueuedAllocations: {
|
||||
lb: 0,
|
||||
},
|
||||
SnapshotIndex: 1272,
|
||||
Status: 'complete',
|
||||
TriggeredBy: 'alloc-failure',
|
||||
Type: 'service',
|
||||
WaitUntil: '2021-12-22T14:10:28.108136Z',
|
||||
},
|
||||
{
|
||||
CreateIndex: 1322,
|
||||
CreateTime: 1640183505760099000,
|
||||
DeploymentID: '878435bf-7265-62b1-7902-d45c44b23b79',
|
||||
ID: 'c184f72b-68a3-5180-afd6-af01860ad371',
|
||||
JobID: 'cores-example',
|
||||
JobModifyIndex: 1305,
|
||||
ModifyIndex: 1329,
|
||||
ModifyTime: 1640183535540881000,
|
||||
Namespace: 'default',
|
||||
PreviousEval: '9a917a93-7bc3-6991-ffc9-15919a38f04b',
|
||||
Priority: 50,
|
||||
QueuedAllocations: {
|
||||
lb: 0,
|
||||
},
|
||||
SnapshotIndex: 1326,
|
||||
Status: 'complete',
|
||||
TriggeredBy: 'alloc-failure',
|
||||
Type: 'service',
|
||||
WaitUntil: '2021-12-22T14:32:15.539556Z',
|
||||
},
|
||||
];
|
||||
|
||||
module('Acceptance | evaluations list', function(hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
test('it passes an accessibility audit', async function(assert) {
|
||||
await visit('/evaluations');
|
||||
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'evaluations.index',
|
||||
'The default route in evaluations is evaluations index'
|
||||
);
|
||||
|
||||
await a11yAudit(assert);
|
||||
});
|
||||
|
||||
test('it renders an empty message if there are no evaluations rendered', async function(assert) {
|
||||
await visit('/evaluations');
|
||||
|
||||
assert.dom('[data-test-empty-evaluations-list]').exists('We display empty table message.');
|
||||
assert
|
||||
.dom('[data-test-no-eval]')
|
||||
.exists('We display a message saying there are no evaluations.');
|
||||
});
|
||||
|
||||
test('it renders a list of evaluations', async function(assert) {
|
||||
assert.expect(3);
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: '',
|
||||
},
|
||||
'Forwards the correct query parameters on default query when route initially loads'
|
||||
);
|
||||
return getStandardRes();
|
||||
});
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
assert.dom('[data-test-eval-table]').exists('Evaluations table should render');
|
||||
assert
|
||||
.dom('[data-test-evaluation]')
|
||||
.exists({ count: 4 }, 'Should render the correct number of evaluations');
|
||||
});
|
||||
|
||||
test('it should enable filtering by evaluation status', async function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
server.get('/evaluations', getStandardRes);
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: 'pending',
|
||||
next_token: '',
|
||||
},
|
||||
'It makes another server request using the options selected by the user'
|
||||
);
|
||||
return [];
|
||||
});
|
||||
|
||||
await clickTrigger('[data-test-evaluation-status-facet]');
|
||||
await selectChoose('[data-test-evaluation-status-facet]', 'Pending');
|
||||
|
||||
assert
|
||||
.dom('[data-test-no-eval-match]')
|
||||
.exists('Renders a message saying no evaluations match filter status');
|
||||
});
|
||||
|
||||
module('page size', function(hooks) {
|
||||
hooks.afterEach(function() {
|
||||
// PageSizeSelect and the Evaluations Controller are both using localStorage directly
|
||||
// Will come back and invert the dependency
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
test('it is possible to change page size', async function(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
server.get('/evaluations', getStandardRes);
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '50',
|
||||
status: '',
|
||||
next_token: '',
|
||||
},
|
||||
'It makes a request with the per_page set by the user'
|
||||
);
|
||||
return getStandardRes();
|
||||
});
|
||||
|
||||
await clickTrigger('[data-test-per-page]');
|
||||
await selectChoose('[data-test-per-page]', 50);
|
||||
});
|
||||
});
|
||||
|
||||
module('pagination', function() {
|
||||
test('it should enable pagination by using next tokens', async function(assert) {
|
||||
assert.expect(7);
|
||||
|
||||
server.get('/evaluations', function() {
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-1' }, getStandardRes());
|
||||
});
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: 'next-token-1',
|
||||
},
|
||||
'It makes another server request using the options selected by the user'
|
||||
);
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-2' }, getStandardRes());
|
||||
});
|
||||
|
||||
assert
|
||||
.dom('[data-test-eval-pagination-next]')
|
||||
.isEnabled(
|
||||
'If there is a next-token in the API response the next button should be enabled.'
|
||||
);
|
||||
await click('[data-test-eval-pagination-next]');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: 'next-token-2',
|
||||
},
|
||||
'It makes another server request using the options selected by the user'
|
||||
);
|
||||
return getStandardRes();
|
||||
});
|
||||
await click('[data-test-eval-pagination-next]');
|
||||
|
||||
assert
|
||||
.dom('[data-test-eval-pagination-next]')
|
||||
.isDisabled('If there is no next-token, the next button is disabled.');
|
||||
|
||||
assert
|
||||
.dom('[data-test-eval-pagination-prev]')
|
||||
.isEnabled('After we transition to the next page, the previous page button is enabled.');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: 'next-token-1',
|
||||
},
|
||||
'It makes a request using the stored old token.'
|
||||
);
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-2' }, getStandardRes());
|
||||
});
|
||||
|
||||
await click('[data-test-eval-pagination-prev]');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: '',
|
||||
},
|
||||
'When there are no more stored previous tokens, we will request with no next-token.'
|
||||
);
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-1' }, getStandardRes());
|
||||
});
|
||||
|
||||
await click('[data-test-eval-pagination-prev]');
|
||||
});
|
||||
|
||||
test('it should clear all query parameters on refresh', async function(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
server.get('/evaluations', function() {
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-1' }, getStandardRes());
|
||||
});
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
server.get('/evaluations', function() {
|
||||
return getStandardRes();
|
||||
});
|
||||
|
||||
await click('[data-test-eval-pagination-next]');
|
||||
|
||||
await clickTrigger('[data-test-evaluation-status-facet]');
|
||||
await selectChoose('[data-test-evaluation-status-facet]', 'Pending');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: '',
|
||||
next_token: '',
|
||||
},
|
||||
'It clears all query parameters when making a refresh'
|
||||
);
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-1' }, getStandardRes());
|
||||
});
|
||||
|
||||
await click('[data-test-eval-refresh]');
|
||||
});
|
||||
|
||||
test('it should reset pagination when filters are applied', async function(assert) {
|
||||
assert.expect(1);
|
||||
|
||||
server.get('/evaluations', function() {
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-1' }, getStandardRes());
|
||||
});
|
||||
|
||||
await visit('/evaluations');
|
||||
|
||||
server.get('/evaluations', function() {
|
||||
return new Response(200, { 'x-nomad-nexttoken': 'next-token-2' }, getStandardRes());
|
||||
});
|
||||
|
||||
await click('[data-test-eval-pagination-next]');
|
||||
|
||||
server.get('/evaluations', getStandardRes);
|
||||
await click('[data-test-eval-pagination-next]');
|
||||
|
||||
server.get('/evaluations', function(_server, fakeRequest) {
|
||||
assert.deepEqual(
|
||||
fakeRequest.queryParams,
|
||||
{
|
||||
namespace: '*',
|
||||
per_page: '25',
|
||||
status: 'pending',
|
||||
next_token: '',
|
||||
},
|
||||
'It clears all next token when filtered request is made'
|
||||
);
|
||||
return getStandardRes();
|
||||
});
|
||||
await clickTrigger('[data-test-evaluation-status-facet]');
|
||||
await selectChoose('[data-test-evaluation-status-facet]', 'Pending');
|
||||
});
|
||||
});
|
||||
|
||||
module('resource linking', function() {
|
||||
test('it should generate a link to the job resource', async function(assert) {
|
||||
server.create('node');
|
||||
const job = server.create('job', { shallow: true });
|
||||
server.create('evaluation', { jobId: job.id });
|
||||
await visit('/evaluations');
|
||||
|
||||
assert
|
||||
.dom('[data-test-evaluation-resource]')
|
||||
.hasText(job.name, 'It conditionally renders the correct resource name');
|
||||
await click('[data-test-evaluation-resource]');
|
||||
assert
|
||||
.dom('[data-test-job-name]')
|
||||
.includesText(job.name, 'We navigate to the correct job page.');
|
||||
});
|
||||
|
||||
test('it should generate a link to the node resource', async function(assert) {
|
||||
const node = server.create('node');
|
||||
server.create('evaluation', { nodeId: node.id });
|
||||
await visit('/evaluations');
|
||||
|
||||
const shortNodeId = node.id.split('-')[0];
|
||||
assert
|
||||
.dom('[data-test-evaluation-resource]')
|
||||
.hasText(shortNodeId, 'It conditionally renders the correct resource name');
|
||||
|
||||
await click('[data-test-evaluation-resource]');
|
||||
|
||||
assert
|
||||
.dom('[data-test-title]')
|
||||
.includesText(node.name, 'We navigate to the correct client page.');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue