Commit 98fe8ff9 authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch '333679-hide-the-runner-registration-token-from-the-screen' into 'master'

Mask runner registration token

See merge request gitlab-org/gitlab!65551
parents 52db1ada ddc541bb
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlButton,
},
props: {
value: {
type: String,
required: false,
default: '',
},
},
data() {
return {
isMasked: true,
};
},
computed: {
label() {
if (this.isMasked) {
return __('Click to reveal');
}
return __('Click to hide');
},
icon() {
if (this.isMasked) {
return 'eye';
}
return 'eye-slash';
},
displayedValue() {
if (this.isMasked && this.value?.length) {
return '*'.repeat(this.value.length);
}
return this.value;
},
},
methods: {
toggleMasked() {
this.isMasked = !this.isMasked;
},
},
};
</script>
<template>
<span
>{{ displayedValue }}
<gl-button
:aria-label="label"
:icon="icon"
class="gl-text-body!"
data-testid="toggle-masked"
variant="link"
@click="toggleMasked"
/>
</span>
</template>
<script> <script>
import { GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; import { GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue'; import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import RunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue'; import RunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
...@@ -11,6 +12,7 @@ export default { ...@@ -11,6 +12,7 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
ClipboardButton, ClipboardButton,
MaskedValue,
RunnerInstructions, RunnerInstructions,
RunnerRegistrationTokenReset, RunnerRegistrationTokenReset,
}, },
...@@ -92,7 +94,9 @@ export default { ...@@ -92,7 +94,9 @@ export default {
{{ __('And this registration token:') }} {{ __('And this registration token:') }}
<br /> <br />
<code data-testid="registration-token">{{ currentRegistrationToken }}</code> <code data-testid="registration-token"
><masked-value :value="currentRegistrationToken"
/></code>
<clipboard-button :title="__('Copy token')" :text="currentRegistrationToken" /> <clipboard-button :title="__('Copy token')" :text="currentRegistrationToken" />
</li> </li>
</ol> </ol>
......
...@@ -6840,6 +6840,12 @@ msgstr "" ...@@ -6840,6 +6840,12 @@ msgstr ""
msgid "Click to expand text" msgid "Click to expand text"
msgstr "" msgstr ""
msgid "Click to hide"
msgstr ""
msgid "Click to reveal"
msgstr ""
msgid "Client authentication certificate" msgid "Client authentication certificate"
msgstr "" msgstr ""
......
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
const mockSecret = '01234567890';
const mockMasked = '***********';
describe('MaskedValue', () => {
let wrapper;
const findButton = () => wrapper.findComponent(GlButton);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(MaskedValue, {
propsData: {
value: mockSecret,
...props,
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('Displays masked value by default', () => {
expect(wrapper.text()).toBe(mockMasked);
});
describe('When the icon is clicked', () => {
beforeEach(() => {
findButton().vm.$emit('click');
});
it('Displays the actual value', () => {
expect(wrapper.text()).toBe(mockSecret);
expect(wrapper.text()).not.toBe(mockMasked);
});
it('When user clicks again, displays masked value', async () => {
await findButton().vm.$emit('click');
expect(wrapper.text()).toBe(mockMasked);
expect(wrapper.text()).not.toBe(mockSecret);
});
});
});
...@@ -3,6 +3,7 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -3,6 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import MaskedValue from '~/runner/components/helpers/masked_value.vue';
import RunnerManualSetupHelp from '~/runner/components/runner_manual_setup_help.vue'; import RunnerManualSetupHelp from '~/runner/components/runner_manual_setup_help.vue';
import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue'; import RunnerRegistrationTokenReset from '~/runner/components/runner_registration_token_reset.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants'; import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
...@@ -37,6 +38,7 @@ describe('RunnerManualSetupHelp', () => { ...@@ -37,6 +38,7 @@ describe('RunnerManualSetupHelp', () => {
...props, ...props,
}, },
stubs: { stubs: {
MaskedValue,
GlSprintf, GlSprintf,
}, },
}), }),
...@@ -93,7 +95,11 @@ describe('RunnerManualSetupHelp', () => { ...@@ -93,7 +95,11 @@ describe('RunnerManualSetupHelp', () => {
expect(findRunnerInstructions().exists()).toBe(true); expect(findRunnerInstructions().exists()).toBe(true);
}); });
it('Displays the registration token', () => { it('Displays the registration token', async () => {
findRegistrationToken().find('[data-testid="toggle-masked"]').vm.$emit('click');
await nextTick();
expect(findRegistrationToken().text()).toBe(mockRegistrationToken); expect(findRegistrationToken().text()).toBe(mockRegistrationToken);
expect(findClipboardButtons().at(1).props('text')).toBe(mockRegistrationToken); expect(findClipboardButtons().at(1).props('text')).toBe(mockRegistrationToken);
}); });
...@@ -105,6 +111,7 @@ describe('RunnerManualSetupHelp', () => { ...@@ -105,6 +111,7 @@ describe('RunnerManualSetupHelp', () => {
it('Replaces the runner reset button', async () => { it('Replaces the runner reset button', async () => {
const mockNewRegistrationToken = 'NEW_MOCK_REGISTRATION_TOKEN'; const mockNewRegistrationToken = 'NEW_MOCK_REGISTRATION_TOKEN';
findRegistrationToken().find('[data-testid="toggle-masked"]').vm.$emit('click');
findRunnerRegistrationTokenReset().vm.$emit('tokenReset', mockNewRegistrationToken); findRunnerRegistrationTokenReset().vm.$emit('tokenReset', mockNewRegistrationToken);
await nextTick(); await nextTick();
......
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