Commit 2af0b083 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'winh-stop-jobs-modal' into 'master'

Add modal for stopping jobs in admin area

Closes #41305

See merge request gitlab-org/gitlab-ce!16283
parents 646ec660 76f16bbf
...@@ -157,6 +157,11 @@ import Activities from './activities'; ...@@ -157,6 +157,11 @@ import Activities from './activities';
case 'dashboard:todos:index': case 'dashboard:todos:index':
import('./pages/dashboard/todos/index').then(callDefault).catch(fail); import('./pages/dashboard/todos/index').then(callDefault).catch(fail);
break; break;
case 'admin:jobs:index':
import('./pages/admin/jobs/index')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:projects:index': case 'dashboard:projects:index':
case 'dashboard:projects:starred': case 'dashboard:projects:starred':
import('./pages/dashboard/projects') import('./pages/dashboard/projects')
......
<script>
import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import modal from '~/vue_shared/components/modal.vue';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
export default {
components: {
modal,
},
props: {
url: {
type: String,
required: true,
},
},
computed: {
text() {
return s__('AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.');
},
},
methods: {
onSubmit() {
return axios.post(this.url)
.then((response) => {
// follow the rediect to refresh the page
redirectTo(response.request.responseURL);
})
.catch((error) => {
Flash(s__('AdminArea|Stopping jobs failed'));
throw error;
});
},
},
};
</script>
<template>
<modal
id="stop-jobs-modal"
:title="s__('AdminArea|Stop all jobs?')"
:text="text"
kind="danger"
:primary-button-label="s__('AdminArea|Stop jobs')"
@submit="onSubmit" />
</template>
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import stopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate);
export default () => {
const stopJobsButton = document.getElementById('stop-jobs-button');
// eslint-disable-next-line no-new
new Vue({
el: '#stop-jobs-modal',
components: {
stopJobsModal,
},
mounted() {
stopJobsButton.classList.remove('disabled');
},
render(createElement) {
return createElement('stop-jobs-modal', {
props: {
url: stopJobsButton.dataset.url,
},
});
},
});
};
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
min-height: $modal-body-height; min-height: $modal-body-height;
position: relative; position: relative;
padding: #{3 * $grid-size} #{2 * $grid-size}; padding: #{3 * $grid-size} #{2 * $grid-size};
text-align: left;
.form-actions { .form-actions {
margin: #{2 * $grid-size} #{-2 * $grid-size} #{-2 * $grid-size}; margin: #{2 * $grid-size} #{-2 * $grid-size} #{-2 * $grid-size};
......
...@@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController ...@@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController
def cancel_all def cancel_all
Ci::Build.running_or_pending.each(&:cancel) Ci::Build.running_or_pending.each(&:cancel)
redirect_to admin_jobs_path redirect_to admin_jobs_path, status: 303
end end
end end
...@@ -9,7 +9,12 @@ ...@@ -9,7 +9,12 @@
.nav-controls .nav-controls
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
= link_to 'Cancel all', cancel_all_admin_jobs_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post #stop-jobs-modal
%button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal',
target: '#stop-jobs-modal',
url: cancel_all_admin_jobs_path } }
= s_('AdminArea|Stop all jobs')
.row-content-block.second-block .row-content-block.second-block
#{(@scope || 'all').capitalize} jobs #{(@scope || 'all').capitalize} jobs
......
...@@ -21,7 +21,7 @@ describe 'Admin Builds' do ...@@ -21,7 +21,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'All') expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_selector('.row-content-block', text: 'All jobs') expect(page).to have_selector('.row-content-block', text: 'All jobs')
expect(page.all('.build-link').size).to eq(4) expect(page.all('.build-link').size).to eq(4)
expect(page).to have_link 'Cancel all' expect(page).to have_button 'Stop all jobs'
end end
end end
...@@ -31,7 +31,7 @@ describe 'Admin Builds' do ...@@ -31,7 +31,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'All') expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_content 'No jobs to show' expect(page).to have_content 'No jobs to show'
expect(page).not_to have_link 'Cancel all' expect(page).not_to have_button 'Stop all jobs'
end end
end end
end end
...@@ -51,7 +51,7 @@ describe 'Admin Builds' do ...@@ -51,7 +51,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build2.id) expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).not_to have_content(build3.id) expect(page.find('.build-link')).not_to have_content(build3.id)
expect(page.find('.build-link')).not_to have_content(build4.id) expect(page.find('.build-link')).not_to have_content(build4.id)
expect(page).to have_link 'Cancel all' expect(page).to have_button 'Stop all jobs'
end end
end end
...@@ -63,7 +63,7 @@ describe 'Admin Builds' do ...@@ -63,7 +63,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Pending') expect(page).to have_selector('.nav-links li.active', text: 'Pending')
expect(page).to have_content 'No jobs to show' expect(page).to have_content 'No jobs to show'
expect(page).not_to have_link 'Cancel all' expect(page).not_to have_button 'Stop all jobs'
end end
end end
end end
...@@ -83,7 +83,7 @@ describe 'Admin Builds' do ...@@ -83,7 +83,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build2.id) expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).not_to have_content(build3.id) expect(page.find('.build-link')).not_to have_content(build3.id)
expect(page.find('.build-link')).not_to have_content(build4.id) expect(page.find('.build-link')).not_to have_content(build4.id)
expect(page).to have_link 'Cancel all' expect(page).to have_button 'Stop all jobs'
end end
end end
...@@ -95,7 +95,7 @@ describe 'Admin Builds' do ...@@ -95,7 +95,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Running') expect(page).to have_selector('.nav-links li.active', text: 'Running')
expect(page).to have_content 'No jobs to show' expect(page).to have_content 'No jobs to show'
expect(page).not_to have_link 'Cancel all' expect(page).not_to have_button 'Stop all jobs'
end end
end end
end end
...@@ -113,7 +113,7 @@ describe 'Admin Builds' do ...@@ -113,7 +113,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build1.id) expect(page.find('.build-link')).not_to have_content(build1.id)
expect(page.find('.build-link')).not_to have_content(build2.id) expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).to have_content(build3.id) expect(page.find('.build-link')).to have_content(build3.id)
expect(page).to have_link 'Cancel all' expect(page).to have_button 'Stop all jobs'
end end
end end
...@@ -125,7 +125,7 @@ describe 'Admin Builds' do ...@@ -125,7 +125,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Finished') expect(page).to have_selector('.nav-links li.active', text: 'Finished')
expect(page).to have_content 'No jobs to show' expect(page).to have_content 'No jobs to show'
expect(page).to have_link 'Cancel all' expect(page).to have_button 'Stop all jobs'
end end
end end
end end
......
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import stopJobsModal from '~/pages/admin/jobs/index/components/stop_jobs_modal.vue';
import * as urlUtility from '~/lib/utils/url_utility';
import mountComponent from '../../../../../helpers/vue_mount_component_helper';
describe('stop_jobs_modal.vue', () => {
const props = {
url: `${gl.TEST_HOST}/stop_jobs_modal.vue/stopAll`,
};
let vm;
afterEach(() => {
vm.$destroy();
});
beforeEach(() => {
const Component = Vue.extend(stopJobsModal);
vm = mountComponent(Component, props);
});
describe('onSubmit', () => {
it('stops jobs and redirects to overview page', (done) => {
const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`;
const redirectSpy = spyOn(urlUtility, 'redirectTo');
spyOn(axios, 'post').and.callFake((url) => {
expect(url).toBe(props.url);
return Promise.resolve({
request: {
responseURL,
},
});
});
vm.onSubmit()
.then(() => {
expect(redirectSpy).toHaveBeenCalledWith(responseURL);
})
.then(done)
.catch(done.fail);
});
it('displays error if stopping jobs failed', (done) => {
const dummyError = new Error('stopping jobs failed');
const redirectSpy = spyOn(urlUtility, 'redirectTo');
spyOn(axios, 'post').and.callFake((url) => {
expect(url).toBe(props.url);
return Promise.reject(dummyError);
});
vm.onSubmit()
.then(done.fail)
.catch((error) => {
expect(error).toBe(dummyError);
expect(redirectSpy).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment