Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
02692fb162
commit
75ea027cb6
|
|
@ -0,0 +1,233 @@
|
|||
<script>
|
||||
import {
|
||||
GlButton,
|
||||
GlDrawer,
|
||||
GlFormCheckbox,
|
||||
GlFormCombobox,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
|
||||
import { getContentWrapperHeight } from '~/lib/utils/dom_utils';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import {
|
||||
defaultVariableState,
|
||||
ENVIRONMENT_SCOPE_LINK_TITLE,
|
||||
EXPANDED_VARIABLES_NOTE,
|
||||
FLAG_LINK_TITLE,
|
||||
VARIABLE_ACTIONS,
|
||||
variableOptions,
|
||||
} from '../constants';
|
||||
import CiEnvironmentsDropdown from './ci_environments_dropdown.vue';
|
||||
import { awsTokenList } from './ci_variable_autocomplete_tokens';
|
||||
|
||||
const i18n = {
|
||||
addVariable: s__('CiVariables|Add Variable'),
|
||||
cancel: __('Cancel'),
|
||||
environments: __('Environments'),
|
||||
environmentScopeLinkTitle: ENVIRONMENT_SCOPE_LINK_TITLE,
|
||||
expandedField: s__('CiVariables|Expand variable reference'),
|
||||
expandedDescription: EXPANDED_VARIABLES_NOTE,
|
||||
flags: __('Flags'),
|
||||
flagsLinkTitle: FLAG_LINK_TITLE,
|
||||
key: __('Key'),
|
||||
maskedField: s__('CiVariables|Mask variable'),
|
||||
maskedDescription: s__(
|
||||
'CiVariables|Variable will be masked in job logs. Requires values to meet regular expression requirements.',
|
||||
),
|
||||
protectedField: s__('CiVariables|Protect variable'),
|
||||
protectedDescription: s__(
|
||||
'CiVariables|Export variable to pipelines running on protected branches and tags only.',
|
||||
),
|
||||
type: __('Type'),
|
||||
value: __('Value'),
|
||||
};
|
||||
|
||||
export default {
|
||||
DRAWER_Z_INDEX,
|
||||
components: {
|
||||
CiEnvironmentsDropdown,
|
||||
GlButton,
|
||||
GlDrawer,
|
||||
GlFormCheckbox,
|
||||
GlFormCombobox,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
},
|
||||
inject: ['environmentScopeLink'],
|
||||
props: {
|
||||
areEnvironmentsLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
environments: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
hasEnvScopeQuery: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator(val) {
|
||||
return VARIABLE_ACTIONS.includes(val);
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
key: defaultVariableState.key,
|
||||
variableType: defaultVariableState.variableType,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
getDrawerHeaderHeight() {
|
||||
return getContentWrapperHeight();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('close-form');
|
||||
},
|
||||
},
|
||||
awsTokenList,
|
||||
flagLink: helpPagePath('ci/variables/index', {
|
||||
anchor: 'define-a-cicd-variable-in-the-ui',
|
||||
}),
|
||||
i18n,
|
||||
variableOptions,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-drawer
|
||||
open
|
||||
data-testid="ci-variable-drawer"
|
||||
:header-height="getDrawerHeaderHeight"
|
||||
:z-index="$options.DRAWER_Z_INDEX"
|
||||
@close="close"
|
||||
>
|
||||
<template #title>
|
||||
<h2 class="gl-m-0">{{ $options.i18n.addVariable }}</h2>
|
||||
</template>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.type"
|
||||
label-for="ci-variable-type"
|
||||
class="gl-border-none gl-mb-n5"
|
||||
>
|
||||
<gl-form-select
|
||||
id="ci-variable-type"
|
||||
v-model="variableType"
|
||||
:options="$options.variableOptions"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
class="gl-border-none gl-mb-n5"
|
||||
label-for="ci-variable-env"
|
||||
data-testid="environment-scope"
|
||||
>
|
||||
<template #label>
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<span class="gl-mr-2">
|
||||
{{ $options.i18n.environments }}
|
||||
</span>
|
||||
<gl-link
|
||||
class="gl-display-flex"
|
||||
:title="$options.i18n.environmentScopeLinkTitle"
|
||||
:href="environmentScopeLink"
|
||||
target="_blank"
|
||||
data-testid="environment-scope-link"
|
||||
>
|
||||
<gl-icon name="question-o" :size="14" />
|
||||
</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
<ci-environments-dropdown
|
||||
class="gl-mb-5"
|
||||
:are-environments-loading="areEnvironmentsLoading"
|
||||
:environments="environments"
|
||||
:has-env-scope-query="hasEnvScopeQuery"
|
||||
selected-environment-scope=""
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-form-group class="gl-border-none gl-mb-n8">
|
||||
<template #label>
|
||||
<div class="gl-display-flex gl-align-items-center gl-mb-n3">
|
||||
<span class="gl-mr-2">
|
||||
{{ $options.i18n.flags }}
|
||||
</span>
|
||||
<gl-link
|
||||
class="gl-display-flex"
|
||||
:title="$options.i18n.flagsLinkTitle"
|
||||
:href="$options.flagLink"
|
||||
target="_blank"
|
||||
>
|
||||
<gl-icon name="question-o" :size="14" />
|
||||
</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
<gl-form-checkbox data-testid="ci-variable-protected-checkbox">
|
||||
{{ $options.i18n.protectedField }}
|
||||
<p class="gl-text-secondary">
|
||||
{{ $options.i18n.protectedDescription }}
|
||||
</p>
|
||||
</gl-form-checkbox>
|
||||
<gl-form-checkbox data-testid="ci-variable-masked-checkbox">
|
||||
{{ $options.i18n.maskedField }}
|
||||
<p class="gl-text-secondary">{{ $options.i18n.maskedDescription }}</p>
|
||||
</gl-form-checkbox>
|
||||
<gl-form-checkbox data-testid="ci-variable-expanded-checkbox">
|
||||
{{ $options.i18n.expandedField }}
|
||||
<p class="gl-text-secondary">
|
||||
<gl-sprintf :message="$options.i18n.expandedDescription" class="gl-text-secondary">
|
||||
<template #code="{ content }">
|
||||
<code>{{ content }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</gl-form-checkbox>
|
||||
</gl-form-group>
|
||||
<gl-form-combobox
|
||||
v-model="key"
|
||||
:token-list="$options.awsTokenList"
|
||||
:label-text="$options.i18n.key"
|
||||
class="gl-border-none gl-pb-0! gl-mb-n5"
|
||||
data-testid="pipeline-form-ci-variable-key"
|
||||
data-qa-selector="ci_variable_key_field"
|
||||
/>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.value"
|
||||
label-for="ci-variable-value"
|
||||
class="gl-border-none gl-mb-n2"
|
||||
>
|
||||
<gl-form-textarea
|
||||
id="ci-variable-value"
|
||||
class="gl-border-none gl-font-monospace!"
|
||||
rows="3"
|
||||
max-rows="10"
|
||||
data-testid="pipeline-form-ci-variable-value"
|
||||
data-qa-selector="ci_variable_value_field"
|
||||
spellcheck="false"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<div class="gl-display-flex gl-justify-content-end">
|
||||
<gl-button category="primary" class="gl-mr-3" data-testid="cancel-button" @click="close"
|
||||
>{{ $options.i18n.cancel }}
|
||||
</gl-button>
|
||||
<gl-button category="primary" variant="confirm" data-testid="confirm-button"
|
||||
>{{ $options.i18n.addVariable }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</gl-drawer>
|
||||
</template>
|
||||
|
|
@ -241,7 +241,7 @@ export default {
|
|||
this.resetVariableData();
|
||||
this.resetValidationErrorEvents();
|
||||
|
||||
this.$emit('hideModal');
|
||||
this.$emit('close-form');
|
||||
},
|
||||
resetVariableData() {
|
||||
this.variable = { ...defaultVariableState };
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
<script>
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { ADD_VARIABLE_ACTION, EDIT_VARIABLE_ACTION, VARIABLE_ACTIONS } from '../constants';
|
||||
import CiVariableDrawer from './ci_variable_drawer.vue';
|
||||
import CiVariableTable from './ci_variable_table.vue';
|
||||
import CiVariableModal from './ci_variable_modal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CiVariableDrawer,
|
||||
CiVariableTable,
|
||||
CiVariableModal,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
areEnvironmentsLoading: {
|
||||
type: Boolean,
|
||||
|
|
@ -62,23 +66,32 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
showModal() {
|
||||
showForm() {
|
||||
return VARIABLE_ACTIONS.includes(this.mode);
|
||||
},
|
||||
useDrawerForm() {
|
||||
return this.glFeatures?.ciVariableDrawer;
|
||||
},
|
||||
showDrawer() {
|
||||
return this.showForm && this.useDrawerForm;
|
||||
},
|
||||
showModal() {
|
||||
return this.showForm && !this.useDrawerForm;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addVariable(variable) {
|
||||
this.$emit('add-variable', variable);
|
||||
},
|
||||
closeForm() {
|
||||
this.mode = null;
|
||||
},
|
||||
deleteVariable(variable) {
|
||||
this.$emit('delete-variable', variable);
|
||||
},
|
||||
updateVariable(variable) {
|
||||
this.$emit('update-variable', variable);
|
||||
},
|
||||
hideModal() {
|
||||
this.mode = null;
|
||||
},
|
||||
setSelectedVariable(variable = null) {
|
||||
if (!variable) {
|
||||
this.selectedVariable = {};
|
||||
|
|
@ -118,10 +131,18 @@ export default {
|
|||
:selected-variable="selectedVariable"
|
||||
@add-variable="addVariable"
|
||||
@delete-variable="deleteVariable"
|
||||
@hideModal="hideModal"
|
||||
@close-form="closeForm"
|
||||
@update-variable="updateVariable"
|
||||
@search-environment-scope="$emit('search-environment-scope', $event)"
|
||||
/>
|
||||
<ci-variable-drawer
|
||||
v-if="showDrawer"
|
||||
:are-environments-loading="areEnvironmentsLoading"
|
||||
:has-env-scope-query="hasEnvScopeQuery"
|
||||
:mode="mode"
|
||||
v-on="$listeners"
|
||||
@close-form="closeForm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,14 +7,6 @@ export const SORT_DIRECTIONS = {
|
|||
ASC: 'KEY_ASC',
|
||||
DESC: 'KEY_DESC',
|
||||
};
|
||||
|
||||
// This const will be deprecated once we remove VueX from the section
|
||||
export const displayText = {
|
||||
variableText: __('Variable'),
|
||||
fileText: __('File'),
|
||||
allEnvironmentsText: __('All (default)'),
|
||||
};
|
||||
|
||||
export const variableTypes = {
|
||||
envType: 'ENV_VAR',
|
||||
fileType: 'FILE',
|
||||
|
|
@ -26,8 +18,8 @@ export const allEnvironments = {
|
|||
};
|
||||
|
||||
export const variableOptions = [
|
||||
{ value: variableTypes.envType, text: variableTypes.envType },
|
||||
{ value: variableTypes.fileType, text: variableTypes.fileType },
|
||||
{ value: variableTypes.envType, text: __('Variable (default)') },
|
||||
{ value: variableTypes.fileType, text: __('File') },
|
||||
];
|
||||
|
||||
export const defaultVariableState = {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
import { GlButton, GlFormInput, GlModal, GlSprintf, GlAlert } from '@gitlab/ui';
|
||||
import { GlButton, GlFormInput, GlModal, GlSprintf } from '@gitlab/ui';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
import { sprintf } from '~/locale';
|
||||
import eventHub from '../event_hub';
|
||||
import { I18N_DELETE_TAG_MODAL } from '../constants';
|
||||
|
||||
export default {
|
||||
csrf,
|
||||
|
|
@ -12,7 +13,6 @@ export default {
|
|||
GlButton,
|
||||
GlFormInput,
|
||||
GlSprintf,
|
||||
GlAlert,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -94,57 +94,38 @@ export default {
|
|||
this.$refs.modal.hide();
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
modalTitle: s__('TagsPage|Delete tag. Are you ABSOLUTELY SURE?'),
|
||||
modalTitleProtectedTag: s__('TagsPage|Delete protected tag. Are you ABSOLUTELY SURE?'),
|
||||
modalMessage: s__(
|
||||
"TagsPage|You're about to permanently delete the tag %{strongStart}%{tagName}.%{strongEnd}",
|
||||
),
|
||||
modalMessageProtectedTag: s__(
|
||||
"TagsPage|You're about to permanently delete the protected tag %{strongStart}%{tagName}.%{strongEnd}",
|
||||
),
|
||||
undoneWarning: s__(
|
||||
'TagsPage|After you confirm and select %{strongStart}%{buttonText},%{strongEnd} you cannot recover this tag.',
|
||||
),
|
||||
cancelButtonText: s__('TagsPage|Cancel, keep tag'),
|
||||
confirmationText: s__(
|
||||
'TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} tag cannot be undone. Are you sure?',
|
||||
),
|
||||
confirmationTextProtectedTag: s__('TagsPage|Please type the following to confirm:'),
|
||||
deleteButtonText: s__('TagsPage|Yes, delete tag'),
|
||||
deleteButtonTextProtectedTag: s__('TagsPage|Yes, delete protected tag'),
|
||||
},
|
||||
i18n: I18N_DELETE_TAG_MODAL,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-modal ref="modal" size="sm" :modal-id="modalId" :title="title">
|
||||
<gl-alert class="gl-mb-5" variant="danger" :dismissible="false">
|
||||
<div data-testid="modal-message">
|
||||
<gl-sprintf :message="message">
|
||||
<template #strong="{ content }">
|
||||
<strong> {{ content }} </strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
</gl-alert>
|
||||
<div data-testid="modal-message">
|
||||
<gl-sprintf :message="message">
|
||||
<template #strong="{ content }">
|
||||
<strong> {{ content }} </strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
<p class="gl-mt-4">
|
||||
<gl-sprintf :message="confirmationText">
|
||||
<template #strong="{ content }">
|
||||
<strong>
|
||||
{{ content }}
|
||||
</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
|
||||
<form ref="form" :action="path" method="post">
|
||||
<div v-if="isProtected" class="gl-mt-4">
|
||||
<p>
|
||||
<gl-sprintf :message="undoneWarning">
|
||||
<template #strong="{ content }">
|
||||
<strong> {{ content }} </strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p>
|
||||
<gl-sprintf :message="$options.i18n.confirmationTextProtectedTag">
|
||||
<template #strong="{ content }">
|
||||
{{ content }}
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<code class="gl-white-space-pre-wrap"> {{ tagName }} </code>
|
||||
<code> {{ tagName }} </code>
|
||||
<gl-form-input
|
||||
v-model="enteredTagName"
|
||||
name="delete_tag_input"
|
||||
|
|
@ -155,17 +136,6 @@ export default {
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="gl-mt-4">
|
||||
<gl-sprintf :message="confirmationText">
|
||||
<template #strong="{ content }">
|
||||
<strong>
|
||||
{{ content }}
|
||||
</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<input ref="method" type="hidden" name="_method" value="delete" />
|
||||
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const MODAL_TITLE = s__('TagsPage|Permanently delete tag?');
|
||||
|
||||
export const MODAL_TITLE_PROTECTED_TAG = s__('TagsPage|Permanently delete protected tag?');
|
||||
|
||||
export const MODAL_MESSAGE = s__(
|
||||
'TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} tag cannot be undone.',
|
||||
);
|
||||
|
||||
export const MODAL_MESSAGE_PROTECTED_TAG = s__(
|
||||
'TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} protected tag cannot be undone.',
|
||||
);
|
||||
|
||||
export const CANCEL_BUTTON_TEXT = s__('TagsPage|Cancel, keep tag');
|
||||
|
||||
export const CONFIRMATION_TEXT = s__('TagsPage|Are you sure you want to delete this tag?');
|
||||
|
||||
export const CONFIRMATION_TEXT_PROTECTED_TAG = s__(
|
||||
'TagsPage|Please type the following to confirm:',
|
||||
);
|
||||
|
||||
export const DELETE_BUTTON_TEXT = s__('TagsPage|Yes, delete tag');
|
||||
|
||||
export const DELETE_BUTTON_TEXT_PROTECTED_TAG = s__('TagsPage|Yes, delete protected tag');
|
||||
|
||||
export const I18N_DELETE_TAG_MODAL = {
|
||||
modalTitle: MODAL_TITLE,
|
||||
modalTitleProtectedTag: MODAL_TITLE_PROTECTED_TAG,
|
||||
modalMessage: MODAL_MESSAGE,
|
||||
modalMessageProtectedTag: MODAL_MESSAGE_PROTECTED_TAG,
|
||||
cancelButtonText: CANCEL_BUTTON_TEXT,
|
||||
confirmationText: CONFIRMATION_TEXT,
|
||||
confirmationTextProtectedTag: CONFIRMATION_TEXT_PROTECTED_TAG,
|
||||
deleteButtonText: DELETE_BUTTON_TEXT,
|
||||
deleteButtonTextProtectedTag: DELETE_BUTTON_TEXT_PROTECTED_TAG,
|
||||
};
|
||||
|
|
@ -135,3 +135,16 @@
|
|||
.gl-fill-red-500 {
|
||||
fill: $red-500;
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/3569
|
||||
.gl-mb-n5 {
|
||||
margin-bottom: -$gl-spacing-scale-5;
|
||||
}
|
||||
|
||||
.gl-mb-n7 {
|
||||
margin-bottom: -$gl-spacing-scale-7;
|
||||
}
|
||||
|
||||
.gl-mb-n8 {
|
||||
margin-bottom: -$gl-spacing-scale-8;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:ci_variables_pages, current_user)
|
||||
push_frontend_feature_flag(:ci_variable_drawer, current_user)
|
||||
end
|
||||
|
||||
feature_category :not_owned, [ # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ module Groups
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:ci_variables_pages, current_user)
|
||||
push_frontend_feature_flag(:ci_variable_drawer, current_user)
|
||||
end
|
||||
|
||||
urgency :low
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ module Projects
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:ci_variables_pages, current_user)
|
||||
push_frontend_feature_flag(:ci_variable_drawer, current_user)
|
||||
end
|
||||
|
||||
helper_method :highlight_badge
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'CiGroupEnvironmentScope'
|
||||
description 'Ci/CD environment scope for a group.'
|
||||
|
||||
connection_type_class(Types::Ci::GroupEnvironmentScopeConnectionType)
|
||||
connection_type_class Types::Ci::GroupEnvironmentScopeConnectionType
|
||||
|
||||
field :name, GraphQL::Types::String,
|
||||
null: true,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'CiGroupVariable'
|
||||
description 'CI/CD variables for a group.'
|
||||
|
||||
connection_type_class(Types::Ci::GroupVariableConnectionType)
|
||||
connection_type_class Types::Ci::GroupVariableConnectionType
|
||||
implements(VariableInterface)
|
||||
|
||||
field :environment_scope, GraphQL::Types::String,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module Types
|
|||
|
||||
present_using ::Ci::BuildPresenter
|
||||
|
||||
connection_type_class(Types::LimitedCountableConnectionType)
|
||||
connection_type_class Types::LimitedCountableConnectionType
|
||||
|
||||
expose_permissions Types::PermissionTypes::Ci::Job
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
|
||||
description 'Represents a pipeline schedule'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
expose_permissions Types::PermissionTypes::Ci::PipelineSchedules
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module Types
|
|||
class PipelineType < BaseObject
|
||||
graphql_name 'Pipeline'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_pipeline
|
||||
present_using ::Ci::PipelinePresenter
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'CiProjectVariable'
|
||||
description 'CI/CD variables for a project.'
|
||||
|
||||
connection_type_class(Types::Ci::ProjectVariableConnectionType)
|
||||
connection_type_class Types::Ci::ProjectVariableConnectionType
|
||||
implements(VariableInterface)
|
||||
|
||||
field :environment_scope, GraphQL::Types::String,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'RecentFailures'
|
||||
description 'Recent failure history of a test case.'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :count, GraphQL::Types::Int, null: true,
|
||||
description: 'Number of times the test case has failed in the past 14 days.'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module Types
|
|||
class RunnerManagerType < BaseObject
|
||||
graphql_name 'CiRunnerManager'
|
||||
|
||||
connection_type_class(::Types::CountableConnectionType)
|
||||
connection_type_class ::Types::CountableConnectionType
|
||||
|
||||
authorize :read_runner_manager
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Types
|
|||
graphql_name 'CiRunner'
|
||||
|
||||
edge_type_class(RunnerWebUrlEdge)
|
||||
connection_type_class(RunnerCountableConnectionType)
|
||||
connection_type_class RunnerCountableConnectionType
|
||||
|
||||
authorize :read_runner
|
||||
present_using ::Ci::RunnerPresenter
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'TestCase'
|
||||
description 'Test case in pipeline test report.'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :status,
|
||||
Types::Ci::TestCaseStatusEnum,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'TestSuiteSummary'
|
||||
description 'Test suite summary in a pipeline test report.'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :name, GraphQL::Types::String, null: true,
|
||||
description: 'Name of the test suite.'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
graphql_name 'TestSuite'
|
||||
description 'Test suite in a pipeline test report.'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :name, GraphQL::Types::String, null: true,
|
||||
description: 'Name of the test suite.'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
|
||||
authorize :read_cluster_agent
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :recorded_at,
|
||||
Types::TimeType,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
|
||||
authorize :read_cluster_agent
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :cluster_agent,
|
||||
Types::Clusters::AgentType,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
|
||||
authorize :read_cluster_agent
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :created_at,
|
||||
Types::TimeType,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class IssueType < BaseObject
|
||||
graphql_name 'Issue'
|
||||
|
||||
connection_type_class(Types::IssueConnectionType)
|
||||
connection_type_class Types::IssueConnectionType
|
||||
|
||||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::CurrentUserTodos)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class LabelType < BaseObject
|
||||
graphql_name 'Label'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_label
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class MergeRequestType < BaseObject
|
||||
graphql_name 'MergeRequest'
|
||||
|
||||
connection_type_class(Types::MergeRequestConnectionType)
|
||||
connection_type_class Types::MergeRequestConnectionType
|
||||
|
||||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::CurrentUserTodos)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Types
|
|||
graphql_name 'PackageBase'
|
||||
description 'Represents a package in the Package Registry'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_package
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class ProjectType < BaseObject
|
||||
graphql_name 'Project'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_project
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module Types
|
|||
graphql_name 'Release'
|
||||
description 'Represents a release'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_release
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class SavedReplyType < BaseObject
|
||||
graphql_name 'SavedReply'
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
authorize :read_saved_replies
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module Types
|
|||
description 'Represents the snippet blob'
|
||||
present_using SnippetBlobPresenter
|
||||
|
||||
connection_type_class(Types::Snippets::BlobConnectionType)
|
||||
connection_type_class Types::Snippets::BlobConnectionType
|
||||
|
||||
field :rich_data, GraphQL::Types::String,
|
||||
description: 'Blob highlighted data.',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Types
|
|||
|
||||
authorize :read_terraform_state
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
field :id, GraphQL::Types::ID,
|
||||
null: false,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Types
|
|||
class TimelogType < BaseObject
|
||||
graphql_name 'Timelog'
|
||||
|
||||
connection_type_class(Types::TimeTracking::TimelogConnectionType)
|
||||
connection_type_class Types::TimeTracking::TimelogConnectionType
|
||||
|
||||
authorize :read_issuable
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_variable_drawer
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126197
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/418005
|
||||
milestone: '16.3'
|
||||
type: development
|
||||
group: group::pipeline security
|
||||
default_enabled: false
|
||||
|
|
@ -9886,6 +9886,9 @@ msgstr ""
|
|||
msgid "CiStatus|running"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Add Variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Attributes"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9898,9 +9901,15 @@ msgstr ""
|
|||
msgid "CiVariables|Environments"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Expand variable reference"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Expanded"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Export variable to pipelines running on protected branches and tags only."
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|File"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9916,6 +9925,9 @@ msgstr ""
|
|||
msgid "CiVariables|Key"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Mask variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Masked"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9925,6 +9937,9 @@ msgstr ""
|
|||
msgid "CiVariables|Maximum number of variables reached."
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Protect variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Protected"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9964,6 +9979,9 @@ msgstr ""
|
|||
msgid "CiVariables|Value"
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Variable will be masked in job logs. Requires values to meet regular expression requirements."
|
||||
msgstr ""
|
||||
|
||||
msgid "CiVariables|Variables"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -45544,7 +45562,7 @@ msgstr ""
|
|||
msgid "Tags:"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|After you confirm and select %{strongStart}%{buttonText},%{strongEnd} you cannot recover this tag."
|
||||
msgid "TagsPage|Are you sure you want to delete this tag?"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Browse commits"
|
||||
|
|
@ -45571,16 +45589,13 @@ msgstr ""
|
|||
msgid "TagsPage|Delete protected tag"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Delete protected tag. Are you ABSOLUTELY SURE?"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Delete tag"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Delete tag. Are you ABSOLUTELY SURE?"
|
||||
msgid "TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} protected tag cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} tag cannot be undone. Are you sure?"
|
||||
msgid "TagsPage|Deleting the %{strongStart}%{tagName}%{strongEnd} tag cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Do you want to create a release with the new tag? You can do that in the %{link_start}New release page%{link_end}."
|
||||
|
|
@ -45610,6 +45625,12 @@ msgstr ""
|
|||
msgid "TagsPage|Optionally, add a message to the tag. Leaving this blank creates a %{link_start}lightweight tag.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Permanently delete protected tag?"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Permanently delete tag?"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|Please type the following to confirm:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -45637,12 +45658,6 @@ msgstr ""
|
|||
msgid "TagsPage|Yes, delete tag"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|You're about to permanently delete the protected tag %{strongStart}%{tagName}.%{strongEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|You're about to permanently delete the tag %{strongStart}%{tagName}.%{strongEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "TagsPage|protected"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -50660,6 +50675,9 @@ msgstr ""
|
|||
msgid "Variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "Variable (default)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Variable name '%{variable}' must not start with '%{prefix}'"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ RSpec.describe 'Instance variables', :js, feature_category: :secrets_management
|
|||
sign_in(admin)
|
||||
gitlab_enable_admin_mode_sign_in(admin)
|
||||
|
||||
stub_feature_flags(ci_variable_drawer: false)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
|
@ -29,4 +30,14 @@ RSpec.describe 'Instance variables', :js, feature_category: :secrets_management
|
|||
|
||||
it_behaves_like 'variable list', is_admin: true
|
||||
end
|
||||
|
||||
context 'when ci_variable_drawer FF is enabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variable_drawer: true)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'variable list drawer', is_admin: true
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ RSpec.describe 'Group variables', :js, feature_category: :secrets_management do
|
|||
before do
|
||||
group.add_owner(user)
|
||||
gitlab_sign_in(user)
|
||||
|
||||
stub_feature_flags(ci_variable_drawer: false)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
|
@ -27,4 +29,14 @@ RSpec.describe 'Group variables', :js, feature_category: :secrets_management do
|
|||
|
||||
it_behaves_like 'variable list'
|
||||
end
|
||||
|
||||
context 'when ci_variable_drawer FF is enabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variable_drawer: true)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'variable list drawer'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ RSpec.describe 'Project variables', :js, feature_category: :secrets_management d
|
|||
sign_in(user)
|
||||
project.add_maintainer(user)
|
||||
project.variables << variable
|
||||
|
||||
stub_feature_flags(ci_variable_drawer: false)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
|
@ -49,4 +51,14 @@ RSpec.describe 'Project variables', :js, feature_category: :secrets_management d
|
|||
expect(find('.js-ci-variable-row:first-child [data-label="Environments"]').text).to eq('review/*')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ci_variable_drawer FF is enabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variable_drawer: true)
|
||||
visit page_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'variable list drawer'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import { GlDrawer, GlFormSelect } from '@gitlab/ui';
|
||||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue';
|
||||
import {
|
||||
ADD_VARIABLE_ACTION,
|
||||
variableOptions,
|
||||
variableTypes,
|
||||
} from '~/ci/ci_variable_list/constants';
|
||||
|
||||
describe('CI Variable Drawer', () => {
|
||||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
areEnvironmentsLoading: false,
|
||||
hasEnvScopeQuery: true,
|
||||
mode: ADD_VARIABLE_ACTION,
|
||||
};
|
||||
|
||||
const createComponent = ({ mountFn = shallowMountExtended, props = {} } = {}) => {
|
||||
wrapper = mountFn(CiVariableDrawer, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
environmentScopeLink: '/help/environments',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findDrawer = () => wrapper.findComponent(GlDrawer);
|
||||
const findTypeDropdown = () => wrapper.findComponent(GlFormSelect);
|
||||
|
||||
describe('validations', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ mountFn: mountExtended });
|
||||
});
|
||||
|
||||
describe('type dropdown', () => {
|
||||
it('adds each type option as a dropdown item', () => {
|
||||
expect(findTypeDropdown().findAll('option')).toHaveLength(variableOptions.length);
|
||||
|
||||
variableOptions.forEach((v) => {
|
||||
expect(findTypeDropdown().text()).toContain(v.text);
|
||||
});
|
||||
});
|
||||
|
||||
it('is set to environment variable by default', () => {
|
||||
expect(findTypeDropdown().findAll('option').at(0).attributes('value')).toBe(
|
||||
variableTypes.envType,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawer events', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('emits `close-form` when closing the drawer', async () => {
|
||||
expect(wrapper.emitted('close-form')).toBeUndefined();
|
||||
|
||||
await findDrawer().vm.$emit('close');
|
||||
|
||||
expect(wrapper.emitted('close-form')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -122,9 +122,9 @@ describe('Ci variable modal', () => {
|
|||
expect(wrapper.emitted('add-variable')).toEqual([[currentVariable]]);
|
||||
});
|
||||
|
||||
it('Dispatches the `hideModal` event when dismissing', () => {
|
||||
it('Dispatches the `close-form` event when dismissing', () => {
|
||||
findModal().vm.$emit('hidden');
|
||||
expect(wrapper.emitted('hideModal')).toEqual([[]]);
|
||||
expect(wrapper.emitted('close-form')).toEqual([[]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -313,9 +313,9 @@ describe('Ci variable modal', () => {
|
|||
expect(wrapper.emitted('update-variable')).toEqual([[variable]]);
|
||||
});
|
||||
|
||||
it('Propagates the `hideModal` event', () => {
|
||||
it('Propagates the `close-form` event', () => {
|
||||
findModal().vm.$emit('hidden');
|
||||
expect(wrapper.emitted('hideModal')).toEqual([[]]);
|
||||
expect(wrapper.emitted('close-form')).toEqual([[]]);
|
||||
});
|
||||
|
||||
it('dispatches `delete-variable` with correct variable to delete', () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import CiVariableSettings from '~/ci/ci_variable_list/components/ci_variable_settings.vue';
|
||||
import ciVariableModal from '~/ci/ci_variable_list/components/ci_variable_modal.vue';
|
||||
import ciVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue';
|
||||
import CiVariableModal from '~/ci/ci_variable_list/components/ci_variable_modal.vue';
|
||||
import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue';
|
||||
import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue';
|
||||
|
||||
import {
|
||||
ADD_VARIABLE_ACTION,
|
||||
EDIT_VARIABLE_ACTION,
|
||||
|
|
@ -27,15 +29,22 @@ describe('Ci variable table', () => {
|
|||
variables: mockVariablesWithScopes(projectString),
|
||||
};
|
||||
|
||||
const findCiVariableTable = () => wrapper.findComponent(ciVariableTable);
|
||||
const findCiVariableModal = () => wrapper.findComponent(ciVariableModal);
|
||||
const findCiVariableDrawer = () => wrapper.findComponent(CiVariableDrawer);
|
||||
const findCiVariableTable = () => wrapper.findComponent(CiVariableTable);
|
||||
const findCiVariableModal = () => wrapper.findComponent(CiVariableModal);
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
const createComponent = ({ props = {}, featureFlags = {} } = {}) => {
|
||||
wrapper = shallowMount(CiVariableSettings, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
glFeatures: {
|
||||
ciVariableDrawer: false,
|
||||
...featureFlags,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -70,51 +79,51 @@ describe('Ci variable table', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('modal mode', () => {
|
||||
describe.each`
|
||||
bool | flagStatus | elementName | findElement
|
||||
${false} | ${'disabled'} | ${'modal'} | ${findCiVariableModal}
|
||||
${true} | ${'enabled'} | ${'drawer'} | ${findCiVariableDrawer}
|
||||
`('when ciVariableDrawer feature flag is $flagStatus', ({ bool, elementName, findElement }) => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
createComponent({ featureFlags: { ciVariableDrawer: bool } });
|
||||
});
|
||||
|
||||
it('passes down ADD mode when receiving an empty variable', async () => {
|
||||
it(`${elementName} is hidden by default`, () => {
|
||||
expect(findElement().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it(`shows ${elementName} when adding a new variable`, async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable');
|
||||
|
||||
expect(findCiVariableModal().props('mode')).toBe(ADD_VARIABLE_ACTION);
|
||||
expect(findElement().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('passes down EDIT mode when receiving a variable', async () => {
|
||||
it(`shows ${elementName} when updating a variable`, async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable', newVariable);
|
||||
|
||||
expect(findCiVariableModal().props('mode')).toBe(EDIT_VARIABLE_ACTION);
|
||||
});
|
||||
});
|
||||
|
||||
describe('variable modal', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
expect(findElement().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('is hidden by default', () => {
|
||||
expect(findCiVariableModal().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('shows modal when adding a new variable', async () => {
|
||||
it(`hides ${elementName} when closing the form`, async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable');
|
||||
|
||||
expect(findCiVariableModal().exists()).toBe(true);
|
||||
expect(findElement().isVisible()).toBe(true);
|
||||
|
||||
await findElement().vm.$emit('close-form');
|
||||
|
||||
expect(findElement().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('shows modal when updating a variable', async () => {
|
||||
it(`passes down ADD mode to ${elementName} when receiving an empty variable`, async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable');
|
||||
|
||||
expect(findElement().props('mode')).toBe(ADD_VARIABLE_ACTION);
|
||||
});
|
||||
|
||||
it(`passes down EDIT mode to ${elementName} when receiving a variable`, async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable', newVariable);
|
||||
|
||||
expect(findCiVariableModal().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('hides modal when receiving the event from the modal', async () => {
|
||||
await findCiVariableTable().vm.$emit('set-selected-variable');
|
||||
|
||||
await findCiVariableModal().vm.$emit('hideModal');
|
||||
|
||||
expect(findCiVariableModal().exists()).toBe(false);
|
||||
expect(findElement().props('mode')).toBe(EDIT_VARIABLE_ACTION);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import DeleteTagModal from '~/tags/components/delete_tag_modal.vue';
|
||||
import eventHub from '~/tags/event_hub';
|
||||
import { I18N_DELETE_TAG_MODAL } from '~/tags/constants';
|
||||
|
||||
let wrapper;
|
||||
|
||||
|
|
@ -52,18 +53,17 @@ const findForm = () => wrapper.find('form');
|
|||
|
||||
describe('Delete tag modal', () => {
|
||||
describe('Deleting a regular tag', () => {
|
||||
const expectedTitle = 'Delete tag. Are you ABSOLUTELY SURE?';
|
||||
const expectedMessage = "You're about to permanently delete the tag test-tag.";
|
||||
const expectedMessage = 'Deleting the test-tag tag cannot be undone.';
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders the modal correctly', () => {
|
||||
expect(findModal().props('title')).toBe(expectedTitle);
|
||||
expect(findModal().props('title')).toBe(I18N_DELETE_TAG_MODAL.modalTitle);
|
||||
expect(findModalMessage().text()).toMatchInterpolatedText(expectedMessage);
|
||||
expect(findCancelButton().text()).toBe('Cancel, keep tag');
|
||||
expect(findDeleteButton().text()).toBe('Yes, delete tag');
|
||||
expect(findCancelButton().text()).toBe(I18N_DELETE_TAG_MODAL.cancelButtonText);
|
||||
expect(findDeleteButton().text()).toBe(I18N_DELETE_TAG_MODAL.deleteButtonText);
|
||||
expect(findForm().attributes('action')).toBe(path);
|
||||
});
|
||||
|
||||
|
|
@ -92,11 +92,8 @@ describe('Delete tag modal', () => {
|
|||
});
|
||||
|
||||
describe('Deleting a protected tag (for owner or maintainer)', () => {
|
||||
const expectedTitleProtected = 'Delete protected tag. Are you ABSOLUTELY SURE?';
|
||||
const expectedMessageProtected =
|
||||
"You're about to permanently delete the protected tag test-tag.";
|
||||
const expectedConfirmationText =
|
||||
'After you confirm and select Yes, delete protected tag, you cannot recover this tag. Please type the following to confirm: test-tag';
|
||||
const expectedMessage = 'Deleting the test-tag protected tag cannot be undone.';
|
||||
const expectedConfirmationText = 'Please type the following to confirm: test-tag';
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({ isProtected: true });
|
||||
|
|
@ -104,11 +101,11 @@ describe('Delete tag modal', () => {
|
|||
|
||||
describe('rendering the modal correctly for a protected tag', () => {
|
||||
it('sets the modal title for a protected tag', () => {
|
||||
expect(findModal().props('title')).toBe(expectedTitleProtected);
|
||||
expect(findModal().props('title')).toBe(I18N_DELETE_TAG_MODAL.modalTitleProtectedTag);
|
||||
});
|
||||
|
||||
it('renders the correct text in the modal message', () => {
|
||||
expect(findModalMessage().text()).toMatchInterpolatedText(expectedMessageProtected);
|
||||
expect(findModalMessage().text()).toMatchInterpolatedText(expectedMessage);
|
||||
});
|
||||
|
||||
it('renders the protected tag name confirmation form with expected text and action', () => {
|
||||
|
|
@ -117,8 +114,8 @@ describe('Delete tag modal', () => {
|
|||
});
|
||||
|
||||
it('renders the buttons with the correct button text', () => {
|
||||
expect(findCancelButton().text()).toBe('Cancel, keep tag');
|
||||
expect(findDeleteButton().text()).toBe('Yes, delete protected tag');
|
||||
expect(findCancelButton().text()).toBe(I18N_DELETE_TAG_MODAL.cancelButtonText);
|
||||
expect(findDeleteButton().text()).toBe(I18N_DELETE_TAG_MODAL.deleteButtonTextProtectedTag);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ RSpec.describe SwapTodosNoteIdToBigintForSelfManaged, feature_category: :databas
|
|||
connection.execute('ALTER TABLE todos ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer')
|
||||
end
|
||||
|
||||
after do
|
||||
connection = described_class.new.connection
|
||||
connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
|
||||
end
|
||||
|
||||
it 'does not swap the columns' do
|
||||
# rubocop: disable RSpec/AnyInstanceOf
|
||||
allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
|
||||
|
|
@ -115,6 +120,11 @@ RSpec.describe SwapTodosNoteIdToBigintForSelfManaged, feature_category: :databas
|
|||
BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
|
||||
end
|
||||
|
||||
after do
|
||||
connection = described_class.new.connection
|
||||
connection.execute('ALTER TABLE todos DROP COLUMN IF EXISTS note_id_convert_to_bigint')
|
||||
end
|
||||
|
||||
it 'swaps the columns' do
|
||||
# rubocop: disable RSpec/AnyInstanceOf
|
||||
allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'variable list drawer' do
|
||||
it 'adds a new CI variable' do
|
||||
click_button('Add variable')
|
||||
|
||||
# For now, we just check that the drawer is displayed
|
||||
expect(page).to have_selector('[data-testid="ci-variable-drawer"]')
|
||||
|
||||
# TODO: Add tests for ADDING a variable via drawer when feature is available
|
||||
end
|
||||
|
||||
it 'edits a variable' do
|
||||
page.within('[data-testid="ci-variable-table"]') do
|
||||
click_button('Edit')
|
||||
end
|
||||
|
||||
# For now, we just check that the drawer is displayed
|
||||
expect(page).to have_selector('[data-testid="ci-variable-drawer"]')
|
||||
|
||||
# TODO: Add tests for EDITING a variable via drawer when feature is available
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue