Commit d6a9e35c authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '300261-gl-dropdown-in-pipeline_actions' into 'master'

Use gl-dropdown in pipeline manual actions

See merge request gitlab-org/gitlab!53223
parents 577dcd4a 30695b13
<script>
import { GlTooltipDirective, GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '~/flash';
import createFlash from '~/flash';
import { s__, __, sprintf } from '~/locale';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
import eventHub from '../../event_hub';
......@@ -11,10 +11,10 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
GlIcon,
GlCountdown,
GlButton,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlIcon,
},
props: {
actions: {
......@@ -61,7 +61,7 @@ export default {
})
.catch(() => {
this.isLoading = false;
flash(__('An error occurred while making the request.'));
createFlash({ message: __('An error occurred while making the request.') });
});
},
......@@ -76,39 +76,27 @@ export default {
};
</script>
<template>
<div class="btn-group">
<button
v-gl-tooltip
type="button"
:disabled="isLoading"
class="dropdown-new gl-button btn btn-default js-pipeline-dropdown-manual-actions"
:title="__('Run manual or delayed jobs')"
data-toggle="dropdown"
:aria-label="__('Run manual or delayed jobs')"
<gl-dropdown
v-gl-tooltip
:title="__('Run manual or delayed jobs')"
:loading="isLoading"
data-testid="pipelines-manual-actions-dropdown"
right
icon="play"
>
<gl-dropdown-item
v-for="action in actions"
:key="action.path"
:disabled="isActionDisabled(action)"
@click="onClickAction(action)"
>
<gl-icon name="play" class="icon-play" />
<gl-icon name="chevron-down" />
<gl-loading-icon v-if="isLoading" />
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="action in actions" :key="action.path">
<gl-button
category="tertiary"
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
class="js-pipeline-action-link"
@click="onClickAction(action)"
>
<div class="d-flex justify-content-between flex-wrap">
{{ action.name }}
<span v-if="action.scheduled_at">
<gl-icon name="clock" />
<gl-countdown :end-date-string="action.scheduled_at" />
</span>
</div>
</gl-button>
</li>
</ul>
</div>
<div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap">
{{ action.name }}
<span v-if="action.scheduled_at">
<gl-icon name="clock" />
<gl-countdown :end-date-string="action.scheduled_at" />
</span>
</div>
</gl-dropdown-item>
</gl-dropdown>
</template>
......@@ -288,23 +288,23 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown with play button' do
expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it 'has link to the manual action' do
find('.js-pipeline-dropdown-manual-actions').click
find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_button('manual build')
end
context 'when manual action was played' do
before do
find('.js-pipeline-dropdown-manual-actions').click
find('[data-testid="pipelines-manual-actions-dropdown"]').click
click_button('manual build')
end
it 'enqueues manual action job' do
expect(page).to have_selector('.js-pipeline-dropdown-manual-actions:disabled')
expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] .gl-dropdown-toggle:disabled')
end
end
end
......@@ -322,11 +322,11 @@ RSpec.describe 'Pipelines', :js do
end
it 'has a dropdown for actionable jobs' do
expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
end
it "has link to the delayed job's action" do
find('.js-pipeline-dropdown-manual-actions').click
find('[data-testid="pipelines-manual-actions-dropdown"]').click
time_diff = [0, delayed_job.scheduled_at - Time.now].max
expect(page).to have_button('delayed job 1')
......@@ -342,7 +342,7 @@ RSpec.describe 'Pipelines', :js do
end
it "shows 00:00:00 as the remaining time" do
find('.js-pipeline-dropdown-manual-actions').click
find('[data-testid="pipelines-manual-actions-dropdown"]').click
expect(page).to have_content("00:00:00")
end
......@@ -350,7 +350,7 @@ RSpec.describe 'Pipelines', :js do
context 'when user played a delayed job immediately' do
before do
find('.js-pipeline-dropdown-manual-actions').click
find('[data-testid="pipelines-manual-actions-dropdown"]').click
page.accept_confirm { click_button('delayed job 1') }
wait_for_requests
end
......
import { shallowMount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { GlButton } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import PipelinesActions from '~/pipelines/components/pipelines_list/pipelines_actions.vue';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
jest.mock('~/flash');
describe('Pipelines Actions dropdown', () => {
let wrapper;
let mock;
const createComponent = (actions = []) => {
wrapper = shallowMount(PipelinesActions, {
const createComponent = (props, mountFn = shallowMount) => {
wrapper = mountFn(PipelinesActions, {
propsData: {
actions,
...props,
},
});
};
const findAllDropdownItems = () => wrapper.findAll(GlButton);
const findDropdown = () => wrapper.find(GlDropdown);
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findAllCountdowns = () => wrapper.findAll(GlCountdown);
beforeEach(() => {
......@@ -47,7 +51,7 @@ describe('Pipelines Actions dropdown', () => {
];
beforeEach(() => {
createComponent(mockActions);
createComponent({ actions: mockActions });
});
it('renders a dropdown with the provided actions', () => {
......@@ -59,16 +63,33 @@ describe('Pipelines Actions dropdown', () => {
});
describe('on click', () => {
it('makes a request and toggles the loading state', () => {
beforeEach(() => {
createComponent({ actions: mockActions }, mount);
});
it('makes a request and toggles the loading state', async () => {
mock.onPost(mockActions.path).reply(200);
wrapper.find(GlButton).vm.$emit('click');
findAllDropdownItems().at(0).vm.$emit('click');
await wrapper.vm.$nextTick();
expect(findDropdown().props('loading')).toBe(true);
await waitForPromises();
expect(findDropdown().props('loading')).toBe(false);
});
it('makes a failed request and toggles the loading state', async () => {
mock.onPost(mockActions.path).reply(500);
findAllDropdownItems().at(0).vm.$emit('click');
expect(wrapper.vm.isLoading).toBe(true);
await wrapper.vm.$nextTick();
expect(findDropdown().props('loading')).toBe(true);
return waitForPromises().then(() => {
expect(wrapper.vm.isLoading).toBe(false);
});
await waitForPromises();
expect(findDropdown().props('loading')).toBe(false);
expect(createFlash).toHaveBeenCalledTimes(1);
});
});
});
......@@ -89,10 +110,10 @@ describe('Pipelines Actions dropdown', () => {
beforeEach(() => {
jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
createComponent([scheduledJobAction, expiredJobAction]);
createComponent({ actions: [scheduledJobAction, expiredJobAction] });
});
it('makes post request after confirming', () => {
it('makes post request after confirming', async () => {
mock.onPost(scheduledJobAction.path).reply(200);
jest.spyOn(window, 'confirm').mockReturnValue(true);
......@@ -100,19 +121,22 @@ describe('Pipelines Actions dropdown', () => {
expect(window.confirm).toHaveBeenCalled();
return waitForPromises().then(() => {
expect(mock.history.post.length).toBe(1);
});
await waitForPromises();
expect(mock.history.post).toHaveLength(1);
});
it('does not make post request if confirmation is cancelled', () => {
it('does not make post request if confirmation is cancelled', async () => {
mock.onPost(scheduledJobAction.path).reply(200);
jest.spyOn(window, 'confirm').mockReturnValue(false);
findAllDropdownItems().at(0).vm.$emit('click');
expect(window.confirm).toHaveBeenCalled();
expect(mock.history.post.length).toBe(0);
await waitForPromises();
expect(mock.history.post).toHaveLength(0);
});
it('displays the remaining time in the dropdown', () => {
......
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