diff --git a/ui/app/templates/components/job-page/periodic.hbs b/ui/app/templates/components/job-page/periodic.hbs
index 065c8c96e..6a815b332 100644
--- a/ui/app/templates/components/job-page/periodic.hbs
+++ b/ui/app/templates/components/job-page/periodic.hbs
@@ -10,7 +10,7 @@
{{job.name}}
{{job.status}}
periodic
-
+
diff --git a/ui/mirage/config.js b/ui/mirage/config.js
index b32c70b7c..c17c7d3e5 100644
--- a/ui/mirage/config.js
+++ b/ui/mirage/config.js
@@ -11,6 +11,7 @@ export function findLeader(schema) {
}
export default function() {
+ const server = this;
this.timing = 0; // delay for each request, automatically set to 0 during testing
this.namespace = 'v1';
@@ -58,6 +59,22 @@ export default function() {
return this.serialize(deployments.where({ jobId: params.id }));
});
+ this.post('/job/:id/periodic/force', function(schema, { params }) {
+ // Create the child job
+ const parent = schema.jobs.find(params.id);
+
+ // Use the server instead of the schema to leverage the job factory
+ server.create('job', 'periodicChild', {
+ parentId: parent.id,
+ namespaceId: parent.namespaceId,
+ namespace: parent.namespace,
+ createAllocations: parent.createAllocations,
+ });
+
+ // Return bogus, since the response is normally just eval information
+ return new Response(200, {}, '{}');
+ });
+
this.get('/deployment/:id');
this.get('/job/:id/evaluations', function({ evaluations }, { params }) {
diff --git a/ui/tests/integration/job-page/periodic-test.js b/ui/tests/integration/job-page/periodic-test.js
new file mode 100644
index 000000000..6c6c379a5
--- /dev/null
+++ b/ui/tests/integration/job-page/periodic-test.js
@@ -0,0 +1,89 @@
+import { run } from '@ember/runloop';
+import { getOwner } from '@ember/application';
+import { test, moduleForComponent } from 'ember-qunit';
+import { click, find, findAll } from 'ember-native-dom-helpers';
+import wait from 'ember-test-helpers/wait';
+import hbs from 'htmlbars-inline-precompile';
+import sinon from 'sinon';
+import { clickTrigger } from 'ember-power-select/test-support/helpers';
+import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
+
+moduleForComponent('job-page/periodic', 'Integration | Component | job-page/periodic', {
+ integration: true,
+ beforeEach() {
+ window.localStorage.clear();
+ this.store = getOwner(this).lookup('service:store');
+ this.server = startMirage();
+ this.server.create('namespace');
+ },
+ afterEach() {
+ this.server.shutdown();
+ window.localStorage.clear();
+ },
+});
+
+test('Clicking Force Launch launches a new periodic child job', function(assert) {
+ const childrenCount = 3;
+
+ this.server.create('job', 'periodic', {
+ id: 'parent',
+ childrenCount,
+ createAllocations: false,
+ });
+
+ this.store.findAll('job');
+
+ return wait().then(() => {
+ const job = this.store.peekAll('job').findBy('plainId', 'parent');
+ this.setProperties({
+ job,
+ sortProperty: 'name',
+ sortDescending: true,
+ currentPage: 1,
+ gotoJob: () => {},
+ });
+
+ this.render(hbs`
+ {{job-page/periodic
+ job=job
+ sortProperty=sortProperty
+ sortDescending=sortDescending
+ currentPage=currentPage
+ gotoJob=gotoJob}}
+ `);
+
+ return wait().then(() => {
+ 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'
+ );
+
+ click('[data-test-force-launch]');
+
+ return wait().then(() => {
+ const id = job.get('plainId');
+ const namespace = job.get('namespace.name') || 'default';
+
+ assert.ok(
+ server.pretender.handledRequests
+ .filterBy('method', 'POST')
+ .find(req => req.url === `/v1/job/${id}/periodic/force?namespace=${namespace}`),
+ 'POST URL was correct'
+ );
+
+ assert.ok(server.db.jobs.length, currentJobCount + 1, 'POST request was made');
+
+ return wait().then(() => {
+ assert.equal(
+ findAll('[data-test-job-name]').length,
+ childrenCount + 1,
+ 'The new periodic job launch is in the children list'
+ );
+ });
+ });
+ });
+ });
+});