UI: Remove ember-native-dom-helpers (#5959)

This also includes migration of some tests to async.
This commit is contained in:
Buck Doyle 2019-07-23 14:40:32 -05:00 committed by GitHub
parent cc20b3169c
commit 354b4c830f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 969 additions and 1478 deletions

View file

@ -75,7 +75,6 @@
"ember-load-initializers": "^1.1.0",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-moment": "^7.8.1",
"ember-native-dom-helpers": "^0.5.4",
"ember-page-title": "^5.0.2",
"ember-power-select": "^2.2.3",
"ember-qunit-nice-errors": "^1.2.0",

View file

@ -1,4 +1,4 @@
import { find } from 'ember-native-dom-helpers';
import { find } from '@ember/test-helpers';
import { module, skip, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';

View file

@ -1,13 +1,11 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import generateResources from '../../mirage/data/generate-resources';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { find } from 'ember-native-dom-helpers';
import { find, render } from '@ember/test-helpers';
import Response from 'ember-cli-mirage/response';
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
import { Promise, resolve } from 'rsvp';
module('Integration | Component | allocation row', function(hooks) {
setupRenderingTest(hooks);
@ -25,7 +23,7 @@ module('Integration | Component | allocation row', function(hooks) {
this.server.shutdown();
});
test('Allocation row polls for stats, even when it errors or has an invalid response', function(assert) {
test('Allocation row polls for stats, even when it errors or has an invalid response', async function(assert) {
const component = this;
let currentFrame = 0;
@ -52,41 +50,34 @@ module('Integration | Component | allocation row', function(hooks) {
});
this.server.create('allocation', { clientStatus: 'running' });
this.store.findAll('allocation');
await this.store.findAll('allocation');
let allocation;
const allocation = this.store.peekAll('allocation').get('firstObject');
return settled()
.then(async () => {
allocation = this.store.peekAll('allocation').get('firstObject');
this.setProperties({
allocation,
context: 'job',
enablePolling: true,
});
this.setProperties({
allocation,
context: 'job',
enablePolling: true,
});
await render(hbs`
{{allocation-row
allocation=allocation
context=context
enablePolling=enablePolling}}
`);
await render(hbs`
{{allocation-row
allocation=allocation
context=context
enablePolling=enablePolling}}
`);
return settled();
})
.then(() => {
assert.equal(
this.server.pretender.handledRequests.filterBy(
'url',
`/v1/client/allocation/${allocation.get('id')}/stats`
).length,
frames.length,
'Requests continue to be made after malformed responses and server errors'
);
});
assert.equal(
this.server.pretender.handledRequests.filterBy(
'url',
`/v1/client/allocation/${allocation.get('id')}/stats`
).length,
frames.length,
'Requests continue to be made after malformed responses and server errors'
);
});
test('Allocation row shows warning when it requires drivers that are unhealthy on the node it is running on', function(assert) {
test('Allocation row shows warning when it requires drivers that are unhealthy on the node it is running on', async function(assert) {
const node = this.server.schema.nodes.first();
const drivers = node.drivers;
Object.values(drivers).forEach(driver => {
@ -96,38 +87,29 @@ module('Integration | Component | allocation row', function(hooks) {
node.update({ drivers });
this.server.create('allocation', { clientStatus: 'running' });
this.store.findAll('job');
this.store.findAll('node');
this.store.findAll('allocation');
await this.store.findAll('job');
await this.store.findAll('node');
await this.store.findAll('allocation');
let allocation;
const allocation = this.store.peekAll('allocation').get('firstObject');
return settled()
.then(async () => {
allocation = this.store.peekAll('allocation').get('firstObject');
this.setProperties({
allocation,
context: 'job',
});
this.setProperties({
allocation,
context: 'job',
});
await render(hbs`
{{allocation-row
allocation=allocation
context=context}}
`);
await render(hbs`
{{allocation-row
allocation=allocation
context=context}}
`);
return settled();
})
.then(() => {
assert.ok(find('[data-test-icon="unhealthy-driver"]'), 'Unhealthy driver icon is shown');
});
assert.ok(find('[data-test-icon="unhealthy-driver"]'), 'Unhealthy driver icon is shown');
});
test('Allocation row shows an icon indicator when it was preempted', async function(assert) {
const allocId = this.server.create('allocation', 'preempted').id;
const allocation = await this.store.findRecord('allocation', allocId);
await settled();
this.setProperties({ allocation, context: 'job' });
await render(hbs`
@ -135,12 +117,11 @@ module('Integration | Component | allocation row', function(hooks) {
allocation=allocation
context=context}}
`);
await settled();
assert.ok(find('[data-test-icon="preemption"]'), 'Preempted icon is shown');
});
test('when an allocation is not running, the utilization graphs are omitted', function(assert) {
test('when an allocation is not running, the utilization graphs are omitted', async function(assert) {
this.setProperties({
context: 'job',
enablePolling: false,
@ -151,56 +132,22 @@ module('Integration | Component | allocation row', function(hooks) {
this.server.create('allocation', { clientStatus })
);
this.store.findAll('allocation');
await this.store.findAll('allocation');
return settled().then(() => {
const allocations = this.store.peekAll('allocation');
return waitForEach(
allocations.map(allocation => async () => {
this.set('allocation', allocation);
await render(hbs`
{{allocation-row
allocation=allocation
context=context
enablePolling=enablePolling}}
`);
return settled().then(() => {
const status = allocation.get('clientStatus');
assert.notOk(find('[data-test-cpu] .inline-chart'), `No CPU chart for ${status}`);
assert.notOk(find('[data-test-mem] .inline-chart'), `No Mem chart for ${status}`);
});
})
);
});
const allocations = this.store.peekAll('allocation');
for (const allocation of allocations.toArray()) {
this.set('allocation', allocation);
await this.render(hbs`
{{allocation-row
allocation=allocation
context=context
enablePolling=enablePolling}}
`);
const status = allocation.get('clientStatus');
assert.notOk(find('[data-test-cpu] .inline-chart'), `No CPU chart for ${status}`);
assert.notOk(find('[data-test-mem] .inline-chart'), `No Mem chart for ${status}`);
}
});
// A way to loop over asynchronous code. Can be replaced by async/await in the future.
const waitForEach = fns => {
let i = 0;
let done = () => {};
// This function is asynchronous and needs to return a promise
const pending = new Promise(resolve => {
done = resolve;
});
const step = () => {
// The waitForEach promise and this recursive loop are done once
// all functions have been called.
if (i >= fns.length) {
done();
return;
}
// Call the current function
const promise = fns[i]() || resolve(true);
// Increment the function position
i++;
// Wait for async behaviors to settle and repeat
promise.then(() => settled()).then(step);
};
step();
return pending;
};
});

View file

@ -2,8 +2,7 @@ import Service from '@ember/service';
import RSVP from 'rsvp';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { findAll } from 'ember-native-dom-helpers';
import { findAll, render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import PromiseObject from 'nomad-ui/utils/classes/promise-object';

View file

@ -1,7 +1,6 @@
import { find, findAll } from 'ember-native-dom-helpers';
import { find, findAll, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import flat from 'flat';

View file

@ -1,7 +1,6 @@
import { findAll, find } from 'ember-native-dom-helpers';
import { findAll, find, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import cleanWhitespace from '../utils/clean-whitespace';

View file

@ -1,8 +1,6 @@
import { assign } from '@ember/polyfills';
import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { create } from 'ember-cli-page-object';
import sinon from 'sinon';
@ -95,8 +93,7 @@ module('Integration | Component | job-editor', function(hooks) {
const renderNewJob = async (component, job) => {
component.setProperties({ job, onSubmit: sinon.spy(), context: 'new' });
component.render(commonTemplate);
await settled();
await component.render(commonTemplate);
};
const renderEditJob = async (component, job) => {
@ -106,33 +103,22 @@ module('Integration | Component | job-editor', function(hooks) {
const planJob = async spec => {
await Editor.editor.fillIn(spec);
await settled();
await Editor.plan();
await settled();
};
test('the default state is an editor with an explanation popup', async function(assert) {
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
assert.ok(Editor.editorHelp.isPresent, 'Editor explanation popup is present');
assert.ok(Editor.editor.isPresent, 'Editor is present');
});
test('the explanation popup can be dismissed', async function(assert) {
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await Editor.editorHelp.dismiss();
await settled();
assert.notOk(Editor.editorHelp.isPresent, 'Editor explanation popup is gone');
assert.equal(
window.localStorage.nomadMessageJobEditor,
@ -144,24 +130,16 @@ module('Integration | Component | job-editor', function(hooks) {
test('the explanation popup is not shown once the dismissal state is set in localStorage', async function(assert) {
window.localStorage.nomadMessageJobEditor = 'false';
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
assert.notOk(Editor.editorHelp.isPresent, 'Editor explanation popup is gone');
});
test('submitting a json job skips the parse endpoint', async function(assert) {
const spec = jsonJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
const requests = this.server.pretender.handledRequests.mapBy('url');
@ -171,12 +149,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('submitting an hcl job requires the parse endpoint', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
const requests = this.server.pretender.handledRequests.mapBy('url');
@ -190,12 +164,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('when a job is successfully parsed and planned, the plan is shown to the user', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
assert.ok(Editor.planOutput, 'The plan is outputted');
@ -205,16 +175,11 @@ module('Integration | Component | job-editor', function(hooks) {
test('from the plan screen, the cancel button goes back to the editor with the job still in tact', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
await Editor.cancel();
await settled();
assert.ok(Editor.editor.isPresent, 'The editor is shown again');
assert.equal(Editor.editor.contents, spec, 'The spec that was planned is still in the editor');
});
@ -222,15 +187,10 @@ module('Integration | Component | job-editor', function(hooks) {
test('when parse fails, the parse error message is shown', async function(assert) {
const spec = hclJob();
const errorMessage = 'Parse Failed!! :o';
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
this.server.pretender.post('/v1/jobs/parse', () => [400, {}, errorMessage]);
await settled();
await renderNewJob(this, job);
await planJob(spec);
assert.notOk(Editor.planError.isPresent, 'Plan error is not shown');
@ -247,15 +207,10 @@ module('Integration | Component | job-editor', function(hooks) {
test('when plan fails, the plan error message is shown', async function(assert) {
const spec = hclJob();
const errorMessage = 'Plan Failed!! :o';
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
this.server.pretender.post(`/v1/job/${newJobName}/plan`, () => [400, {}, errorMessage]);
await settled();
await renderNewJob(this, job);
await planJob(spec);
assert.notOk(Editor.parseError.isPresent, 'Parse error is not shown');
@ -272,19 +227,13 @@ module('Integration | Component | job-editor', function(hooks) {
test('when run fails, the run error message is shown', async function(assert) {
const spec = hclJob();
const errorMessage = 'Run Failed!! :o';
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
this.server.pretender.post('/v1/jobs', () => [400, {}, errorMessage]);
await settled();
await renderNewJob(this, job);
await planJob(spec);
await Editor.run();
await settled();
assert.notOk(Editor.planError.isPresent, 'Plan error is not shown');
assert.notOk(Editor.parseError.isPresent, 'Parse error is not shown');
@ -298,12 +247,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('when the scheduler dry-run has warnings, the warnings are shown to the user', async function(assert) {
const spec = jsonJob({ Unschedulable: true });
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
assert.ok(
@ -322,12 +267,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('when the scheduler dry-run has no warnings, a success message is shown to the user', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
assert.ok(
@ -342,12 +283,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('when a job is submitted in the edit context, a POST request is made to the update job endpoint', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderEditJob(this, job);
await planJob(spec);
await Editor.run();
@ -358,12 +295,8 @@ module('Integration | Component | job-editor', function(hooks) {
test('when a job is submitted in the new context, a POST request is made to the create job endpoint', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
await Editor.run();
@ -377,16 +310,11 @@ module('Integration | Component | job-editor', function(hooks) {
test('when a job is successfully submitted, the onSubmit hook is called', async function(assert) {
const spec = hclJob();
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
await planJob(spec);
await Editor.run();
await settled();
assert.ok(
this.get('onSubmit').calledWith(newJobName, 'default'),
'The onSubmit hook was called with the correct arguments'
@ -394,34 +322,22 @@ module('Integration | Component | job-editor', function(hooks) {
});
test('when the job-editor cancelable flag is false, there is no cancel button in the header', async function(assert) {
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderNewJob(this, job);
assert.notOk(Editor.cancelEditingIsAvailable, 'No way to cancel editing');
});
test('when the job-editor cancelable flag is true, there is a cancel button in the header', async function(assert) {
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderEditJob(this, job);
assert.ok(Editor.cancelEditingIsAvailable, 'Cancel editing button exists');
});
test('when the job-editor cancel button is clicked, the onCancel hook is called', async function(assert) {
let job;
run(() => {
job = this.store.createRecord('job');
});
const job = await this.store.createRecord('job');
await settled();
await renderEditJob(this, job);
await Editor.cancelEditing();
assert.ok(this.get('onCancel').calledOnce, 'The onCancel hook was called');

View file

@ -1,5 +1,4 @@
import { click, find } from 'ember-native-dom-helpers';
import { settled } from '@ember/test-helpers';
import { click, find } from '@ember/test-helpers';
export function jobURL(job, path = '') {
const id = job.get('plainId');
@ -11,20 +10,14 @@ export function jobURL(job, path = '') {
return expectedURL;
}
export function stopJob() {
click('[data-test-stop] [data-test-idle-button]');
return settled().then(() => {
click('[data-test-stop] [data-test-confirm-button]');
return settled();
});
export async function stopJob() {
await click('[data-test-stop] [data-test-idle-button]');
await click('[data-test-stop] [data-test-confirm-button]');
}
export function startJob() {
click('[data-test-start] [data-test-idle-button]');
return settled().then(() => {
click('[data-test-start] [data-test-confirm-button]');
return settled();
});
export async function startJob() {
await click('[data-test-start] [data-test-idle-button]');
await click('[data-test-start] [data-test-confirm-button]');
}
export function expectStartRequest(assert, server, job) {
@ -39,22 +32,19 @@ export function expectStartRequest(assert, server, job) {
assert.ok(requestPayload.Stop == null, 'The Stop signal is not sent in the POST request');
}
export function expectError(assert, title) {
return () => {
assert.equal(
find('[data-test-job-error-title]').textContent,
title,
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
export async function expectError(assert, title) {
assert.equal(
find('[data-test-job-error-title]').textContent,
title,
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
click('[data-test-job-error-close]');
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
return settled();
};
await click('[data-test-job-error-close]');
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
}
export function expectDeleteRequest(assert, server, job) {
@ -66,6 +56,4 @@ export function expectDeleteRequest(assert, server, job) {
.find(req => req.url === expectedURL),
'DELETE URL was made correctly'
);
return settled();
}

View file

@ -1,8 +1,6 @@
import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { find, findAll } from 'ember-native-dom-helpers';
import { find, findAll, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
@ -29,19 +27,14 @@ module('Integration | Component | job-page/parts/body', function(hooks) {
{{/job-page/parts/body}}
`);
await settled();
assert.ok(find('[data-test-subnav="job"]'), 'Job subnav is rendered');
});
test('the subnav includes the deployments link when the job is a service', async function(assert) {
const store = this.owner.lookup('service:store');
let job;
run(() => {
job = store.createRecord('job', {
id: 'service-job',
type: 'service',
});
const job = await store.createRecord('job', {
id: 'service-job',
type: 'service',
});
this.set('job', job);
@ -52,7 +45,6 @@ module('Integration | Component | job-page/parts/body', function(hooks) {
{{/job-page/parts/body}}
`);
await settled();
const subnavLabels = findAll('[data-test-tab]').map(anchor => anchor.textContent);
assert.ok(subnavLabels.some(label => label === 'Definition'), 'Definition link');
assert.ok(subnavLabels.some(label => label === 'Versions'), 'Versions link');
@ -61,13 +53,9 @@ module('Integration | Component | job-page/parts/body', function(hooks) {
test('the subnav does not include the deployments link when the job is not a service', async function(assert) {
const store = this.owner.lookup('service:store');
let job;
run(() => {
job = store.createRecord('job', {
id: 'batch-job',
type: 'batch',
});
const job = await store.createRecord('job', {
id: 'batch-job',
type: 'batch',
});
this.set('job', job);
@ -78,7 +66,6 @@ module('Integration | Component | job-page/parts/body', function(hooks) {
{{/job-page/parts/body}}
`);
await settled();
const subnavLabels = findAll('[data-test-tab]').map(anchor => anchor.textContent);
assert.ok(subnavLabels.some(label => label === 'Definition'), 'Definition link');
assert.ok(subnavLabels.some(label => label === 'Versions'), 'Versions link');
@ -94,7 +81,6 @@ module('Integration | Component | job-page/parts/body', function(hooks) {
{{/job-page/parts/body}}
`);
await settled();
assert.ok(
find('[data-test-subnav="job"] + .section > .inner-content'),
'Content is rendered immediately after the subnav'

View file

@ -1,11 +1,9 @@
import { assign } from '@ember/polyfills';
import { run } from '@ember/runloop';
import hbs from 'htmlbars-inline-precompile';
import { findAll, find, click } from 'ember-native-dom-helpers';
import { findAll, find, click, render } from '@ember/test-helpers';
import sinon from 'sinon';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
module('Integration | Component | job-page/parts/children', function(hooks) {
@ -35,139 +33,112 @@ module('Integration | Component | job-page/parts/children', function(hooks) {
options
);
test('lists each child', function(assert) {
let parent;
test('lists each child', async function(assert) {
this.server.create('job', 'periodic', {
id: 'parent',
childrenCount: 3,
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
parent = this.store.peekAll('job').findBy('plainId', 'parent');
});
const parent = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(props(parent));
this.setProperties(props(parent));
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
return settled().then(() => {
assert.equal(
findAll('[data-test-job-name]').length,
parent.get('children.length'),
'A row for each child'
);
});
});
assert.equal(
findAll('[data-test-job-name]').length,
parent.get('children.length'),
'A row for each child'
);
});
test('eventually paginates', function(assert) {
let parent;
test('eventually paginates', async function(assert) {
this.server.create('job', 'periodic', {
id: 'parent',
childrenCount: 11,
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
parent = this.store.peekAll('job').findBy('plainId', 'parent');
});
const parent = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(props(parent));
this.setProperties(props(parent));
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
return settled().then(() => {
const childrenCount = parent.get('children.length');
assert.ok(childrenCount > 10, 'Parent has more children than one page size');
assert.equal(findAll('[data-test-job-name]').length, 10, 'Table length maxes out at 10');
assert.ok(find('.pagination-next'), 'Next button is rendered');
const childrenCount = parent.get('children.length');
assert.ok(childrenCount > 10, 'Parent has more children than one page size');
assert.equal(findAll('[data-test-job-name]').length, 10, 'Table length maxes out at 10');
assert.ok(find('.pagination-next'), 'Next button is rendered');
assert.ok(
new RegExp(`1.10.+?${childrenCount}`).test(find('.pagination-numbers').textContent.trim())
);
});
});
assert.ok(
new RegExp(`1.10.+?${childrenCount}`).test(find('.pagination-numbers').textContent.trim())
);
});
test('is sorted based on the sortProperty and sortDescending properties', function(assert) {
let parent;
test('is sorted based on the sortProperty and sortDescending properties', async function(assert) {
this.server.create('job', 'periodic', {
id: 'parent',
childrenCount: 3,
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
parent = this.store.peekAll('job').findBy('plainId', 'parent');
});
const parent = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(props(parent));
this.setProperties(props(parent));
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
return settled().then(() => {
const sortedChildren = parent.get('children').sortBy('name');
const childRows = findAll('[data-test-job-name]');
const sortedChildren = parent.get('children').sortBy('name');
const childRows = findAll('[data-test-job-name]');
sortedChildren.reverse().forEach((child, index) => {
assert.equal(
childRows[index].textContent.trim(),
child.get('name'),
`Child ${index} is ${child.get('name')}`
);
});
sortedChildren.reverse().forEach((child, index) => {
assert.equal(
childRows[index].textContent.trim(),
child.get('name'),
`Child ${index} is ${child.get('name')}`
);
});
this.set('sortDescending', false);
await this.set('sortDescending', false);
sortedChildren.forEach((child, index) => {
assert.equal(
childRows[index].textContent.trim(),
child.get('name'),
`Child ${index} is ${child.get('name')}`
);
});
return settled();
});
sortedChildren.forEach((child, index) => {
assert.equal(
childRows[index].textContent.trim(),
child.get('name'),
`Child ${index} is ${child.get('name')}`
);
});
});
test('gotoJob is called when a job row is clicked', function(assert) {
let parent;
test('gotoJob is called when a job row is clicked', async function(assert) {
const gotoJobSpy = sinon.spy();
this.server.create('job', 'periodic', {
@ -176,35 +147,30 @@ module('Integration | Component | job-page/parts/children', function(hooks) {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
parent = this.store.peekAll('job').findBy('plainId', 'parent');
});
const parent = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(
props(parent, {
gotoJob: gotoJobSpy,
})
);
this.setProperties(
props(parent, {
gotoJob: gotoJobSpy,
})
);
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
await render(hbs`
{{job-page/parts/children
job=job
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage
gotoJob=gotoJob}}
`);
return settled().then(() => {
click('tr.job-row');
assert.ok(
gotoJobSpy.withArgs(parent.get('children.firstObject')).calledOnce,
'Clicking the job row calls the gotoJob action'
);
});
});
await click('tr.job-row');
assert.ok(
gotoJobSpy.withArgs(parent.get('children.firstObject')).calledOnce,
'Clicking the job row calls the gotoJob action'
);
});
});

View file

@ -1,7 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { click, find } from 'ember-native-dom-helpers';
import { click, find, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import moment from 'moment';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
@ -23,182 +22,156 @@ module('Integration | Component | job-page/parts/latest-deployment', function(ho
window.localStorage.clear();
});
test('there is no latest deployment section when the job has no deployments', function(assert) {
test('there is no latest deployment section when the job has no deployments', async function(assert) {
this.server.create('job', {
type: 'service',
noDeployments: true,
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}})
`);
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}})
`);
return settled().then(() => {
assert.notOk(find('[data-test-active-deployment]'), 'No active deployment');
});
});
assert.notOk(find('[data-test-active-deployment]'), 'No active deployment');
});
test('the latest deployment section shows up for the currently running deployment', function(assert) {
test('the latest deployment section shows up for the currently running deployment', async function(assert) {
this.server.create('job', {
type: 'service',
createAllocations: false,
activeDeployment: true,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
return settled().then(() => {
const deployment = this.get('job.latestDeployment');
const version = deployment.get('version');
const deployment = await this.get('job.latestDeployment');
const version = await deployment.get('version');
assert.ok(find('[data-test-active-deployment]'), 'Active deployment');
assert.ok(
find('[data-test-active-deployment]').classList.contains('is-info'),
'Running deployment gets the is-info class'
);
assert.equal(
find('[data-test-active-deployment-stat="id"]').textContent.trim(),
deployment.get('shortId'),
'The active deployment is the most recent running deployment'
);
assert.ok(find('[data-test-active-deployment]'), 'Active deployment');
assert.ok(
find('[data-test-active-deployment]').classList.contains('is-info'),
'Running deployment gets the is-info class'
);
assert.equal(
find('[data-test-active-deployment-stat="id"]').textContent.trim(),
deployment.get('shortId'),
'The active deployment is the most recent running deployment'
);
assert.equal(
find('[data-test-active-deployment-stat="submit-time"]').textContent.trim(),
moment(version.get('submitTime')).fromNow(),
'Time since the job was submitted is in the active deployment header'
);
assert.equal(
find('[data-test-active-deployment-stat="submit-time"]').textContent.trim(),
moment(version.get('submitTime')).fromNow(),
'Time since the job was submitted is in the active deployment header'
);
assert.equal(
find('[data-test-deployment-metric="canaries"]').textContent.trim(),
`${deployment.get('placedCanaries')} / ${deployment.get('desiredCanaries')}`,
'Canaries, both places and desired, are in the metrics'
);
assert.equal(
find('[data-test-deployment-metric="canaries"]').textContent.trim(),
`${deployment.get('placedCanaries')} / ${deployment.get('desiredCanaries')}`,
'Canaries, both places and desired, are in the metrics'
);
assert.equal(
find('[data-test-deployment-metric="placed"]').textContent.trim(),
deployment.get('placedAllocs'),
'Placed allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="placed"]').textContent.trim(),
deployment.get('placedAllocs'),
'Placed allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="desired"]').textContent.trim(),
deployment.get('desiredTotal'),
'Desired allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="desired"]').textContent.trim(),
deployment.get('desiredTotal'),
'Desired allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="healthy"]').textContent.trim(),
deployment.get('healthyAllocs'),
'Healthy allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="healthy"]').textContent.trim(),
deployment.get('healthyAllocs'),
'Healthy allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="unhealthy"]').textContent.trim(),
deployment.get('unhealthyAllocs'),
'Unhealthy allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-metric="unhealthy"]').textContent.trim(),
deployment.get('unhealthyAllocs'),
'Unhealthy allocs aggregates across task groups'
);
assert.equal(
find('[data-test-deployment-notification]').textContent.trim(),
deployment.get('statusDescription'),
'Status description is in the metrics block'
);
});
});
assert.equal(
find('[data-test-deployment-notification]').textContent.trim(),
deployment.get('statusDescription'),
'Status description is in the metrics block'
);
});
test('when there is no running deployment, the latest deployment section shows up for the last deployment', function(assert) {
test('when there is no running deployment, the latest deployment section shows up for the last deployment', async function(assert) {
this.server.create('job', {
type: 'service',
createAllocations: false,
noActiveDeployment: true,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
return settled().then(() => {
assert.ok(find('[data-test-active-deployment]'), 'Active deployment');
assert.notOk(
find('[data-test-active-deployment]').classList.contains('is-info'),
'Non-running deployment does not get the is-info class'
);
});
});
assert.ok(find('[data-test-active-deployment]'), 'Active deployment');
assert.notOk(
find('[data-test-active-deployment]').classList.contains('is-info'),
'Non-running deployment does not get the is-info class'
);
});
test('the latest deployment section can be expanded to show task groups and allocations', function(assert) {
test('the latest deployment section can be expanded to show task groups and allocations', async function(assert) {
this.server.create('node');
this.server.create('job', { type: 'service', activeDeployment: true });
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
return settled().then(() => {
assert.notOk(find('[data-test-deployment-task-groups]'), 'Task groups not found');
assert.notOk(find('[data-test-deployment-allocations]'), 'Allocations not found');
assert.notOk(find('[data-test-deployment-task-groups]'), 'Task groups not found');
assert.notOk(find('[data-test-deployment-allocations]'), 'Allocations not found');
click('[data-test-deployment-toggle-details]');
await click('[data-test-deployment-toggle-details]');
return settled().then(() => {
assert.ok(find('[data-test-deployment-task-groups]'), 'Task groups found');
assert.ok(find('[data-test-deployment-allocations]'), 'Allocations found');
});
});
});
assert.ok(find('[data-test-deployment-task-groups]'), 'Task groups found');
assert.ok(find('[data-test-deployment-allocations]'), 'Allocations found');
});
test('each task group in the expanded task group section shows task group details', function(assert) {
test('each task group in the expanded task group section shows task group details', async function(assert) {
this.server.create('node');
this.server.create('job', { type: 'service', activeDeployment: true });
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
const job = this.store.peekAll('job').get('firstObject');
const job = this.store.peekAll('job').get('firstObject');
this.set('job', job);
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
this.set('job', job);
await render(hbs`
{{job-page/parts/latest-deployment job=job}}
`);
return settled()
.then(() => {
click('[data-test-deployment-toggle-details]');
return settled();
})
.then(() => {
const task = job.get('runningDeployment.taskGroupSummaries.firstObject');
const findForTaskGroup = selector =>
find(`[data-test-deployment-task-group-${selector}]`);
assert.equal(findForTaskGroup('name').textContent.trim(), task.get('name'));
assert.equal(
findForTaskGroup('progress-deadline').textContent.trim(),
moment(task.get('requireProgressBy')).format("MMM DD, 'YY HH:mm:ss ZZ")
);
});
});
await click('[data-test-deployment-toggle-details]');
const task = job.get('runningDeployment.taskGroupSummaries.firstObject');
const findForTaskGroup = selector => find(`[data-test-deployment-task-group-${selector}]`);
assert.equal(findForTaskGroup('name').textContent.trim(), task.get('name'));
assert.equal(
findForTaskGroup('progress-deadline').textContent.trim(),
moment(task.get('requireProgressBy')).format("MMM DD, 'YY HH:mm:ss ZZ")
);
});
});

View file

@ -1,9 +1,7 @@
import { run } from '@ember/runloop';
import hbs from 'htmlbars-inline-precompile';
import { findAll, find } from 'ember-native-dom-helpers';
import { findAll, find, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
@ -23,69 +21,58 @@ module('Integration | Component | job-page/parts/placement-failures', function(h
window.localStorage.clear();
});
test('when the job has placement failures, they are called out', function(assert) {
test('when the job has placement failures, they are called out', async function(assert) {
this.server.create('job', { failedPlacements: true, createAllocations: false });
this.store.findAll('job').then(jobs => {
jobs.forEach(job => job.reload());
});
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
this.set('job', this.store.peekAll('job').get('firstObject'));
});
const job = this.store.peekAll('job').get('firstObject');
await job.reload();
await render(hbs`
{{job-page/parts/placement-failures job=job}})
`);
this.set('job', job);
return settled().then(() => {
const failedEvaluation = this.get('job.evaluations')
.filterBy('hasPlacementFailures')
.sortBy('modifyIndex')
.reverse()
.get('firstObject');
const failedTGAllocs = failedEvaluation.get('failedTGAllocs');
await render(hbs`
{{job-page/parts/placement-failures job=job}})
`);
assert.ok(find('[data-test-placement-failures]'), 'Placement failures section found');
const failedEvaluation = this.get('job.evaluations')
.filterBy('hasPlacementFailures')
.sortBy('modifyIndex')
.reverse()
.get('firstObject');
const failedTGAllocs = failedEvaluation.get('failedTGAllocs');
const taskGroupLabels = findAll('[data-test-placement-failure-task-group]').map(title =>
title.textContent.trim()
);
assert.ok(find('[data-test-placement-failures]'), 'Placement failures section found');
failedTGAllocs.forEach(alloc => {
const name = alloc.get('name');
assert.ok(
taskGroupLabels.find(label => label.includes(name)),
`${name} included in placement failures list`
);
assert.ok(
taskGroupLabels.find(label => label.includes(alloc.get('coalescedFailures') + 1)),
'The number of unplaced allocs = CoalescedFailures + 1'
);
});
});
const taskGroupLabels = findAll('[data-test-placement-failure-task-group]').map(title =>
title.textContent.trim()
);
failedTGAllocs.forEach(alloc => {
const name = alloc.get('name');
assert.ok(
taskGroupLabels.find(label => label.includes(name)),
`${name} included in placement failures list`
);
assert.ok(
taskGroupLabels.find(label => label.includes(alloc.get('coalescedFailures') + 1)),
'The number of unplaced allocs = CoalescedFailures + 1'
);
});
});
test('when the job has no placement failures, the placement failures section is gone', function(assert) {
test('when the job has no placement failures, the placement failures section is gone', async function(assert) {
this.server.create('job', { noFailedPlacements: true, createAllocations: false });
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
run(() => {
this.set('job', this.store.peekAll('job').get('firstObject'));
});
const job = this.store.peekAll('job').get('firstObject');
await job.reload();
await render(hbs`
{{job-page/parts/placement-failures job=job}})
`);
this.set('job', job);
return settled().then(() => {
assert.notOk(
find('[data-test-placement-failures]'),
'Placement failures section not found'
);
});
});
await render(hbs`
{{job-page/parts/placement-failures job=job}})
`);
assert.notOk(find('[data-test-placement-failures]'), 'Placement failures section not found');
});
});

View file

@ -1,8 +1,7 @@
import hbs from 'htmlbars-inline-precompile';
import { find, click } from 'ember-native-dom-helpers';
import { find, click, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
@ -22,262 +21,214 @@ module('Integration | Component | job-page/parts/summary', function(hooks) {
window.localStorage.clear();
});
test('jobs with children use the children diagram', function(assert) {
test('jobs with children use the children diagram', async function(assert) {
this.server.create('job', 'periodic', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled().then(() => {
assert.ok(find('[data-test-children-status-bar]'), 'Children status bar found');
assert.notOk(find('[data-test-allocation-status-bar]'), 'Allocation status bar not found');
});
});
assert.ok(find('[data-test-children-status-bar]'), 'Children status bar found');
assert.notOk(find('[data-test-allocation-status-bar]'), 'Allocation status bar not found');
});
test('jobs without children use the allocations diagram', function(assert) {
test('jobs without children use the allocations diagram', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled().then(() => {
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation status bar found');
assert.notOk(find('[data-test-children-status-bar]'), 'Children status bar not found');
});
});
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation status bar found');
assert.notOk(find('[data-test-children-status-bar]'), 'Children status bar not found');
});
test('the allocations diagram lists all allocation status figures', function(assert) {
test('the allocations diagram lists all allocation status figures', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled().then(() => {
assert.equal(
find('[data-test-legend-value="queued"]').textContent,
this.get('job.queuedAllocs'),
`${this.get('job.queuedAllocs')} are queued`
);
assert.equal(
find('[data-test-legend-value="queued"]').textContent,
this.get('job.queuedAllocs'),
`${this.get('job.queuedAllocs')} are queued`
);
assert.equal(
find('[data-test-legend-value="starting"]').textContent,
this.get('job.startingAllocs'),
`${this.get('job.startingAllocs')} are starting`
);
assert.equal(
find('[data-test-legend-value="starting"]').textContent,
this.get('job.startingAllocs'),
`${this.get('job.startingAllocs')} are starting`
);
assert.equal(
find('[data-test-legend-value="running"]').textContent,
this.get('job.runningAllocs'),
`${this.get('job.runningAllocs')} are running`
);
assert.equal(
find('[data-test-legend-value="running"]').textContent,
this.get('job.runningAllocs'),
`${this.get('job.runningAllocs')} are running`
);
assert.equal(
find('[data-test-legend-value="complete"]').textContent,
this.get('job.completeAllocs'),
`${this.get('job.completeAllocs')} are complete`
);
assert.equal(
find('[data-test-legend-value="complete"]').textContent,
this.get('job.completeAllocs'),
`${this.get('job.completeAllocs')} are complete`
);
assert.equal(
find('[data-test-legend-value="failed"]').textContent,
this.get('job.failedAllocs'),
`${this.get('job.failedAllocs')} are failed`
);
assert.equal(
find('[data-test-legend-value="failed"]').textContent,
this.get('job.failedAllocs'),
`${this.get('job.failedAllocs')} are failed`
);
assert.equal(
find('[data-test-legend-value="lost"]').textContent,
this.get('job.lostAllocs'),
`${this.get('job.lostAllocs')} are lost`
);
});
});
assert.equal(
find('[data-test-legend-value="lost"]').textContent,
this.get('job.lostAllocs'),
`${this.get('job.lostAllocs')} are lost`
);
});
test('the children diagram lists all children status figures', function(assert) {
test('the children diagram lists all children status figures', async function(assert) {
this.server.create('job', 'periodic', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled().then(() => {
assert.equal(
find('[data-test-legend-value="queued"]').textContent,
this.get('job.pendingChildren'),
`${this.get('job.pendingChildren')} are pending`
);
assert.equal(
find('[data-test-legend-value="queued"]').textContent,
this.get('job.pendingChildren'),
`${this.get('job.pendingChildren')} are pending`
);
assert.equal(
find('[data-test-legend-value="running"]').textContent,
this.get('job.runningChildren'),
`${this.get('job.runningChildren')} are running`
);
assert.equal(
find('[data-test-legend-value="running"]').textContent,
this.get('job.runningChildren'),
`${this.get('job.runningChildren')} are running`
);
assert.equal(
find('[data-test-legend-value="complete"]').textContent,
this.get('job.deadChildren'),
`${this.get('job.deadChildren')} are dead`
);
});
});
assert.equal(
find('[data-test-legend-value="complete"]').textContent,
this.get('job.deadChildren'),
`${this.get('job.deadChildren')} are dead`
);
});
test('the summary block can be collapsed', function(assert) {
test('the summary block can be collapsed', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled();
})
.then(() => {
click('[data-test-accordion-toggle]');
return settled();
})
.then(() => {
assert.notOk(find('[data-test-accordion-body]'), 'No accordion body');
assert.notOk(find('[data-test-legend]'), 'No legend');
});
await click('[data-test-accordion-toggle]');
assert.notOk(find('[data-test-accordion-body]'), 'No accordion body');
assert.notOk(find('[data-test-legend]'), 'No legend');
});
test('when collapsed, the summary block includes an inline version of the chart', function(assert) {
test('when collapsed, the summary block includes an inline version of the chart', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
await this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled();
})
.then(() => {
click('[data-test-accordion-toggle]');
return settled();
})
.then(() => {
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation bar still existed');
assert.ok(
find('.inline-chart [data-test-allocation-status-bar]'),
'Allocation bar is rendered in an inline-chart container'
);
});
await click('[data-test-accordion-toggle]');
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation bar still existed');
assert.ok(
find('.inline-chart [data-test-allocation-status-bar]'),
'Allocation bar is rendered in an inline-chart container'
);
});
test('the collapsed/expanded state is persisted to localStorage', function(assert) {
test('the collapsed/expanded state is persisted to localStorage', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled();
})
.then(() => {
assert.notOk(window.localStorage.nomadExpandJobSummary, 'No value in localStorage yet');
click('[data-test-accordion-toggle]');
return settled();
})
.then(() => {
assert.equal(
window.localStorage.nomadExpandJobSummary,
'false',
'Value is stored for the collapsed state'
);
});
assert.notOk(window.localStorage.nomadExpandJobSummary, 'No value in localStorage yet');
await click('[data-test-accordion-toggle]');
assert.equal(
window.localStorage.nomadExpandJobSummary,
'false',
'Value is stored for the collapsed state'
);
});
test('the collapsed/expanded state from localStorage is used for the initial state when available', function(assert) {
test('the collapsed/expanded state from localStorage is used for the initial state when available', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
window.localStorage.nomadExpandJobSummary = 'false';
return settled()
.then(async () => {
this.set('job', this.store.peekAll('job').get('firstObject'));
this.set('job', this.store.peekAll('job').get('firstObject'));
await render(hbs`
{{job-page/parts/summary job=job}}
`);
await render(hbs`
{{job-page/parts/summary job=job}}
`);
return settled();
})
.then(() => {
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation bar still existed');
assert.ok(
find('.inline-chart [data-test-allocation-status-bar]'),
'Allocation bar is rendered in an inline-chart container'
);
assert.ok(find('[data-test-allocation-status-bar]'), 'Allocation bar still existed');
assert.ok(
find('.inline-chart [data-test-allocation-status-bar]'),
'Allocation bar is rendered in an inline-chart container'
);
click('[data-test-accordion-toggle]');
return settled();
})
.then(() => {
assert.equal(
window.localStorage.nomadExpandJobSummary,
'true',
'localStorage value still toggles'
);
assert.ok(find('[data-test-accordion-body]'), 'Summary still expands');
});
await click('[data-test-accordion-toggle]');
assert.equal(
window.localStorage.nomadExpandJobSummary,
'true',
'localStorage value still toggles'
);
assert.ok(find('[data-test-accordion-body]'), 'Summary still expands');
});
});

View file

@ -1,18 +1,15 @@
import { assign } from '@ember/polyfills';
import hbs from 'htmlbars-inline-precompile';
import { click, findAll, find } from 'ember-native-dom-helpers';
import { click, findAll, find } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import sinon from 'sinon';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
import { setupRenderingTest } from 'ember-qunit';
module('Integration | Component | job-page/parts/task-groups', function(hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function() {
fragmentSerializerInitializer(this.owner);
window.localStorage.clear();
this.store = this.owner.lookup('service:store');
this.server = startMirage();
@ -34,135 +31,123 @@ module('Integration | Component | job-page/parts/task-groups', function(hooks) {
options
);
test('the job detail page should list all task groups', function(assert) {
test('the job detail page should list all task groups', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job').then(jobs => {
await this.store.findAll('job').then(jobs => {
jobs.forEach(job => job.reload());
});
return settled().then(async () => {
const job = this.store.peekAll('job').get('firstObject');
this.setProperties(props(job));
const job = this.store.peekAll('job').get('firstObject');
this.setProperties(props(job));
await render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
await this.render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
return settled().then(() => {
assert.equal(
findAll('[data-test-task-group]').length,
job.get('taskGroups.length'),
'One row per task group'
);
});
});
assert.equal(
findAll('[data-test-task-group]').length,
job.get('taskGroups.length'),
'One row per task group'
);
});
test('each row in the task group table should show basic information about the task group', function(assert) {
test('each row in the task group table should show basic information about the task group', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job').then(jobs => {
jobs.forEach(job => job.reload());
const job = await this.store.findAll('job').then(async jobs => {
return await jobs.get('firstObject').reload();
});
return settled().then(async () => {
const job = this.store.peekAll('job').get('firstObject');
const taskGroup = job
.get('taskGroups')
.sortBy('name')
.reverse()
.get('firstObject');
const taskGroups = await job.get('taskGroups');
const taskGroup = taskGroups
.sortBy('name')
.reverse()
.get('firstObject');
this.setProperties(props(job));
this.setProperties(props(job));
await render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
await this.render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
return settled().then(() => {
const taskGroupRow = find('[data-test-task-group]');
const taskGroupRow = find('[data-test-task-group]');
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-name]').textContent.trim(),
taskGroup.get('name'),
'Name'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-count]').textContent.trim(),
taskGroup.get('count'),
'Count'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-cpu]').textContent.trim(),
`${taskGroup.get('reservedCPU')} MHz`,
'Reserved CPU'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-mem]').textContent.trim(),
`${taskGroup.get('reservedMemory')} MiB`,
'Reserved Memory'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-disk]').textContent.trim(),
`${taskGroup.get('reservedEphemeralDisk')} MiB`,
'Reserved Disk'
);
});
});
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-name]').textContent.trim(),
taskGroup.get('name'),
'Name'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-count]').textContent.trim(),
taskGroup.get('count'),
'Count'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-cpu]').textContent.trim(),
`${taskGroup.get('reservedCPU')} MHz`,
'Reserved CPU'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-mem]').textContent.trim(),
`${taskGroup.get('reservedMemory')} MiB`,
'Reserved Memory'
);
assert.equal(
taskGroupRow.querySelector('[data-test-task-group-disk]').textContent.trim(),
`${taskGroup.get('reservedEphemeralDisk')} MiB`,
'Reserved Disk'
);
});
test('gotoTaskGroup is called when task group rows are clicked', function(assert) {
test('gotoTaskGroup is called when task group rows are clicked', async function(assert) {
this.server.create('job', {
createAllocations: false,
});
this.store.findAll('job').then(jobs => {
jobs.forEach(job => job.reload());
const job = await this.store.findAll('job').then(async jobs => {
return await jobs.get('firstObject').reload();
});
return settled().then(async () => {
const taskGroupSpy = sinon.spy();
const job = this.store.peekAll('job').get('firstObject');
const taskGroup = job
.get('taskGroups')
.sortBy('name')
.reverse()
.get('firstObject');
const taskGroupSpy = sinon.spy();
this.setProperties(
props(job, {
gotoTaskGroup: taskGroupSpy,
})
);
const taskGroups = await job.get('taskGroups');
const taskGroup = taskGroups
.sortBy('name')
.reverse()
.get('firstObject');
await render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
this.setProperties(
props(job, {
gotoTaskGroup: taskGroupSpy,
})
);
return settled().then(() => {
click('[data-test-task-group]');
assert.ok(
taskGroupSpy.withArgs(taskGroup).calledOnce,
'Clicking the task group row calls the gotoTaskGroup action'
);
});
});
await this.render(hbs`
{{job-page/parts/task-groups
job=job
sortProperty=sortProperty
sortDescending=sortDescending
gotoTaskGroup=gotoTaskGroup}}
`);
await click('[data-test-task-group]');
assert.ok(
taskGroupSpy.withArgs(taskGroup).calledOnce,
'Clicking the task group row calls the gotoTaskGroup action'
);
});
});

View file

@ -1,7 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { click, find, findAll } from 'ember-native-dom-helpers';
import { click, find, findAll, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import {
@ -45,7 +44,7 @@ module('Integration | Component | job-page/periodic', function(hooks) {
gotoJob: () => {},
});
test('Clicking Force Launch launches a new periodic child job', function(assert) {
test('Clicking Force Launch launches a new periodic child job', async function(assert) {
const childrenCount = 3;
this.server.create('job', 'periodic', {
@ -54,42 +53,36 @@ module('Integration | Component | job-page/periodic', function(hooks) {
createAllocations: false,
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
const job = this.store.peekAll('job').findBy('plainId', 'parent');
const job = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await this.render(commonTemplate);
return settled().then(() => {
const currentJobCount = server.db.jobs.length;
const currentJobCount = server.db.jobs.length;
assert.equal(
findAll('[data-test-job-name]').length,
childrenCount,
'The new periodic job launch is in the children list'
);
assert.equal(
findAll('[data-test-job-name]').length,
childrenCount,
'The new periodic job launch is in the children list'
);
click('[data-test-force-launch]');
await click('[data-test-force-launch]');
return settled().then(() => {
const expectedURL = jobURL(job, '/periodic/force');
const expectedURL = jobURL(job, '/periodic/force');
assert.ok(
this.server.pretender.handledRequests
.filterBy('method', 'POST')
.find(req => req.url === expectedURL),
'POST URL was correct'
);
assert.ok(
this.server.pretender.handledRequests
.filterBy('method', 'POST')
.find(req => req.url === expectedURL),
'POST URL was correct'
);
assert.equal(server.db.jobs.length, currentJobCount + 1, 'POST request was made');
});
});
});
assert.equal(server.db.jobs.length, currentJobCount + 1, 'POST request was made');
});
test('Clicking force launch without proper permissions shows an error message', function(assert) {
test('Clicking force launch without proper permissions shows an error message', async function(assert) {
this.server.pretender.post('/v1/job/:id/periodic/force', () => [403, {}, null]);
this.server.create('job', 'periodic', {
@ -99,39 +92,33 @@ module('Integration | Component | job-page/periodic', function(hooks) {
status: 'running',
});
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
const job = this.store.peekAll('job').findBy('plainId', 'parent');
const job = this.store.peekAll('job').findBy('plainId', 'parent');
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await this.render(commonTemplate);
return settled().then(() => {
assert.notOk(find('[data-test-job-error-title]'), 'No error message yet');
assert.notOk(find('[data-test-job-error-title]'), 'No error message yet');
click('[data-test-force-launch]');
await click('[data-test-force-launch]');
return settled().then(() => {
assert.equal(
find('[data-test-job-error-title]').textContent,
'Could Not Force Launch',
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
assert.equal(
find('[data-test-job-error-title]').textContent,
'Could Not Force Launch',
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
click('[data-test-job-error-close]');
await click('[data-test-job-error-close]');
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
});
});
});
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
});
test('Stopping a job sends a delete request for the job', function(assert) {
test('Stopping a job sends a delete request for the job', async function(assert) {
const mirageJob = this.server.create('job', 'periodic', {
childrenCount: 0,
createAllocations: false,
@ -139,22 +126,18 @@ module('Integration | Component | job-page/periodic', function(hooks) {
});
let job;
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
await stopJob();
return settled();
})
.then(stopJob)
.then(() => expectDeleteRequest(assert, this.server, job));
expectDeleteRequest(assert, this.server, job);
});
test('Stopping a job without proper permissions shows an error message', function(assert) {
test('Stopping a job without proper permissions shows an error message', async function(assert) {
this.server.pretender.delete('/v1/job/:id', () => [403, {}, null]);
const mirageJob = this.server.create('job', 'periodic', {
@ -163,45 +146,35 @@ module('Integration | Component | job-page/periodic', function(hooks) {
status: 'running',
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(stopJob)
.then(expectError(assert, 'Could Not Stop Job'));
await stopJob();
expectError(assert, 'Could Not Stop Job');
});
test('Starting a job sends a post request for the job using the current definition', function(assert) {
let job;
test('Starting a job sends a post request for the job using the current definition', async function(assert) {
const mirageJob = this.server.create('job', 'periodic', {
childrenCount: 0,
createAllocations: false,
status: 'dead',
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(startJob)
.then(() => expectStartRequest(assert, this.server, job));
await startJob();
expectStartRequest(assert, this.server, job);
});
test('Starting a job without proper permissions shows an error message', function(assert) {
test('Starting a job without proper permissions shows an error message', async function(assert) {
this.server.pretender.post('/v1/job/:id', () => [403, {}, null]);
const mirageJob = this.server.create('job', 'periodic', {
@ -209,18 +182,14 @@ module('Integration | Component | job-page/periodic', function(hooks) {
createAllocations: false,
status: 'dead',
});
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(startJob)
.then(expectError(assert, 'Could Not Start Job'));
await startJob();
expectError(assert, 'Could Not Start Job');
});
});

View file

@ -1,8 +1,7 @@
import { assign } from '@ember/polyfills';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { click, find } from 'ember-native-dom-helpers';
import { click, find, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import { startJob, stopJob, expectError, expectDeleteRequest, expectStartRequest } from './helpers';
@ -57,228 +56,166 @@ module('Integration | Component | job-page/service', function(hooks) {
)
);
test('Stopping a job sends a delete request for the job', function(assert) {
let job;
test('Stopping a job sends a delete request for the job', async function(assert) {
const mirageJob = makeMirageJob(this.server);
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(stopJob)
.then(() => expectDeleteRequest(assert, this.server, job));
await stopJob();
expectDeleteRequest(assert, this.server, job);
});
test('Stopping a job without proper permissions shows an error message', function(assert) {
test('Stopping a job without proper permissions shows an error message', async function(assert) {
this.server.pretender.delete('/v1/job/:id', () => [403, {}, null]);
const mirageJob = makeMirageJob(this.server);
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(stopJob)
.then(expectError(assert, 'Could Not Stop Job'));
await stopJob();
expectError(assert, 'Could Not Stop Job');
});
test('Starting a job sends a post request for the job using the current definition', function(assert) {
let job;
test('Starting a job sends a post request for the job using the current definition', async function(assert) {
const mirageJob = makeMirageJob(this.server, { status: 'dead' });
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(startJob)
.then(() => expectStartRequest(assert, this.server, job));
await startJob();
expectStartRequest(assert, this.server, job);
});
test('Starting a job without proper permissions shows an error message', function(assert) {
test('Starting a job without proper permissions shows an error message', async function(assert) {
this.server.pretender.post('/v1/job/:id', () => [403, {}, null]);
const mirageJob = makeMirageJob(this.server, { status: 'dead' });
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(startJob)
.then(expectError(assert, 'Could Not Start Job'));
await startJob();
expectError(assert, 'Could Not Start Job');
});
test('Recent allocations shows allocations in the job context', function(assert) {
let job;
test('Recent allocations shows allocations in the job context', async function(assert) {
this.server.create('node');
const mirageJob = makeMirageJob(this.server, { createAllocations: true });
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(() => {
const allocation = this.server.db.allocations.sortBy('modifyIndex').reverse()[0];
const allocationRow = Job.allocations.objectAt(0);
const allocation = this.server.db.allocations.sortBy('modifyIndex').reverse()[0];
const allocationRow = Job.allocations.objectAt(0);
assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'ID');
assert.equal(allocationRow.taskGroup, allocation.taskGroup, 'Task Group name');
});
assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'ID');
assert.equal(allocationRow.taskGroup, allocation.taskGroup, 'Task Group name');
});
test('Recent allocations caps out at five', function(assert) {
let job;
test('Recent allocations caps out at five', async function(assert) {
this.server.create('node');
const mirageJob = makeMirageJob(this.server);
this.server.createList('allocation', 10);
this.store.findAll('job');
await this.store.findAll('job');
return settled().then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled().then(() => {
assert.equal(Job.allocations.length, 5, 'Capped at 5 allocations');
assert.ok(
Job.viewAllAllocations.includes(job.get('allocations.length') + ''),
`View link mentions ${job.get('allocations.length')} allocations`
);
});
});
assert.equal(Job.allocations.length, 5, 'Capped at 5 allocations');
assert.ok(
Job.viewAllAllocations.includes(job.get('allocations.length') + ''),
`View link mentions ${job.get('allocations.length')} allocations`
);
});
test('Recent allocations shows an empty message when the job has no allocations', function(assert) {
let job;
test('Recent allocations shows an empty message when the job has no allocations', async function(assert) {
this.server.create('node');
const mirageJob = makeMirageJob(this.server);
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
Job.recentAllocationsEmptyState.headline.includes('No Allocations'),
'No allocations empty message'
);
});
assert.ok(
Job.recentAllocationsEmptyState.headline.includes('No Allocations'),
'No allocations empty message'
);
});
test('Active deployment can be promoted', function(assert) {
let job;
let deployment;
test('Active deployment can be promoted', async function(assert) {
this.server.create('node');
const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
deployment = job.get('latestDeployment');
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const deployment = await job.get('latestDeployment');
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(() => {
click('[data-test-promote-canary]');
return settled();
})
.then(() => {
const requests = this.server.pretender.handledRequests;
assert.ok(
requests
.filterBy('method', 'POST')
.findBy('url', `/v1/deployment/promote/${deployment.get('id')}`),
'A promote POST request was made'
);
});
await click('[data-test-promote-canary]');
const requests = this.server.pretender.handledRequests;
assert.ok(
requests
.filterBy('method', 'POST')
.findBy('url', `/v1/deployment/promote/${deployment.get('id')}`),
'A promote POST request was made'
);
});
test('When promoting the active deployment fails, an error is shown', function(assert) {
test('When promoting the active deployment fails, an error is shown', async function(assert) {
this.server.pretender.post('/v1/deployment/promote/:id', () => [403, {}, null]);
let job;
this.server.create('node');
const mirageJob = makeMirageJob(this.server, { activeDeployment: true });
this.store.findAll('job');
await this.store.findAll('job');
return settled()
.then(async () => {
job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
this.setProperties(commonProperties(job));
await render(commonTemplate);
this.setProperties(commonProperties(job));
await render(commonTemplate);
return settled();
})
.then(() => {
click('[data-test-promote-canary]');
return settled();
})
.then(() => {
assert.equal(
find('[data-test-job-error-title]').textContent,
'Could Not Promote Deployment',
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
await click('[data-test-promote-canary]');
click('[data-test-job-error-close]');
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
return settled();
});
assert.equal(
find('[data-test-job-error-title]').textContent,
'Could Not Promote Deployment',
'Appropriate error is shown'
);
assert.ok(
find('[data-test-job-error-body]').textContent.includes('ACL'),
'The error message mentions ACLs'
);
await click('[data-test-job-error-close]');
assert.notOk(find('[data-test-job-error-title]'), 'Error message is dismissable');
});
});

View file

@ -1,7 +1,6 @@
import { findAll, find } from 'ember-native-dom-helpers';
import { findAll, find, render } from '@ember/test-helpers';
import { module, skip, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | list pagination', function(hooks) {
@ -240,7 +239,8 @@ module('Integration | Component | list pagination', function(hooks) {
assert.equal(
findAll('.item')[item].textContent,
item + (currentPage - 1) * size,
`Rendered items are in the current page, ${currentPage} (${item + (currentPage - 1) * size})`
`Rendered items are in the current page, ${currentPage} (${item +
(currentPage - 1) * size})`
);
}
}

View file

@ -1,7 +1,6 @@
import { findAll, find } from 'ember-native-dom-helpers';
import { findAll, find, render } from '@ember/test-helpers';
import { module, skip, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { faker } from 'ember-cli-mirage';
import hbs from 'htmlbars-inline-precompile';

View file

@ -1,7 +1,6 @@
import { findAll, find, click, focus, keyEvent } from 'ember-native-dom-helpers';
import { findAll, find, click, focus, render, triggerKeyEvent } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import sinon from 'sinon';
import hbs from 'htmlbars-inline-precompile';
@ -55,17 +54,12 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
return settled()
.then(() => {
assert.ok(find('[data-test-dropdown-options]'), 'Options are shown now');
click('[data-test-dropdown-trigger]');
return settled();
})
.then(() => {
assert.notOk(find('[data-test-dropdown-options]'), 'Options are hidden after clicking again');
});
await assert.ok(find('[data-test-dropdown-options]'), 'Options are shown now');
await click('[data-test-dropdown-trigger]');
assert.notOk(find('[data-test-dropdown-options]'), 'Options are hidden after clicking again');
});
test('all options are shown in the options dropdown, each with a checkbox input', async function(assert) {
@ -73,19 +67,17 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
return settled().then(() => {
assert.equal(
findAll('[data-test-dropdown-option]').length,
props.options.length,
'All options are shown'
);
findAll('[data-test-dropdown-option]').forEach((optionEl, index) => {
const label = props.options[index].label;
assert.equal(optionEl.textContent.trim(), label, `Correct label for ${label}`);
assert.ok(optionEl.querySelector('input[type="checkbox"]'), 'Option contains a checkbox');
});
assert.equal(
findAll('[data-test-dropdown-option]').length,
props.options.length,
'All options are shown'
);
findAll('[data-test-dropdown-option]').forEach((optionEl, index) => {
const label = props.options[index].label;
assert.equal(optionEl.textContent.trim(), label, `Correct label for ${label}`);
assert.ok(optionEl.querySelector('input[type="checkbox"]'), 'Option contains a checkbox');
});
});
@ -94,22 +86,16 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-option] label');
return settled()
.then(() => {
click('[data-test-dropdown-option] label');
return settled();
})
.then(() => {
assert.ok(props.onSelect.called, 'onSelect was called');
const newSelection = props.onSelect.getCall(0).args[0];
assert.deepEqual(
newSelection,
[props.options[0].key],
'onSelect was called with the first option key'
);
});
assert.ok(props.onSelect.called, 'onSelect was called');
const newSelection = props.onSelect.getCall(0).args[0];
assert.deepEqual(
newSelection,
[props.options[0].key],
'onSelect was called with the first option key'
);
});
test('the component trigger shows the selection count when there is a selection', async function(assert) {
@ -118,21 +104,22 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
assert.ok(find('[data-test-dropdown-trigger] [data-test-dropdown-count]'), 'The count is shown');
assert.ok(
find('[data-test-dropdown-trigger] [data-test-dropdown-count]'),
'The count is shown'
);
assert.equal(
find('[data-test-dropdown-trigger] [data-test-dropdown-count]').textContent,
props.selection.length,
'The count is accurate'
);
this.set('selection', []);
await this.set('selection', []);
return settled().then(() => {
assert.notOk(
find('[data-test-dropdown-trigger] [data-test-dropdown-count]'),
'The count is no longer shown when the selection is empty'
);
});
assert.notOk(
find('[data-test-dropdown-trigger] [data-test-dropdown-count]'),
'The count is no longer shown when the selection is empty'
);
});
test('pressing DOWN when the trigger has focus opens the options list', async function(assert) {
@ -140,9 +127,9 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
focus('[data-test-dropdown-trigger]');
await focus('[data-test-dropdown-trigger]');
assert.notOk(find('[data-test-dropdown-options]'), 'Options are not shown on focus');
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
assert.ok(find('[data-test-dropdown-options]'), 'Options are now shown');
assert.equal(
document.activeElement,
@ -156,9 +143,9 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
focus('[data-test-dropdown-trigger]');
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await focus('[data-test-dropdown-trigger]');
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
assert.equal(
document.activeElement,
find('[data-test-dropdown-option]'),
@ -171,9 +158,9 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
focus('[data-test-dropdown-trigger]');
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
keyEvent('[data-test-dropdown-trigger]', 'keydown', TAB);
await focus('[data-test-dropdown-trigger]');
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', TAB);
assert.equal(
document.activeElement,
find('[data-test-dropdown-option]'),
@ -186,10 +173,10 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
focus('[data-test-dropdown-option]');
keyEvent('[data-test-dropdown-option]', 'keydown', ARROW_UP);
await focus('[data-test-dropdown-option]');
await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ARROW_UP);
assert.equal(
document.activeElement,
find('[data-test-dropdown-option]'),
@ -202,10 +189,10 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
focus('[data-test-dropdown-option]');
keyEvent('[data-test-dropdown-option]', 'keydown', ARROW_DOWN);
await focus('[data-test-dropdown-option]');
await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ARROW_DOWN);
assert.equal(
document.activeElement,
findAll('[data-test-dropdown-option]')[1],
@ -218,20 +205,26 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
focus('[data-test-dropdown-option]');
await focus('[data-test-dropdown-option]');
const optionEls = findAll('[data-test-dropdown-option]');
const lastIndex = optionEls.length - 1;
optionEls.forEach((option, index) => {
keyEvent(option, 'keydown', ARROW_DOWN);
for (const [index, option] of optionEls.entries()) {
await triggerKeyEvent(option, 'keydown', ARROW_DOWN);
if (index < lastIndex) {
assert.equal(document.activeElement, optionEls[index + 1], `Option ${index + 1} has focus`);
}
});
}
keyEvent(optionEls[lastIndex], 'keydown', ARROW_DOWN);
assert.equal(document.activeElement, optionEls[lastIndex], `Option ${lastIndex} still has focus`);
await triggerKeyEvent(optionEls[lastIndex], 'keydown', ARROW_DOWN);
assert.equal(
document.activeElement,
optionEls[lastIndex],
`Option ${lastIndex} still has focus`
);
});
test('onSelect gets called when pressing SPACE when a list option is focused', async function(assert) {
@ -239,10 +232,10 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
focus('[data-test-dropdown-option]');
keyEvent('[data-test-dropdown-option]', 'keydown', SPACE);
await focus('[data-test-dropdown-option]');
await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', SPACE);
assert.ok(props.onSelect.called, 'onSelect was called');
const newSelection = props.onSelect.getCall(0).args[0];
@ -258,7 +251,7 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
findAll('[data-test-dropdown-option]').forEach(option => {
assert.ok(parseInt(option.getAttribute('tabindex'), 10) > 0, 'tabindex is a positive value');
@ -270,7 +263,7 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
findAll('[data-test-dropdown-option]').forEach(option => {
assert.ok(
@ -285,10 +278,10 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
focus('[data-test-dropdown-trigger]');
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
keyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
keyEvent('[data-test-dropdown-option]', 'keydown', ESC);
await focus('[data-test-dropdown-trigger]');
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await triggerKeyEvent('[data-test-dropdown-trigger]', 'keydown', ARROW_DOWN);
await triggerKeyEvent('[data-test-dropdown-option]', 'keydown', ESC);
assert.notOk(find('[data-test-dropdown-options]'), 'The options list is hidden once more');
assert.equal(
@ -304,7 +297,7 @@ module('Integration | Component | multi-select dropdown', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
click('[data-test-dropdown-trigger]');
await click('[data-test-dropdown-trigger]');
assert.ok(find('[data-test-dropdown-options]'), 'The dropdown is still shown');
assert.ok(find('[data-test-dropdown-empty]'), 'The empty state is shown');
assert.notOk(find('[data-test-dropdown-option]'), 'No options are shown');

View file

@ -1,7 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { find, click } from 'ember-native-dom-helpers';
import { find, click, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
@ -23,54 +22,36 @@ module('Integration | Component | page layout', function(hooks) {
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is not open'
);
click('[data-test-header-gutter-toggle]');
await click('[data-test-header-gutter-toggle]');
return settled().then(() => {
assert.ok(find('[data-test-gutter-menu]').classList.contains('is-open'), 'Gutter menu is open');
});
assert.ok(find('[data-test-gutter-menu]').classList.contains('is-open'), 'Gutter menu is open');
});
test('the gutter-menu hamburger menu closes the gutter menu', async function(assert) {
await render(hbs`{{page-layout}}`);
click('[data-test-header-gutter-toggle]');
await click('[data-test-header-gutter-toggle]');
return settled()
.then(() => {
assert.ok(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is open'
);
click('[data-test-gutter-gutter-toggle]');
return settled();
})
.then(() => {
assert.notOk(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is not open'
);
});
assert.ok(find('[data-test-gutter-menu]').classList.contains('is-open'), 'Gutter menu is open');
await click('[data-test-gutter-gutter-toggle]');
assert.notOk(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is not open'
);
});
test('the gutter-menu backdrop closes the gutter menu', async function(assert) {
await render(hbs`{{page-layout}}`);
click('[data-test-header-gutter-toggle]');
await click('[data-test-header-gutter-toggle]');
return settled()
.then(() => {
assert.ok(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is open'
);
click('[data-test-gutter-backdrop]');
return settled();
})
.then(() => {
assert.notOk(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is not open'
);
});
assert.ok(find('[data-test-gutter-menu]').classList.contains('is-open'), 'Gutter menu is open');
await click('[data-test-gutter-backdrop]');
assert.notOk(
find('[data-test-gutter-menu]').classList.contains('is-open'),
'Gutter menu is not open'
);
});
});

View file

@ -1,7 +1,6 @@
import { find, findAll } from 'ember-native-dom-helpers';
import { find, findAll, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { assign } from '@ember/polyfills';
import hbs from 'htmlbars-inline-precompile';
import cleanWhitespace from '../utils/clean-whitespace';

View file

@ -2,9 +2,8 @@ import EmberObject, { computed } from '@ember/object';
import Service from '@ember/service';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { find } from 'ember-native-dom-helpers';
import { find, render } from '@ember/test-helpers';
import { task } from 'ember-concurrency';
import sinon from 'sinon';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
@ -57,144 +56,107 @@ module('Integration | Component | primary metric', function(hooks) {
metric=metric}}
`;
test('Contains a line chart, a percentage bar, a percentage figure, and an absolute usage figure', function(assert) {
test('Contains a line chart, a percentage bar, a percentage figure, and an absolute usage figure', async function(assert) {
let resource;
const metric = 'cpu';
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(find('[data-test-line-chart]'), 'Line chart');
assert.ok(find('[data-test-percentage-bar]'), 'Percentage bar');
assert.ok(find('[data-test-percentage]'), 'Percentage figure');
assert.ok(find('[data-test-absolute-value]'), 'Absolute usage figure');
});
await render(commonTemplate);
assert.ok(find('[data-test-line-chart]'), 'Line chart');
assert.ok(find('[data-test-percentage-bar]'), 'Percentage bar');
assert.ok(find('[data-test-percentage]'), 'Percentage figure');
assert.ok(find('[data-test-absolute-value]'), 'Absolute usage figure');
});
test('The CPU metric maps to is-info', function(assert) {
let resource;
test('The CPU metric maps to is-info', async function(assert) {
const metric = 'cpu';
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
const resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
find('[data-test-line-chart] .canvas').classList.contains('is-info'),
'Info class for CPU metric'
);
});
await render(commonTemplate);
assert.ok(
find('[data-test-line-chart] .canvas').classList.contains('is-info'),
'Info class for CPU metric'
);
});
test('The Memory metric maps to is-danger', function(assert) {
let resource;
test('The Memory metric maps to is-danger', async function(assert) {
const metric = 'memory';
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
const resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
find('[data-test-line-chart] .canvas').classList.contains('is-danger'),
'Danger class for Memory metric'
);
});
await render(commonTemplate);
assert.ok(
find('[data-test-line-chart] .canvas').classList.contains('is-danger'),
'Danger class for Memory metric'
);
});
test('Gets the tracker from the tracker registry', function(assert) {
let resource;
test('Gets the tracker from the tracker registry', async function(assert) {
const metric = 'cpu';
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
const resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
this.getTrackerSpy.calledWith(resource),
'Uses the tracker registry to get the tracker for the provided resource'
);
});
await render(commonTemplate);
assert.ok(
this.getTrackerSpy.calledWith(resource),
'Uses the tracker registry to get the tracker for the provided resource'
);
});
test('Immediately polls the tracker', function(assert) {
let resource;
test('Immediately polls the tracker', async function(assert) {
const metric = 'cpu';
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
const resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric });
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(this.trackerPollSpy.calledOnce, 'The tracker is polled immediately');
});
await render(commonTemplate);
assert.ok(this.trackerPollSpy.calledOnce, 'The tracker is polled immediately');
});
test('A pause signal is sent to the tracker when the component is destroyed', function(assert) {
let resource;
test('A pause signal is sent to the tracker when the component is destroyed', async function(assert) {
const metric = 'cpu';
// Capture a reference to the spy before the component is destroyed
const trackerSignalPauseSpy = this.trackerSignalPauseSpy;
this.store.findAll('node');
await this.store.findAll('node');
return settled()
.then(async () => {
resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric, showComponent: true });
await render(hbs`
{{#if showComponent}}
{{primary-metric
resource=resource
metric=metric}}
}}
{{/if}}
`);
return settled();
})
.then(() => {
assert.notOk(trackerSignalPauseSpy.called, 'No pause signal has been sent yet');
// This will toggle the if statement, resulting the primary-metric component being destroyed.
this.set('showComponent', false);
return settled();
})
.then(() => {
assert.ok(trackerSignalPauseSpy.calledOnce, 'A pause signal is sent to the tracker');
});
const resource = this.store.peekAll('node').get('firstObject');
this.setProperties({ resource, metric, showComponent: true });
await render(hbs`
{{#if showComponent}}
{{primary-metric
resource=resource
metric=metric}}
}}
{{/if}}
`);
assert.notOk(trackerSignalPauseSpy.called, 'No pause signal has been sent yet');
// This will toggle the if statement, resulting the primary-metric component being destroyed.
this.set('showComponent', false);
assert.ok(trackerSignalPauseSpy.calledOnce, 'A pause signal is sent to the tracker');
});
});

View file

@ -1,7 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { find, findAll } from 'ember-native-dom-helpers';
import { find, findAll, render } from '@ember/test-helpers';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import hbs from 'htmlbars-inline-precompile';
import moment from 'moment';
@ -25,7 +24,7 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
{{reschedule-event-timeline allocation=allocation}}
`;
test('when the allocation is running, the timeline shows past allocations', function(assert) {
test('when the allocation is running, the timeline shows past allocations', async function(assert) {
const attempts = 2;
this.server.create('allocation', 'rescheduled', {
@ -33,53 +32,46 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
rescheduleSuccess: true,
});
this.store.findAll('allocation');
let allocation;
await this.store.findAll('allocation');
return settled()
.then(async () => {
allocation = this.store
.peekAll('allocation')
.find(alloc => !alloc.get('nextAllocation.content'));
const allocation = this.store
.peekAll('allocation')
.find(alloc => !alloc.get('nextAllocation.content'));
this.set('allocation', allocation);
await render(commonTemplate);
this.set('allocation', allocation);
await render(commonTemplate);
return settled();
})
.then(() => {
assert.equal(
findAll('[data-test-allocation]').length,
attempts + 1,
'Total allocations equals current allocation plus all past allocations'
);
assert.equal(
find('[data-test-allocation]'),
find(`[data-test-allocation="${allocation.id}"]`),
'First allocation is the current allocation'
);
assert.equal(
findAll('[data-test-allocation]').length,
attempts + 1,
'Total allocations equals current allocation plus all past allocations'
);
assert.equal(
find('[data-test-allocation]'),
find(`[data-test-allocation="${allocation.id}"]`),
'First allocation is the current allocation'
);
assert.notOk(find('[data-test-stop-warning]'), 'No stop warning');
assert.notOk(find('[data-test-attempt-notice]'), 'No attempt notice');
assert.notOk(find('[data-test-stop-warning]'), 'No stop warning');
assert.notOk(find('[data-test-attempt-notice]'), 'No attempt notice');
assert.equal(
find(
`[data-test-allocation="${allocation.id}"] [data-test-allocation-link]`
).textContent.trim(),
allocation.get('shortId'),
'The "this" allocation is correct'
);
assert.equal(
find(
`[data-test-allocation="${allocation.id}"] [data-test-allocation-status]`
).textContent.trim(),
allocation.get('clientStatus'),
'Allocation shows the status'
);
});
assert.equal(
find(
`[data-test-allocation="${allocation.id}"] [data-test-allocation-link]`
).textContent.trim(),
allocation.get('shortId'),
'The "this" allocation is correct'
);
assert.equal(
find(
`[data-test-allocation="${allocation.id}"] [data-test-allocation-status]`
).textContent.trim(),
allocation.get('clientStatus'),
'Allocation shows the status'
);
});
test('when the allocation has failed and there is a follow up evaluation, a note with a time is shown', function(assert) {
test('when the allocation has failed and there is a follow up evaluation, a note with a time is shown', async function(assert) {
const attempts = 2;
this.server.create('allocation', 'rescheduled', {
@ -87,27 +79,20 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
rescheduleSuccess: false,
});
this.store.findAll('allocation');
let allocation;
await this.store.findAll('allocation');
return settled()
.then(async () => {
allocation = this.store
.peekAll('allocation')
.find(alloc => !alloc.get('nextAllocation.content'));
const allocation = this.store
.peekAll('allocation')
.find(alloc => !alloc.get('nextAllocation.content'));
this.set('allocation', allocation);
await render(commonTemplate);
this.set('allocation', allocation);
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
find('[data-test-stop-warning]'),
'Stop warning is shown since the last allocation failed'
);
assert.notOk(find('[data-test-attempt-notice]'), 'Reschdule attempt notice is not shown');
});
assert.ok(
find('[data-test-stop-warning]'),
'Stop warning is shown since the last allocation failed'
);
assert.notOk(find('[data-test-attempt-notice]'), 'Reschdule attempt notice is not shown');
});
test('when the allocation has failed and there is no follow up evaluation, a warning is shown', async function(assert) {
@ -128,7 +113,6 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
});
await this.store.findAll('allocation');
await settled();
let allocation = this.store
.peekAll('allocation')
@ -136,7 +120,6 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
this.set('allocation', allocation);
await render(commonTemplate);
await settled();
assert.ok(
find('[data-test-attempt-notice]'),
@ -145,7 +128,7 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
assert.notOk(find('[data-test-stop-warning]'), 'Stop warning is not shown');
});
test('when the allocation has a next allocation already, it is shown in the timeline', function(assert) {
test('when the allocation has a next allocation already, it is shown in the timeline', async function(assert) {
const attempts = 2;
const originalAllocation = this.server.create('allocation', 'rescheduled', {
@ -153,39 +136,32 @@ module('Integration | Component | reschedule event timeline', function(hooks) {
rescheduleSuccess: true,
});
this.store.findAll('allocation');
let allocation;
await this.store.findAll('allocation');
return settled()
.then(async () => {
allocation = this.store.peekAll('allocation').findBy('id', originalAllocation.id);
const allocation = this.store.peekAll('allocation').findBy('id', originalAllocation.id);
this.set('allocation', allocation);
await render(commonTemplate);
this.set('allocation', allocation);
await render(commonTemplate);
return settled();
})
.then(() => {
assert.ok(
find('[data-test-reschedule-label]').textContent.trim(),
'Next Allocation',
'The first allocation is the next allocation and labeled as such'
);
assert.ok(
find('[data-test-reschedule-label]').textContent.trim(),
'Next Allocation',
'The first allocation is the next allocation and labeled as such'
);
assert.equal(
find('[data-test-allocation] [data-test-allocation-link]').textContent.trim(),
allocation.get('nextAllocation.shortId'),
'The next allocation item is for the correct allocation'
);
assert.equal(
find('[data-test-allocation] [data-test-allocation-link]').textContent.trim(),
allocation.get('nextAllocation.shortId'),
'The next allocation item is for the correct allocation'
);
assert.equal(
findAll('[data-test-allocation]')[1],
find(`[data-test-allocation="${allocation.id}"]`),
'Second allocation is the current allocation'
);
assert.equal(
findAll('[data-test-allocation]')[1],
find(`[data-test-allocation="${allocation.id}"]`),
'Second allocation is the current allocation'
);
assert.notOk(find('[data-test-stop-warning]'), 'No stop warning');
assert.notOk(find('[data-test-attempt-notice]'), 'No attempt notice');
});
assert.notOk(find('[data-test-stop-warning]'), 'No stop warning');
assert.notOk(find('[data-test-attempt-notice]'), 'No attempt notice');
});
});

View file

@ -1,8 +1,7 @@
import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import { find, click } from 'ember-native-dom-helpers';
import { find, click, render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import Pretender from 'pretender';
import { logEncode } from '../../mirage/data/logs';

View file

@ -1,7 +1,6 @@
import { find, click } from 'ember-native-dom-helpers';
import { find, click, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import { create } from 'ember-cli-page-object';
@ -53,23 +52,21 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await TwoStepButton.idle();
return settled().then(() => {
assert.ok(find('[data-test-cancel-button]'), 'Cancel button is rendered');
assert.equal(TwoStepButton.cancelText, props.cancelText, 'Button is labeled correctly');
assert.ok(find('[data-test-cancel-button]'), 'Cancel button is rendered');
assert.equal(TwoStepButton.cancelText, props.cancelText, 'Button is labeled correctly');
assert.ok(find('[data-test-confirm-button]'), 'Confirm button is rendered');
assert.equal(TwoStepButton.confirmText, props.confirmText, 'Button is labeled correctly');
assert.ok(find('[data-test-confirm-button]'), 'Confirm button is rendered');
assert.equal(TwoStepButton.confirmText, props.confirmText, 'Button is labeled correctly');
assert.equal(
TwoStepButton.confirmationMessage,
props.confirmationMessage,
'Confirmation message is shown'
);
assert.equal(
TwoStepButton.confirmationMessage,
props.confirmationMessage,
'Confirmation message is shown'
);
assert.notOk(find('[data-test-idle-button]'), 'No more idle button');
});
assert.notOk(find('[data-test-idle-button]'), 'No more idle button');
});
test('canceling in the promptForConfirmation state calls the onCancel hook and resets to the idle state', async function(assert) {
@ -77,16 +74,12 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await TwoStepButton.idle();
return settled().then(() => {
TwoStepButton.cancel();
await TwoStepButton.cancel();
return settled().then(() => {
assert.ok(props.onCancel.calledOnce, 'The onCancel hook fired');
assert.ok(find('[data-test-idle-button]'), 'Idle button is back');
});
});
assert.ok(props.onCancel.calledOnce, 'The onCancel hook fired');
assert.ok(find('[data-test-idle-button]'), 'Idle button is back');
});
test('confirming the promptForConfirmation state calls the onConfirm hook and resets to the idle state', async function(assert) {
@ -94,16 +87,12 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await TwoStepButton.idle();
return settled().then(() => {
TwoStepButton.confirm();
await TwoStepButton.confirm();
return settled().then(() => {
assert.ok(props.onConfirm.calledOnce, 'The onConfirm hook fired');
assert.ok(find('[data-test-idle-button]'), 'Idle button is back');
});
});
assert.ok(props.onConfirm.calledOnce, 'The onConfirm hook fired');
assert.ok(find('[data-test-idle-button]'), 'Idle button is back');
});
test('when awaitingConfirmation is true, the cancel and submit buttons are disabled and the submit button is loading', async function(assert) {
@ -112,13 +101,11 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await TwoStepButton.idle();
return settled().then(() => {
assert.ok(TwoStepButton.cancelIsDisabled, 'The cancel button is disabled');
assert.ok(TwoStepButton.confirmIsDisabled, 'The confirm button is disabled');
assert.ok(TwoStepButton.isRunning, 'The confirm button is in a loading state');
});
assert.ok(TwoStepButton.cancelIsDisabled, 'The cancel button is disabled');
assert.ok(TwoStepButton.confirmIsDisabled, 'The confirm button is disabled');
assert.ok(TwoStepButton.isRunning, 'The confirm button is in a loading state');
});
test('when in the prompt state, clicking outside will reset state back to idle', async function(assert) {
@ -126,13 +113,11 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await settled();
await TwoStepButton.idle();
assert.ok(find('[data-test-cancel-button]'), 'In the prompt state');
click(document.body);
await settled();
await click(document.body);
assert.ok(find('[data-test-idle-button]'), 'Back in the idle state');
});
@ -142,13 +127,11 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await settled();
await TwoStepButton.idle();
assert.ok(find('[data-test-cancel-button]'), 'In the prompt state');
click('[data-test-confirmation-message]');
await settled();
await click('[data-test-confirmation-message]');
assert.notOk(find('[data-test-idle-button]'), 'Still in the prompt state');
});
@ -159,13 +142,11 @@ module('Integration | Component | two step button', function(hooks) {
this.setProperties(props);
await render(commonTemplate);
TwoStepButton.idle();
await settled();
await TwoStepButton.idle();
assert.ok(find('[data-test-cancel-button]'), 'In the prompt state');
click(document.body);
await settled();
await click(document.body);
assert.notOk(find('[data-test-idle-button]'), 'Still in the prompt state');
});
@ -178,7 +159,7 @@ module('Integration | Component | two step button', function(hooks) {
assert.ok(TwoStepButton.isDisabled, 'The idle button is disabled');
TwoStepButton.idle();
await TwoStepButton.idle();
assert.ok(find('[data-test-idle-button]'), 'Still in the idle state after clicking');
});
});

View file

@ -4824,7 +4824,7 @@ ember-moment@^7.8.1:
ember-getowner-polyfill "^2.2.0"
ember-macro-helpers "^2.1.0"
ember-native-dom-helpers@^0.5.3, ember-native-dom-helpers@^0.5.4:
ember-native-dom-helpers@^0.5.3:
version "0.5.10"
resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.5.10.tgz#9c7172e4ddfa5dd86830c46a936e2f8eca3e5896"
integrity sha512-bPJX49vlgnBGwFn/3WJPPJjjyd7/atvzW5j01u1dbyFf3bXvHg9Rs1qaZJdk8js0qZ1FINadIEC9vWtgN3w7tg==