Commit fbbf5713 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'mw-deploy-token-revoke-modal-migration' into 'master'

Migrate deploy token revoke button and modal

See merge request gitlab-org/gitlab!57076
parents 33dc6e4b fcd8f890
<script>
import { GlButton, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui';
export default {
components: {
GlModal,
GlSprintf,
GlButton,
},
directives: {
GlModal: GlModalDirective,
},
inject: {
token: {
default: null,
},
revokePath: {
default: '',
},
buttonClass: {
default: '',
},
},
computed: {
modalId() {
return `revoke-modal-${this.token.id}`;
},
},
methods: {
cancelHandler() {
this.$refs.modal.hide();
},
},
};
</script>
<template>
<div>
<gl-button
v-gl-modal="modalId"
:class="buttonClass"
category="primary"
variant="danger"
class="float-right"
data-testid="revoke-button"
>{{ s__('DeployTokens|Revoke') }}</gl-button
>
<gl-modal ref="modal" :modal-id="modalId">
<template #modal-title>
<gl-sprintf :message="s__(`DeployTokens|Revoke %{boldStart}${token.name}%{boldEnd}?`)">
<template #bold="{ content }"
><b>{{ content }}</b></template
>
</gl-sprintf>
</template>
<gl-sprintf
:message="s__(`DeployTokens|You are about to revoke %{boldStart}${token.name}%{boldEnd}.`)"
>
<template #bold="{ content }">
<b>{{ content }}</b>
</template>
</gl-sprintf>
{{ s__('DeployTokens|This action cannot be undone.') }}
<template #modal-footer>
<gl-button category="secondary" @click="cancelHandler">{{ s__('Cancel') }}</gl-button>
<gl-button
category="primary"
variant="danger"
:href="revokePath"
data-method="put"
class="text-truncate"
data-testid="primary-revoke-btn"
>
<gl-sprintf :message="s__('DeployTokens|Revoke %{name}')">
<template #name>{{ token.name }}</template>
</gl-sprintf>
</gl-button>
</template>
</gl-modal>
</div>
</template>
import Vue from 'vue';
import RevokeButton from './components/revoke_button.vue';
export default () => {
const containers = document.querySelectorAll('.js-deploy-token-revoke-button');
if (!containers.length) {
return false;
}
return containers.forEach((el) => {
const { token, revokePath, buttonClass } = el.dataset;
return new Vue({
el,
provide: {
token: JSON.parse(token),
revokePath,
buttonClass,
},
render(h) {
return h(RevokeButton);
},
});
});
};
import initRevokeButton from '~/deploy_tokens/init_revoke_button';
import initSearchSettings from '~/search_settings';
initSearchSettings();
initRevokeButton();
import initRevokeButton from '~/deploy_tokens/init_revoke_button';
import initSearchSettings from '~/search_settings';
initSearchSettings();
initRevokeButton();
.modal{ id: "revoke-modal-#{token.id}", tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
%h4.modal-title
= s_('DeployTokens|Revoke %{b_start}%{name}%{b_end}?').html_safe % { b_start: '<b>'.html_safe, name: token.name, b_end: '</b>'.html_safe }
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
%span{ "aria-hidden": true } &times;
.modal-body
%p
= s_('DeployTokens|You are about to revoke %{b_start}%{name}%{b_end}.').html_safe % { b_start: '<b>'.html_safe, name: token.name, b_end: '</b>'.html_safe }
= s_('DeployTokens|This action cannot be undone.')
.modal-footer.gl-flex-direction-row
%a{ href: '#', data: { dismiss: 'modal' }, class: 'gl-button btn btn-default' }= _('Cancel')
= link_to s_('DeployTokens|Revoke %{name}') % { name: token.name }, revoke_deploy_token_path(group_or_project, token), method: :put, class: 'gl-button btn btn-danger text-truncate'
......@@ -24,8 +24,9 @@
- else
%span.token-never-expires-label= _('Never')
%td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td= link_to s_('DeployTokens|Revoke'), "#", class: "gl-button btn btn-danger float-right", data: { toggle: "modal", target: "#revoke-modal-#{token.id}"}
= render 'shared/deploy_tokens/revoke_modal', token: token, group_or_project: group_or_project
%td
.js-deploy-token-revoke-button{ data: { button_class: 'float-right', token: token.to_json, revoke_path: revoke_deploy_token_path(group_or_project, token) } }
- else
.settings-message.text-center
= s_('DeployTokens|This %{entity_type} has no active Deploy Tokens.') % { entity_type: group_or_project.class.name.downcase }
......@@ -10484,9 +10484,6 @@ msgstr ""
msgid "DeployTokens|Revoke"
msgstr ""
msgid "DeployTokens|Revoke %{b_start}%{name}%{b_end}?"
msgstr ""
msgid "DeployTokens|Revoke %{name}"
msgstr ""
......@@ -10514,9 +10511,6 @@ msgstr ""
msgid "DeployTokens|Username"
msgstr ""
msgid "DeployTokens|You are about to revoke %{b_start}%{name}%{b_end}."
msgstr ""
msgid "DeployTokens|Your new Deploy Token username"
msgstr ""
......
......@@ -13,16 +13,10 @@ RSpec.describe 'Repository Settings > User sees revoke deploy token modal', :js
sign_in(user)
stub_feature_flags(ajax_new_deploy_token: project)
visit(project_settings_repository_path(project))
click_link('Revoke')
click_button('Revoke')
end
it 'shows the revoke deploy token modal' do
expect(page).to have_content('You are about to revoke')
end
it 'closes the revoke deploy token modal with escape keypress' do
find('.modal.show').send_keys(:escape)
expect(page).not_to have_content('You are about to revoke')
end
end
import { GlModal } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import RevokeButton from '~/deploy_tokens/components/revoke_button.vue';
const mockToken = {
created_at: '2021-03-18T19:13:03.011Z',
deploy_token_type: 'project_type',
expires_at: null,
id: 1,
name: 'testtoken',
read_package_registry: true,
read_registry: false,
read_repository: true,
revoked: false,
token: 'xUVsGDfK4y_Xj5UhqvaH',
token_encrypted: 'JYeg+WK4obIlrhyAYWvBvaY7CNB/U3FPX3cdLrivAly5qToy',
username: 'gitlab+deploy-token-1',
write_package_registry: true,
write_registry: false,
};
const mockRevokePath = '';
describe('RevokeButton', () => {
let wrapper;
let glModalDirective;
function createComponent(injectedProperties = {}) {
glModalDirective = jest.fn();
return extendedWrapper(
mount(RevokeButton, {
provide: {
token: mockToken,
revokePath: mockRevokePath,
...injectedProperties,
},
directives: {
glModal: {
bind(_, { value }) {
glModalDirective(value);
},
},
},
stubs: {
GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
},
}),
);
}
afterEach(() => {
wrapper.destroy();
});
const findRevokeButton = () => wrapper.findByTestId('revoke-button');
const findModal = () => wrapper.findComponent(GlModal);
const findPrimaryModalButton = () => wrapper.findByTestId('primary-revoke-btn');
describe('template', () => {
beforeEach(() => {
wrapper = createComponent();
});
describe('revoke button', () => {
it('displays the revoke button', () => {
expect(findRevokeButton().exists()).toBe(true);
});
it('passes the buttonClass to the button', () => {
wrapper = createComponent({ buttonClass: 'my-revoke-button' });
expect(findRevokeButton().classes()).toContain('my-revoke-button');
});
it('opens the modal', () => {
findRevokeButton().trigger('click');
expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.modalId);
});
});
describe('modal', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('renders the revoke modal', () => {
expect(findModal().exists()).toBe(true);
});
it('displays the token name in the modal title', () => {
expect(findModal().text()).toContain('Revoke testtoken');
});
it('displays the token name in the primary action button"', () => {
expect(findPrimaryModalButton().text()).toBe('Revoke testtoken');
});
it('passes the revokePath to the button', () => {
const revokePath = 'gitlab-org/gitlab-test/-/deploy-tokens/1/revoke';
wrapper = createComponent({ revokePath });
expect(findPrimaryModalButton().attributes('href')).toBe(revokePath);
});
});
});
});
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