mirror of
https://github.com/kemko/nomad.git
synced 2026-01-11 04:45:42 +03:00
Merge pull request #8742 from hashicorp/f-ui/poststart-poststop
UI: Add poststart and poststop lifecycle phases
This commit is contained in:
@@ -23,4 +23,17 @@ export default class LifecycleChartRow extends Component {
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@computed('task.lifecycleName')
|
||||
get lifecycleLabel() {
|
||||
const name = this.task.lifecycleName;
|
||||
|
||||
if (name.includes('sidecar')) {
|
||||
return 'sidecar';
|
||||
} else if (name.includes('ephemeral')) {
|
||||
return name.substr(0, name.indexOf('-'));
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,11 @@ export default class LifecycleChart extends Component {
|
||||
get lifecyclePhases() {
|
||||
const tasksOrStates = this.taskStates || this.tasks;
|
||||
const lifecycles = {
|
||||
prestarts: [],
|
||||
sidecars: [],
|
||||
'prestart-ephemerals': [],
|
||||
'prestart-sidecars': [],
|
||||
'poststart-ephemerals': [],
|
||||
'poststart-sidecars': [],
|
||||
poststops: [],
|
||||
mains: [],
|
||||
};
|
||||
|
||||
@@ -25,18 +28,29 @@ export default class LifecycleChart extends Component {
|
||||
});
|
||||
|
||||
const phases = [];
|
||||
const stateActiveIterator = state => state.state === 'running';
|
||||
|
||||
if (lifecycles.prestarts.length || lifecycles.sidecars.length) {
|
||||
if (lifecycles.mains.length < tasksOrStates.length) {
|
||||
phases.push({
|
||||
name: 'Prestart',
|
||||
isActive: lifecycles.prestarts.some(state => state.state === 'running'),
|
||||
isActive: lifecycles['prestart-ephemerals'].some(stateActiveIterator),
|
||||
});
|
||||
}
|
||||
|
||||
if (lifecycles.sidecars.length || lifecycles.mains.length) {
|
||||
phases.push({
|
||||
name: 'Main',
|
||||
isActive: lifecycles.mains.some(state => state.state === 'running'),
|
||||
isActive:
|
||||
lifecycles.mains.some(stateActiveIterator) ||
|
||||
lifecycles['poststart-ephemerals'].some(stateActiveIterator),
|
||||
});
|
||||
|
||||
// Poststart is rendered as a subphase of main and therefore has no independent active state
|
||||
phases.push({
|
||||
name: 'Poststart',
|
||||
});
|
||||
|
||||
phases.push({
|
||||
name: 'Poststop',
|
||||
isActive: lifecycles.poststops.some(stateActiveIterator),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,12 +69,14 @@ export default class LifecycleChart extends Component {
|
||||
}
|
||||
|
||||
const lifecycleNameSortPrefix = {
|
||||
prestart: 0,
|
||||
sidecar: 1,
|
||||
'prestart-ephemeral': 0,
|
||||
'prestart-sidecar': 1,
|
||||
main: 2,
|
||||
'poststart-sidecar': 3,
|
||||
'poststart-ephemeral': 4,
|
||||
poststop: 5,
|
||||
};
|
||||
|
||||
function getTaskSortPrefix(task) {
|
||||
// Prestarts first, then sidecars, then mains
|
||||
return `${lifecycleNameSortPrefix[task.lifecycleName]}-${task.name}`;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { computed } from '@ember/object';
|
||||
import { computed as overridable } from 'ember-overridable-computed';
|
||||
import { task } from 'ember-concurrency';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class IndexController extends Controller {
|
||||
@computed('model.task.taskGroup.tasks.@each.name')
|
||||
get otherTaskStates() {
|
||||
const taskName = this.model.task.name;
|
||||
return this.model.allocation.states.rejectBy('name', taskName);
|
||||
}
|
||||
|
||||
@computed('otherTaskStates.@each.lifecycle')
|
||||
get prestartTaskStates() {
|
||||
return this.otherTaskStates.filterBy('task.lifecycle');
|
||||
}
|
||||
|
||||
@overridable(() => {
|
||||
// { title, description }
|
||||
return null;
|
||||
|
||||
@@ -14,8 +14,18 @@ export default class Task extends Fragment {
|
||||
|
||||
@computed('lifecycle', 'lifecycle.sidecar')
|
||||
get lifecycleName() {
|
||||
if (this.lifecycle && this.lifecycle.sidecar) return 'sidecar';
|
||||
if (this.lifecycle && this.lifecycle.hook === 'prestart') return 'prestart';
|
||||
if (this.lifecycle) {
|
||||
const { hook, sidecar } = this.lifecycle;
|
||||
|
||||
if (hook === 'prestart') {
|
||||
return sidecar ? 'prestart-sidecar' : 'prestart-ephemeral';
|
||||
} else if (hook === 'poststart') {
|
||||
return sidecar ? 'poststart-sidecar' : 'poststart-ephemeral';
|
||||
} else if (hook === 'poststop') {
|
||||
return 'poststop';
|
||||
}
|
||||
}
|
||||
|
||||
return 'main';
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
.divider {
|
||||
position: absolute;
|
||||
left: 25%;
|
||||
height: 100%;
|
||||
|
||||
stroke: $ui-gray-200;
|
||||
@@ -19,6 +18,14 @@
|
||||
stroke-dasharray: 1, 7;
|
||||
stroke-dashoffset: 1;
|
||||
stroke-linecap: square;
|
||||
|
||||
&.prestart {
|
||||
left: 25%;
|
||||
}
|
||||
|
||||
&.poststop {
|
||||
left: 75%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +59,16 @@
|
||||
|
||||
&.main {
|
||||
left: 25%;
|
||||
right: 25%;
|
||||
}
|
||||
|
||||
&.poststart {
|
||||
left: 35%;
|
||||
right: 25%;
|
||||
}
|
||||
|
||||
&.poststop {
|
||||
left: 75%;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
@@ -110,12 +127,34 @@
|
||||
|
||||
&.main {
|
||||
margin-left: 25%;
|
||||
margin-right: 25%;
|
||||
}
|
||||
|
||||
&.prestart {
|
||||
&.prestart-ephemeral {
|
||||
margin-right: 75%;
|
||||
}
|
||||
|
||||
&.prestart-sidecar {
|
||||
margin-right: 25%;
|
||||
}
|
||||
|
||||
&.poststart-ephemeral,
|
||||
&.poststart-sidecar {
|
||||
margin-left: 35%;
|
||||
}
|
||||
|
||||
&.poststart-sidecar {
|
||||
margin-right: 25%;
|
||||
}
|
||||
|
||||
&.poststart-ephemeral {
|
||||
margin-right: 35%;
|
||||
}
|
||||
|
||||
&.poststop {
|
||||
margin-left: 75%;
|
||||
}
|
||||
|
||||
&:last-child .task {
|
||||
margin-bottom: 0.9em;
|
||||
}
|
||||
|
||||
@@ -93,38 +93,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (and (not this.model.task.lifecycle) this.prestartTaskStates)}}
|
||||
<div class="boxed-section" data-test-prestart-tasks>
|
||||
<div class="boxed-section-head">
|
||||
Prestart Tasks
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
<ListTable @source={{this.prestartTaskStates}} as |t|>
|
||||
<t.head>
|
||||
<th class="is-narrow"></th>
|
||||
<th>Task</th>
|
||||
<th>State</th>
|
||||
<th>Lifecycle</th>
|
||||
</t.head>
|
||||
<t.body as |row|>
|
||||
<tr data-test-prestart-task>
|
||||
<td class="is-narrow">
|
||||
{{#if (and row.model.isRunning (eq row.model.task.lifecycleName "prestart"))}}
|
||||
<span class="tooltip text-center" role="tooltip" aria-label="Lifecycle constraints not met">
|
||||
{{x-icon "warning" class="is-warning"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-name>{{row.model.task.name}}</td>
|
||||
<td data-test-state>{{row.model.state}}</td>
|
||||
<td data-test-lifecycle>{{row.model.task.lifecycleName}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.model.task.volumeMounts.length}}
|
||||
<div data-test-volumes class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
{{this.task.name}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="lifecycle" data-test-lifecycle>{{capitalize this.task.lifecycleName}} Task</div>
|
||||
<div class="lifecycle" data-test-lifecycle>{{capitalize this.lifecycleLabel}} Task</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
|
||||
<div class="lifecycle-phases">
|
||||
{{#each this.lifecyclePhases as |phase|}}
|
||||
<div class="lifecycle-phase {{if phase.isActive "is-active"}} {{if (eq phase.name "Main") "main" "prestart"}}" data-test-lifecycle-phase>
|
||||
<div class="lifecycle-phase {{if phase.isActive "is-active"}} {{lowercase phase.name}}" data-test-lifecycle-phase>
|
||||
<div class="name" data-test-name>{{phase.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
<svg class="divider">
|
||||
<svg class="divider prestart">
|
||||
<line x1="0" y1="0" x2="0" y2="100%" />
|
||||
</svg>
|
||||
<svg class="divider poststop">
|
||||
<line x1="0" y1="0" x2="0" y2="100%" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -19,14 +19,20 @@ export default Factory.extend({
|
||||
Resources: generateResources,
|
||||
|
||||
Lifecycle: i => {
|
||||
const cycle = i % 3;
|
||||
const cycle = i % 6;
|
||||
|
||||
if (cycle === 0) {
|
||||
return null;
|
||||
} else if (cycle === 1) {
|
||||
return { Hook: 'prestart', Sidecar: false };
|
||||
} else {
|
||||
} else if (cycle === 2) {
|
||||
return { Hook: 'prestart', Sidecar: true };
|
||||
} else if (cycle === 3) {
|
||||
return { Hook: 'poststart', Sidecar: false };
|
||||
} else if (cycle === 4) {
|
||||
return { Hook: 'poststart', Sidecar: true };
|
||||
} else if (cycle === 5) {
|
||||
return { Hook: 'poststop' };
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||
test('/allocation/:id should present task lifecycles', async function(assert) {
|
||||
const job = server.create('job', {
|
||||
groupsCount: 1,
|
||||
groupTaskCount: 3,
|
||||
groupTaskCount: 6,
|
||||
withGroupServices: true,
|
||||
createAllocations: false,
|
||||
});
|
||||
@@ -92,96 +92,23 @@ module('Acceptance | allocation detail', function(hooks) {
|
||||
jobId: job.id,
|
||||
});
|
||||
|
||||
const taskStatePhases = server.db.taskStates.where({ allocationId: allocation.id }).reduce(
|
||||
(phases, state) => {
|
||||
const lifecycle = server.db.tasks.findBy({ name: state.name }).Lifecycle;
|
||||
|
||||
if (lifecycle) {
|
||||
if (lifecycle.Sidecar) {
|
||||
phases.sidecars.push(state);
|
||||
state.lifecycleString = 'Sidecar';
|
||||
} else {
|
||||
phases.prestarts.push(state);
|
||||
state.lifecycleString = 'Prestart';
|
||||
}
|
||||
} else {
|
||||
phases.mains.push(state);
|
||||
state.lifecycleString = 'Main';
|
||||
}
|
||||
|
||||
return phases;
|
||||
},
|
||||
{
|
||||
prestarts: [],
|
||||
sidecars: [],
|
||||
mains: [],
|
||||
}
|
||||
);
|
||||
|
||||
taskStatePhases.prestarts = taskStatePhases.prestarts.sortBy('name');
|
||||
taskStatePhases.sidecars = taskStatePhases.sidecars.sortBy('name');
|
||||
taskStatePhases.mains = taskStatePhases.mains.sortBy('name');
|
||||
|
||||
const sortedServerStates = taskStatePhases.prestarts.concat(
|
||||
taskStatePhases.sidecars,
|
||||
taskStatePhases.mains
|
||||
);
|
||||
|
||||
await Allocation.visit({ id: allocation.id });
|
||||
|
||||
assert.ok(Allocation.lifecycleChart.isPresent);
|
||||
assert.equal(Allocation.lifecycleChart.title, 'Task Lifecycle Status');
|
||||
assert.equal(Allocation.lifecycleChart.phases.length, 2);
|
||||
assert.equal(Allocation.lifecycleChart.tasks.length, sortedServerStates.length);
|
||||
|
||||
const stateActiveIterator = state => state.state === 'running';
|
||||
const anyPrestartsActive = taskStatePhases.prestarts.some(stateActiveIterator);
|
||||
|
||||
if (anyPrestartsActive) {
|
||||
assert.ok(Allocation.lifecycleChart.phases[0].isActive);
|
||||
} else {
|
||||
assert.notOk(Allocation.lifecycleChart.phases[0].isActive);
|
||||
}
|
||||
|
||||
const anyMainsActive = taskStatePhases.mains.some(stateActiveIterator);
|
||||
|
||||
if (anyMainsActive) {
|
||||
assert.ok(Allocation.lifecycleChart.phases[1].isActive);
|
||||
} else {
|
||||
assert.notOk(Allocation.lifecycleChart.phases[1].isActive);
|
||||
}
|
||||
|
||||
Allocation.lifecycleChart.tasks.forEach((Task, index) => {
|
||||
const serverState = sortedServerStates[index];
|
||||
|
||||
assert.equal(Task.name, serverState.name);
|
||||
|
||||
if (serverState.lifecycleString === 'Sidecar') {
|
||||
assert.ok(Task.isSidecar);
|
||||
} else if (serverState.lifecycleString === 'Prestart') {
|
||||
assert.ok(Task.isPrestart);
|
||||
} else {
|
||||
assert.ok(Task.isMain);
|
||||
}
|
||||
|
||||
assert.equal(Task.lifecycle, `${serverState.lifecycleString} Task`);
|
||||
|
||||
if (serverState.state === 'running') {
|
||||
assert.ok(Task.isActive);
|
||||
} else {
|
||||
assert.notOk(Task.isActive);
|
||||
}
|
||||
|
||||
// Task state factory uses invalid dates for tasks that aren’t finished
|
||||
if (isNaN(serverState.finishedAt)) {
|
||||
assert.notOk(Task.isFinished);
|
||||
} else {
|
||||
assert.ok(Task.isFinished);
|
||||
}
|
||||
});
|
||||
assert.equal(Allocation.lifecycleChart.phases.length, 4);
|
||||
assert.equal(Allocation.lifecycleChart.tasks.length, 6);
|
||||
|
||||
await Allocation.lifecycleChart.tasks[0].visit();
|
||||
assert.equal(currentURL(), `/allocations/${allocation.id}/${sortedServerStates[0].name}`);
|
||||
|
||||
const prestartEphemeralTask = server.db.taskStates
|
||||
.where({ allocationId: allocation.id })
|
||||
.find(taskState => {
|
||||
const task = server.db.tasks.findBy({ name: taskState.name });
|
||||
return task.Lifecycle && task.Lifecycle.Hook === 'prestart' && !task.Lifecycle.Sidecar;
|
||||
});
|
||||
|
||||
assert.equal(currentURL(), `/allocations/${allocation.id}/${prestartEphemeralTask.name}`);
|
||||
});
|
||||
|
||||
test('/allocation/:id should list all tasks for the allocation', async function(assert) {
|
||||
|
||||
@@ -101,86 +101,6 @@ module('Acceptance | task detail', function(hooks) {
|
||||
assert.equal(Task.resourceCharts.objectAt(1).name, 'Memory', 'Second chart is Memory');
|
||||
});
|
||||
|
||||
test('/allocation/:id/:task_name lists related prestart tasks for a main task when they exist', async function(assert) {
|
||||
const job = server.create('job', {
|
||||
groupsCount: 2,
|
||||
groupTaskCount: 3,
|
||||
createAllocations: false,
|
||||
status: 'running',
|
||||
});
|
||||
|
||||
job.taskGroups.models.forEach(taskGroup => {
|
||||
server.create('allocation', {
|
||||
jobId: job.id,
|
||||
taskGroup: taskGroup.name,
|
||||
forceRunningClientStatus: true,
|
||||
});
|
||||
});
|
||||
|
||||
const taskGroup = job.taskGroups.models[0];
|
||||
const [mainTask, sidecarTask, prestartTask] = taskGroup.tasks.models;
|
||||
|
||||
mainTask.attrs.Lifecycle = null;
|
||||
mainTask.save();
|
||||
|
||||
sidecarTask.attrs.Lifecycle = { Sidecar: true, Hook: 'prestart' };
|
||||
sidecarTask.save();
|
||||
|
||||
prestartTask.attrs.Lifecycle = { Sidecar: false, Hook: 'prestart' };
|
||||
prestartTask.save();
|
||||
|
||||
taskGroup.save();
|
||||
|
||||
const noPrestartTasksTaskGroup = job.taskGroups.models[1];
|
||||
noPrestartTasksTaskGroup.tasks.models.forEach(task => {
|
||||
task.attrs.Lifecycle = null;
|
||||
task.save();
|
||||
});
|
||||
|
||||
const mainTaskState = server.schema.taskStates.findBy({ name: mainTask.name });
|
||||
const sidecarTaskState = server.schema.taskStates.findBy({ name: sidecarTask.name });
|
||||
const prestartTaskState = server.schema.taskStates.findBy({ name: prestartTask.name });
|
||||
|
||||
prestartTaskState.attrs.state = 'running';
|
||||
prestartTaskState.attrs.finishedAt = null;
|
||||
prestartTaskState.save();
|
||||
|
||||
await Task.visit({ id: mainTaskState.allocationId, name: mainTask.name });
|
||||
|
||||
assert.ok(Task.hasPrestartTasks);
|
||||
assert.equal(Task.prestartTasks.length, 2);
|
||||
|
||||
Task.prestartTasks[0].as(SidecarTask => {
|
||||
assert.equal(SidecarTask.name, sidecarTask.name);
|
||||
assert.equal(SidecarTask.state, sidecarTaskState.state);
|
||||
assert.equal(SidecarTask.lifecycle, 'sidecar');
|
||||
assert.notOk(SidecarTask.isBlocking);
|
||||
});
|
||||
|
||||
Task.prestartTasks[1].as(PrestartTask => {
|
||||
assert.equal(PrestartTask.name, prestartTask.name);
|
||||
assert.equal(PrestartTask.state, prestartTaskState.state);
|
||||
assert.equal(PrestartTask.lifecycle, 'prestart');
|
||||
assert.ok(PrestartTask.isBlocking);
|
||||
});
|
||||
|
||||
await Task.visit({ id: sidecarTaskState.allocationId, name: sidecarTask.name });
|
||||
|
||||
assert.notOk(Task.hasPrestartTasks);
|
||||
|
||||
const noPrestartTasksTask = noPrestartTasksTaskGroup.tasks.models[0];
|
||||
const noPrestartTasksTaskState = server.db.taskStates.findBy({
|
||||
name: noPrestartTasksTask.name,
|
||||
});
|
||||
|
||||
await Task.visit({
|
||||
id: noPrestartTasksTaskState.allocationId,
|
||||
name: noPrestartTasksTaskState.name,
|
||||
});
|
||||
|
||||
assert.notOk(Task.hasPrestartTasks);
|
||||
});
|
||||
|
||||
test('the events table lists all recent events', async function(assert) {
|
||||
const events = server.db.taskEvents.where({ taskStateId: task.id });
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, settled } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { set } from '@ember/object';
|
||||
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
import LifecycleChart from 'nomad-ui/tests/pages/components/lifecycle-chart';
|
||||
@@ -11,19 +12,31 @@ const Chart = create(LifecycleChart);
|
||||
const tasks = [
|
||||
{
|
||||
lifecycleName: 'main',
|
||||
name: 'main two',
|
||||
name: 'main two: 3',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'main',
|
||||
name: 'main one',
|
||||
name: 'main one: 2',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'prestart',
|
||||
name: 'prestart',
|
||||
lifecycleName: 'prestart-ephemeral',
|
||||
name: 'prestart ephemeral: 0',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'sidecar',
|
||||
name: 'sidecar',
|
||||
lifecycleName: 'prestart-sidecar',
|
||||
name: 'prestart sidecar: 1',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'poststart-ephemeral',
|
||||
name: 'poststart ephemeral: 5',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'poststart-sidecar',
|
||||
name: 'poststart sidecar: 4',
|
||||
},
|
||||
{
|
||||
lifecycleName: 'poststop',
|
||||
name: 'poststop: 6',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -38,20 +51,36 @@ module('Integration | Component | lifecycle-chart', function(hooks) {
|
||||
|
||||
assert.equal(Chart.phases[0].name, 'Prestart');
|
||||
assert.equal(Chart.phases[1].name, 'Main');
|
||||
assert.equal(Chart.phases[2].name, 'Poststart');
|
||||
assert.equal(Chart.phases[3].name, 'Poststop');
|
||||
|
||||
Chart.phases.forEach(phase => assert.notOk(phase.isActive));
|
||||
|
||||
assert.deepEqual(Chart.tasks.mapBy('name'), ['prestart', 'sidecar', 'main one', 'main two']);
|
||||
assert.deepEqual(Chart.tasks.mapBy('name'), [
|
||||
'prestart ephemeral: 0',
|
||||
'prestart sidecar: 1',
|
||||
'main one: 2',
|
||||
'main two: 3',
|
||||
'poststart sidecar: 4',
|
||||
'poststart ephemeral: 5',
|
||||
'poststop: 6',
|
||||
]);
|
||||
assert.deepEqual(Chart.tasks.mapBy('lifecycle'), [
|
||||
'Prestart Task',
|
||||
'Sidecar Task',
|
||||
'Main Task',
|
||||
'Main Task',
|
||||
'Sidecar Task',
|
||||
'Poststart Task',
|
||||
'Poststop Task',
|
||||
]);
|
||||
|
||||
assert.ok(Chart.tasks[0].isPrestart);
|
||||
assert.ok(Chart.tasks[1].isSidecar);
|
||||
assert.ok(Chart.tasks[0].isPrestartEphemeral);
|
||||
assert.ok(Chart.tasks[1].isPrestartSidecar);
|
||||
assert.ok(Chart.tasks[2].isMain);
|
||||
assert.ok(Chart.tasks[4].isPoststartSidecar);
|
||||
assert.ok(Chart.tasks[5].isPoststartEphemeral);
|
||||
assert.ok(Chart.tasks[6].isPoststop);
|
||||
|
||||
Chart.tasks.forEach(task => {
|
||||
assert.notOk(task.isActive);
|
||||
@@ -72,6 +101,13 @@ module('Integration | Component | lifecycle-chart', function(hooks) {
|
||||
assert.notOk(Chart.isPresent);
|
||||
});
|
||||
|
||||
test('it renders all phases when there are any non-main tasks', async function(assert) {
|
||||
this.set('tasks', [tasks[0], tasks[6]]);
|
||||
|
||||
await render(hbs`<LifecycleChart @tasks={{tasks}} />`);
|
||||
assert.ok(Chart.phases.length, 4);
|
||||
});
|
||||
|
||||
test('it reflects phase and task states when states are passed in', async function(assert) {
|
||||
this.set(
|
||||
'taskStates',
|
||||
@@ -90,16 +126,64 @@ module('Integration | Component | lifecycle-chart', function(hooks) {
|
||||
assert.notOk(task.isFinished);
|
||||
});
|
||||
|
||||
this.set('taskStates.firstObject.state', 'running');
|
||||
// Change poststart-ephemeral to be running
|
||||
this.set('taskStates.4.state', 'running');
|
||||
await settled();
|
||||
|
||||
assert.ok(Chart.phases[1].isActive);
|
||||
assert.ok(Chart.tasks[3].isActive);
|
||||
await componentA11yAudit(this.element, assert);
|
||||
|
||||
this.set('taskStates.firstObject.finishedAt', new Date());
|
||||
assert.ok(Chart.tasks[5].isActive);
|
||||
|
||||
assert.ok(Chart.phases[1].isActive);
|
||||
assert.notOk(
|
||||
Chart.phases[2].isActive,
|
||||
'the poststart phase is nested within main and should never have the active class'
|
||||
);
|
||||
|
||||
this.set('taskStates.4.finishedAt', new Date());
|
||||
await settled();
|
||||
|
||||
assert.ok(Chart.tasks[3].isFinished);
|
||||
assert.ok(Chart.tasks[5].isFinished);
|
||||
});
|
||||
|
||||
[
|
||||
{
|
||||
testName: 'expected active phases',
|
||||
runningTaskNames: ['prestart ephemeral', 'main one', 'poststop'],
|
||||
activePhaseNames: ['Prestart', 'Main', 'Poststop'],
|
||||
},
|
||||
{
|
||||
testName: 'sidecar task states don’t affect phase active states',
|
||||
runningTaskNames: ['prestart sidecar', 'poststart sidecar'],
|
||||
activePhaseNames: [],
|
||||
},
|
||||
{
|
||||
testName: 'poststart ephemeral task states affect main phase active state',
|
||||
runningTaskNames: ['poststart ephemeral'],
|
||||
activePhaseNames: ['Main'],
|
||||
},
|
||||
].forEach(async ({ testName, runningTaskNames, activePhaseNames }) => {
|
||||
test(testName, async function(assert) {
|
||||
this.set('taskStates', tasks.map(task => ({ task })));
|
||||
|
||||
await render(hbs`<LifecycleChart @taskStates={{taskStates}} />`);
|
||||
|
||||
runningTaskNames.forEach(taskName => {
|
||||
const taskState = this.get('taskStates').find(taskState =>
|
||||
taskState.task.name.includes(taskName)
|
||||
);
|
||||
set(taskState, 'state', 'running');
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
Chart.phases.forEach(Phase => {
|
||||
if (activePhaseNames.includes(Phase.name)) {
|
||||
assert.ok(Phase.isActive, `expected ${Phase.name} not to be active`);
|
||||
} else {
|
||||
assert.notOk(Phase.isActive, `expected ${Phase.name} phase not to be active`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,8 +19,11 @@ export default {
|
||||
isFinished: hasClass('is-finished'),
|
||||
|
||||
isMain: hasClass('main'),
|
||||
isPrestart: hasClass('prestart'),
|
||||
isSidecar: hasClass('sidecar'),
|
||||
isPrestartEphemeral: hasClass('prestart-ephemeral'),
|
||||
isPrestartSidecar: hasClass('prestart-sidecar'),
|
||||
isPoststartEphemeral: hasClass('poststart-ephemeral'),
|
||||
isPoststartSidecar: hasClass('poststart-sidecar'),
|
||||
isPoststop: hasClass('poststop'),
|
||||
|
||||
visit: clickable('a'),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user