Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
af77437762
commit
0ab17699c8
|
@ -11,6 +11,7 @@ import {
|
||||||
GlSprintf,
|
GlSprintf,
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
import { s__, __ } from '~/locale';
|
import { s__, __ } from '~/locale';
|
||||||
|
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
import Tracking from '~/tracking';
|
import Tracking from '~/tracking';
|
||||||
import { trackAlertIntegrationsViewsOptions, integrationToDeleteDefault } from '../constants';
|
import { trackAlertIntegrationsViewsOptions, integrationToDeleteDefault } from '../constants';
|
||||||
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
|
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
|
||||||
|
@ -48,6 +49,7 @@ export default {
|
||||||
GlTooltip: GlTooltipDirective,
|
GlTooltip: GlTooltipDirective,
|
||||||
GlModal: GlModalDirective,
|
GlModal: GlModalDirective,
|
||||||
},
|
},
|
||||||
|
mixins: [glFeatureFlagsMixin()],
|
||||||
props: {
|
props: {
|
||||||
integrations: {
|
integrations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -96,7 +98,7 @@ export default {
|
||||||
tbodyTrClass(item) {
|
tbodyTrClass(item) {
|
||||||
return {
|
return {
|
||||||
[bodyTrClass]: this.integrations.length,
|
[bodyTrClass]: this.integrations.length,
|
||||||
'gl-bg-blue-50': item?.id === this.currentIntegration?.id,
|
'gl-bg-blue-50': (item !== null && item.id) === this.currentIntegration?.id,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
trackPageViews() {
|
trackPageViews() {
|
||||||
|
@ -150,7 +152,7 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell(actions)="{ item }">
|
<template #cell(actions)="{ item }">
|
||||||
<gl-button-group>
|
<gl-button-group v-if="glFeatures.httpIntegrationsList">
|
||||||
<gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
|
<gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
|
||||||
<gl-button
|
<gl-button
|
||||||
v-gl-modal.deleteIntegration
|
v-gl-modal.deleteIntegration
|
||||||
|
|
|
@ -23,7 +23,9 @@ import {
|
||||||
integrationTypesNew,
|
integrationTypesNew,
|
||||||
JSON_VALIDATE_DELAY,
|
JSON_VALIDATE_DELAY,
|
||||||
targetPrometheusUrlPlaceholder,
|
targetPrometheusUrlPlaceholder,
|
||||||
|
targetOpsgenieUrlPlaceholder,
|
||||||
typeSet,
|
typeSet,
|
||||||
|
sectionHash,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
// Mocks will be removed when integrating with BE is ready
|
// Mocks will be removed when integrating with BE is ready
|
||||||
// data format is defined and will be the same as mocked (maybe with some minor changes)
|
// data format is defined and will be the same as mocked (maybe with some minor changes)
|
||||||
|
@ -31,7 +33,10 @@ import {
|
||||||
import mockedCustomMapping from './mocks/parsedMapping.json';
|
import mockedCustomMapping from './mocks/parsedMapping.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
targetPrometheusUrlPlaceholder,
|
placeholders: {
|
||||||
|
prometheus: targetPrometheusUrlPlaceholder,
|
||||||
|
opsgenie: targetOpsgenieUrlPlaceholder,
|
||||||
|
},
|
||||||
JSON_VALIDATE_DELAY,
|
JSON_VALIDATE_DELAY,
|
||||||
typeSet,
|
typeSet,
|
||||||
i18n: {
|
i18n: {
|
||||||
|
@ -84,6 +89,13 @@ export default {
|
||||||
'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
|
'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
opsgenie: {
|
||||||
|
label: s__('AlertSettings|2. Add link to your Opsgenie alert list'),
|
||||||
|
info: s__(
|
||||||
|
'AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your exisiting Opsgenie instance. By selecting this option, you cannot recieve alerts from any other source in GitLab; it will effectivley be turing Alerts within GitLab off as a feature.',
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -111,6 +123,10 @@ export default {
|
||||||
prometheus: {
|
prometheus: {
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
opsgenie: {
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mixins: [glFeatureFlagsMixin()],
|
mixins: [glFeatureFlagsMixin()],
|
||||||
props: {
|
props: {
|
||||||
|
@ -131,7 +147,6 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedIntegration: integrationTypesNew[0].value,
|
selectedIntegration: integrationTypesNew[0].value,
|
||||||
options: integrationTypesNew,
|
|
||||||
active: false,
|
active: false,
|
||||||
formVisible: false,
|
formVisible: false,
|
||||||
integrationTestPayload: {
|
integrationTestPayload: {
|
||||||
|
@ -142,18 +157,33 @@ export default {
|
||||||
customMapping: null,
|
customMapping: null,
|
||||||
parsingPayload: false,
|
parsingPayload: false,
|
||||||
currentIntegration: null,
|
currentIntegration: null,
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
isManagingOpsgenie: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
jsonIsValid() {
|
jsonIsValid() {
|
||||||
return this.integrationTestPayload.error === null;
|
return this.integrationTestPayload.error === null;
|
||||||
},
|
},
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
disabledIntegrations() {
|
||||||
|
return this.opsgenie.active ? [typeSet.http, typeSet.prometheus] : [typeSet.opsgenie];
|
||||||
|
},
|
||||||
|
options() {
|
||||||
|
return integrationTypesNew.map(el => ({
|
||||||
|
...el,
|
||||||
|
disabled: this.disabledIntegrations.includes(el.value),
|
||||||
|
}));
|
||||||
|
},
|
||||||
selectedIntegrationType() {
|
selectedIntegrationType() {
|
||||||
switch (this.selectedIntegration) {
|
switch (this.selectedIntegration) {
|
||||||
case this.$options.typeSet.http:
|
case typeSet.http:
|
||||||
return this.generic;
|
return this.generic;
|
||||||
case this.$options.typeSet.prometheus:
|
case typeSet.prometheus:
|
||||||
return this.prometheus;
|
return this.prometheus;
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
case typeSet.opsgenie:
|
||||||
|
return this.opsgenie;
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -214,6 +244,30 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.formVisible = true;
|
this.formVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
if (this.opsgenie.active && this.selectedIntegration === typeSet.opsgenie) {
|
||||||
|
this.isManagingOpsgenie = true;
|
||||||
|
this.active = this.opsgenie.active;
|
||||||
|
this.integrationForm.apiUrl = this.opsgenie.opsgenieMvcTargetUrl;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
submitWithOpsgenie() {
|
||||||
|
return service
|
||||||
|
.updateGenericActive({
|
||||||
|
endpoint: this.opsgenie.formPath,
|
||||||
|
params: {
|
||||||
|
service: {
|
||||||
|
opsgenie_mvc_target_url: this.integrationForm.apiUrl,
|
||||||
|
opsgenie_mvc_enabled: this.active,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
window.location.hash = sectionHash;
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
submitWithTestPayload() {
|
submitWithTestPayload() {
|
||||||
return service
|
return service
|
||||||
|
@ -226,9 +280,14 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
if (this.isManagingOpsgenie) {
|
||||||
|
return this.submitWithOpsgenie();
|
||||||
|
}
|
||||||
|
|
||||||
const { name, apiUrl } = this.integrationForm;
|
const { name, apiUrl } = this.integrationForm;
|
||||||
const variables =
|
const variables =
|
||||||
this.selectedIntegration === this.$options.typeSet.http
|
this.selectedIntegration === typeSet.http
|
||||||
? { name, active: this.active }
|
? { name, active: this.active }
|
||||||
: { apiUrl, active: this.active };
|
: { apiUrl, active: this.active };
|
||||||
const integrationPayload = { type: this.selectedIntegration, variables };
|
const integrationPayload = { type: this.selectedIntegration, variables };
|
||||||
|
@ -312,7 +371,6 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
|
<gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
|
||||||
<h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
|
<h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
|
||||||
|
|
||||||
<gl-form-group
|
<gl-form-group
|
||||||
id="integration-type"
|
id="integration-type"
|
||||||
:label="$options.i18n.integrationFormSteps.step1.label"
|
:label="$options.i18n.integrationFormSteps.step1.label"
|
||||||
|
@ -340,170 +398,206 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
<gl-collapse v-model="formVisible" class="gl-mt-3">
|
<gl-collapse v-model="formVisible" class="gl-mt-3">
|
||||||
<gl-form-group
|
<!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
|
||||||
id="name-integration"
|
<div v-if="isManagingOpsgenie">
|
||||||
:label="$options.i18n.integrationFormSteps.step2.label"
|
<gl-form-group
|
||||||
label-for="name-integration"
|
id="integration-webhook"
|
||||||
>
|
:label="$options.i18n.integrationFormSteps.opsgenie.label"
|
||||||
<gl-form-input
|
label-for="integration-webhook"
|
||||||
v-model="integrationForm.name"
|
>
|
||||||
type="text"
|
<span class="gl-my-4">
|
||||||
:placeholder="$options.i18n.integrationFormSteps.step2.placeholder"
|
{{ $options.i18n.integrationFormSteps.opsgenie.info }}
|
||||||
/>
|
|
||||||
</gl-form-group>
|
|
||||||
<gl-form-group
|
|
||||||
id="integration-webhook"
|
|
||||||
:label="$options.i18n.integrationFormSteps.step3.label"
|
|
||||||
label-for="integration-webhook"
|
|
||||||
>
|
|
||||||
<alert-settings-form-help-block
|
|
||||||
:message="$options.i18n.integrationFormSteps.step3.help"
|
|
||||||
link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<gl-toggle
|
|
||||||
v-model="active"
|
|
||||||
:is-loading="loading"
|
|
||||||
:label="__('Active')"
|
|
||||||
class="gl-my-4 gl-font-weight-normal"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div v-if="selectedIntegration === $options.typeSet.prometheus" class="gl-my-4">
|
|
||||||
<span>
|
|
||||||
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.label }}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<gl-form-input
|
<gl-toggle
|
||||||
id="integration-apiUrl"
|
v-model="active"
|
||||||
v-model="integrationForm.apiUrl"
|
:is-loading="loading"
|
||||||
type="text"
|
:label="__('Active')"
|
||||||
:placeholder="$options.targetPrometheusUrlPlaceholder"
|
class="gl-my-4 gl-font-weight-normal"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span class="gl-text-gray-400">
|
<gl-form-input
|
||||||
|
id="opsgenie-opsgenieMvcTargetUrl"
|
||||||
|
v-model="integrationForm.apiUrl"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$options.placeholders.opsgenie"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="gl-text-gray-400 gl-my-1">
|
||||||
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
|
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</gl-form-group>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<gl-form-group
|
||||||
|
id="name-integration"
|
||||||
|
:label="$options.i18n.integrationFormSteps.step2.label"
|
||||||
|
label-for="name-integration"
|
||||||
|
>
|
||||||
|
<gl-form-input
|
||||||
|
v-model="integrationForm.name"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$options.i18n.integrationFormSteps.step2.placeholder"
|
||||||
|
/>
|
||||||
|
</gl-form-group>
|
||||||
|
<gl-form-group
|
||||||
|
id="integration-webhook"
|
||||||
|
:label="$options.i18n.integrationFormSteps.step3.label"
|
||||||
|
label-for="integration-webhook"
|
||||||
|
>
|
||||||
|
<alert-settings-form-help-block
|
||||||
|
:message="$options.i18n.integrationFormSteps.step3.help"
|
||||||
|
link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="gl-my-4">
|
<gl-toggle
|
||||||
<span>
|
v-model="active"
|
||||||
{{ s__('AlertSettings|Webhook URL') }}
|
:is-loading="loading"
|
||||||
</span>
|
:label="__('Active')"
|
||||||
|
class="gl-my-4 gl-font-weight-normal"
|
||||||
|
/>
|
||||||
|
|
||||||
<gl-form-input-group id="url" readonly :value="integrationForm.url">
|
<div v-if="selectedIntegration === $options.typeSet.prometheus" class="gl-my-4">
|
||||||
<template #append>
|
<span>
|
||||||
<clipboard-button
|
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.label }}
|
||||||
:text="integrationForm.url || ''"
|
</span>
|
||||||
:title="__('Copy')"
|
|
||||||
class="gl-m-0!"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</gl-form-input-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gl-my-4">
|
<gl-form-input
|
||||||
<span>
|
id="integration-apiUrl"
|
||||||
{{ $options.i18n.integrationFormSteps.step3.info }}
|
v-model="integrationForm.apiUrl"
|
||||||
</span>
|
type="text"
|
||||||
|
:placeholder="$options.targetPrometheusUrlPlaceholder"
|
||||||
|
/>
|
||||||
|
|
||||||
<gl-form-input-group
|
<span class="gl-text-gray-400">
|
||||||
id="authorization-key"
|
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
|
||||||
class="gl-mb-2"
|
</span>
|
||||||
readonly
|
</div>
|
||||||
:value="integrationForm.token"
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<clipboard-button
|
|
||||||
:text="integrationForm.token || ''"
|
|
||||||
:title="__('Copy')"
|
|
||||||
class="gl-m-0!"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</gl-form-input-group>
|
|
||||||
|
|
||||||
<gl-button v-gl-modal.authKeyModal :disabled="!active" class="gl-mt-3">
|
<div class="gl-my-4">
|
||||||
{{ $options.i18n.integrationFormSteps.step3.reset }}
|
<span>
|
||||||
</gl-button>
|
{{ s__('AlertSettings|Webhook URL') }}
|
||||||
<gl-modal
|
</span>
|
||||||
modal-id="authKeyModal"
|
|
||||||
:title="$options.i18n.integrationFormSteps.step3.reset"
|
|
||||||
:ok-title="$options.i18n.integrationFormSteps.step3.reset"
|
|
||||||
ok-variant="danger"
|
|
||||||
@ok="resetAuthKey"
|
|
||||||
>
|
|
||||||
{{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
|
|
||||||
</gl-modal>
|
|
||||||
</div>
|
|
||||||
</gl-form-group>
|
|
||||||
<gl-form-group
|
|
||||||
id="test-integration"
|
|
||||||
:label="$options.i18n.integrationFormSteps.step4.label"
|
|
||||||
label-for="test-integration"
|
|
||||||
:invalid-feedback="integrationTestPayload.error"
|
|
||||||
>
|
|
||||||
<alert-settings-form-help-block
|
|
||||||
:message="$options.i18n.integrationFormSteps.step4.help"
|
|
||||||
:link="generic.alertsUsageUrl"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<gl-form-textarea
|
<gl-form-input-group id="url" readonly :value="integrationForm.url">
|
||||||
id="test-payload"
|
<template #append>
|
||||||
v-model.trim="integrationTestPayload.json"
|
<clipboard-button
|
||||||
:disabled="isPayloadEditDisabled"
|
:text="integrationForm.url || ''"
|
||||||
:state="jsonIsValid"
|
:title="__('Copy')"
|
||||||
:placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
|
class="gl-m-0!"
|
||||||
class="gl-my-4"
|
/>
|
||||||
:debounce="$options.JSON_VALIDATE_DELAY"
|
</template>
|
||||||
rows="6"
|
</gl-form-input-group>
|
||||||
max-rows="10"
|
</div>
|
||||||
@input="validateJson"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template v-if="showMappingBuilder">
|
<div class="gl-my-4">
|
||||||
<gl-button
|
<span>
|
||||||
v-if="canEditPayload"
|
{{ $options.i18n.integrationFormSteps.step3.info }}
|
||||||
v-gl-modal.resetPayloadModal
|
</span>
|
||||||
:disabled="!active"
|
|
||||||
class="gl-mt-3"
|
|
||||||
>
|
|
||||||
{{ $options.i18n.integrationFormSteps.step4.editPayload }}
|
|
||||||
</gl-button>
|
|
||||||
|
|
||||||
<gl-button
|
<gl-form-input-group
|
||||||
v-else
|
id="authorization-key"
|
||||||
:disabled="!active"
|
class="gl-mb-2"
|
||||||
:loading="parsingPayload"
|
readonly
|
||||||
class="gl-mt-3"
|
:value="integrationForm.token"
|
||||||
@click="parseMapping"
|
>
|
||||||
>
|
<template #append>
|
||||||
{{ $options.i18n.integrationFormSteps.step4.submitPayload }}
|
<clipboard-button
|
||||||
</gl-button>
|
:text="integrationForm.token || ''"
|
||||||
<gl-modal
|
:title="__('Copy')"
|
||||||
modal-id="resetPayloadModal"
|
class="gl-m-0!"
|
||||||
:title="$options.i18n.integrationFormSteps.step4.resetHeader"
|
/>
|
||||||
:ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
|
</template>
|
||||||
ok-variant="danger"
|
</gl-form-input-group>
|
||||||
@ok="resetSamplePayloadConfirmed = true"
|
|
||||||
>
|
|
||||||
{{ $options.i18n.integrationFormSteps.step4.resetBody }}
|
|
||||||
</gl-modal>
|
|
||||||
</template>
|
|
||||||
</gl-form-group>
|
|
||||||
|
|
||||||
<gl-form-group
|
<gl-button v-gl-modal.authKeyModal :disabled="!active" class="gl-mt-3">
|
||||||
v-if="showMappingBuilder"
|
{{ $options.i18n.integrationFormSteps.step3.reset }}
|
||||||
id="mapping-builder"
|
</gl-button>
|
||||||
:label="$options.i18n.integrationFormSteps.step5.label"
|
<gl-modal
|
||||||
label-for="mapping-builder"
|
modal-id="authKeyModal"
|
||||||
>
|
:title="$options.i18n.integrationFormSteps.step3.reset"
|
||||||
<span class="gl-text-gray-500">{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
|
:ok-title="$options.i18n.integrationFormSteps.step3.reset"
|
||||||
<mapping-builder :payload-fields="mappingBuilderFields" :mapping="mappingBuilderMapping" />
|
ok-variant="danger"
|
||||||
</gl-form-group>
|
@ok="resetAuthKey"
|
||||||
|
>
|
||||||
|
{{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
|
||||||
|
</gl-modal>
|
||||||
|
</div>
|
||||||
|
</gl-form-group>
|
||||||
|
<gl-form-group
|
||||||
|
id="test-integration"
|
||||||
|
:label="$options.i18n.integrationFormSteps.step4.label"
|
||||||
|
label-for="test-integration"
|
||||||
|
:invalid-feedback="integrationTestPayload.error"
|
||||||
|
>
|
||||||
|
<alert-settings-form-help-block
|
||||||
|
:message="$options.i18n.integrationFormSteps.step4.help"
|
||||||
|
:link="generic.alertsUsageUrl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<gl-form-textarea
|
||||||
|
id="test-payload"
|
||||||
|
v-model.trim="integrationTestPayload.json"
|
||||||
|
:disabled="isPayloadEditDisabled"
|
||||||
|
:state="jsonIsValid"
|
||||||
|
:placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
|
||||||
|
class="gl-my-4"
|
||||||
|
:debounce="$options.JSON_VALIDATE_DELAY"
|
||||||
|
rows="6"
|
||||||
|
max-rows="10"
|
||||||
|
@input="validateJson"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="showMappingBuilder">
|
||||||
|
<gl-button
|
||||||
|
v-if="canEditPayload"
|
||||||
|
v-gl-modal.resetPayloadModal
|
||||||
|
:disabled="!active"
|
||||||
|
class="gl-mt-3"
|
||||||
|
>
|
||||||
|
{{ $options.i18n.integrationFormSteps.step4.editPayload }}
|
||||||
|
</gl-button>
|
||||||
|
|
||||||
|
<gl-button
|
||||||
|
v-else
|
||||||
|
:disabled="!active"
|
||||||
|
:loading="parsingPayload"
|
||||||
|
class="gl-mt-3"
|
||||||
|
@click="parseMapping"
|
||||||
|
>
|
||||||
|
{{ $options.i18n.integrationFormSteps.step4.submitPayload }}
|
||||||
|
</gl-button>
|
||||||
|
<gl-modal
|
||||||
|
modal-id="resetPayloadModal"
|
||||||
|
:title="$options.i18n.integrationFormSteps.step4.resetHeader"
|
||||||
|
:ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
|
||||||
|
ok-variant="danger"
|
||||||
|
@ok="resetSamplePayloadConfirmed = true"
|
||||||
|
>
|
||||||
|
{{ $options.i18n.integrationFormSteps.step4.resetBody }}
|
||||||
|
</gl-modal>
|
||||||
|
</template>
|
||||||
|
</gl-form-group>
|
||||||
|
|
||||||
|
<gl-form-group
|
||||||
|
v-if="showMappingBuilder"
|
||||||
|
id="mapping-builder"
|
||||||
|
:label="$options.i18n.integrationFormSteps.step5.label"
|
||||||
|
label-for="mapping-builder"
|
||||||
|
>
|
||||||
|
<span class="gl-text-gray-500">{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
|
||||||
|
<mapping-builder
|
||||||
|
:payload-fields="mappingBuilderFields"
|
||||||
|
:mapping="mappingBuilderMapping"
|
||||||
|
/>
|
||||||
|
</gl-form-group>
|
||||||
|
</div>
|
||||||
<div class="gl-display-flex gl-justify-content-end">
|
<div class="gl-display-flex gl-justify-content-end">
|
||||||
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
|
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
|
||||||
|
<!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
|
||||||
<gl-button
|
<gl-button
|
||||||
data-testid="integration-test-and-submit"
|
data-testid="integration-test-and-submit"
|
||||||
:disabled="Boolean(integrationTestPayload.error)"
|
:disabled="Boolean(integrationTestPayload.error) || isManagingOpsgenie"
|
||||||
category="secondary"
|
category="secondary"
|
||||||
variant="success"
|
variant="success"
|
||||||
class="gl-mr-1 js-no-auto-disable"
|
class="gl-mr-1 js-no-auto-disable"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
import { fetchPolicies } from '~/lib/graphql';
|
import { fetchPolicies } from '~/lib/graphql';
|
||||||
|
@ -38,6 +39,10 @@ export default {
|
||||||
integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
|
integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
GlAlert,
|
||||||
|
GlLink,
|
||||||
|
GlSprintf,
|
||||||
IntegrationsList,
|
IntegrationsList,
|
||||||
SettingsFormOld,
|
SettingsFormOld,
|
||||||
SettingsFormNew,
|
SettingsFormNew,
|
||||||
|
@ -50,6 +55,10 @@ export default {
|
||||||
prometheus: {
|
prometheus: {
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
opsgenie: {
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
projectPath: {
|
projectPath: {
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
@ -274,7 +283,27 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
|
||||||
|
<gl-alert v-if="opsgenie.active" :dismissible="false" variant="tip">
|
||||||
|
<gl-sprintf
|
||||||
|
:message="
|
||||||
|
s__(
|
||||||
|
'AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #link="{ content }">
|
||||||
|
<gl-link
|
||||||
|
class="gl-display-inline-block"
|
||||||
|
href="https://gitlab.com/gitlab-org/gitlab/-/issues/273657"
|
||||||
|
target="_blank"
|
||||||
|
>{{ content }}</gl-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</gl-alert>
|
||||||
<integrations-list
|
<integrations-list
|
||||||
|
v-else
|
||||||
:integrations="glFeatures.httpIntegrationsList ? integrations.list : integrationsOptionsOld"
|
:integrations="glFeatures.httpIntegrationsList ? integrations.list : integrationsOptionsOld"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@edit-integration="editIntegration"
|
@edit-integration="editIntegration"
|
||||||
|
|
|
@ -55,6 +55,7 @@ export const integrationTypesNew = [
|
||||||
export const typeSet = {
|
export const typeSet = {
|
||||||
http: 'HTTP',
|
http: 'HTTP',
|
||||||
prometheus: 'PROMETHEUS',
|
prometheus: 'PROMETHEUS',
|
||||||
|
opsgenie: 'OPSGENIE',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const integrationToDeleteDefault = { id: null, name: '' };
|
export const integrationToDeleteDefault = { id: null, name: '' };
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
// This component is being replaced in favor of './board_column_new.vue' for GraphQL boards
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
|
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
|
||||||
import EmptyComponent from '~/vue_shared/components/empty_component';
|
import EmptyComponent from '~/vue_shared/components/empty_component';
|
||||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
|
||||||
import BoardList from './board_list.vue';
|
import BoardList from './board_list.vue';
|
||||||
import BoardListNew from './board_list_new.vue';
|
|
||||||
import boardsStore from '../stores/boards_store';
|
import boardsStore from '../stores/boards_store';
|
||||||
import eventHub from '../eventhub';
|
|
||||||
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
|
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
|
||||||
import { ListType } from '../constants';
|
import { ListType } from '../constants';
|
||||||
|
|
||||||
|
@ -15,9 +12,8 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
BoardPromotionState: EmptyComponent,
|
BoardPromotionState: EmptyComponent,
|
||||||
BoardListHeader,
|
BoardListHeader,
|
||||||
BoardList: gon.features?.graphqlBoardLists ? BoardListNew : BoardList,
|
BoardList,
|
||||||
},
|
},
|
||||||
mixins: [glFeatureFlagMixin()],
|
|
||||||
props: {
|
props: {
|
||||||
list: {
|
list: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -46,44 +42,25 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['getIssuesByList']),
|
|
||||||
showBoardListAndBoardInfo() {
|
showBoardListAndBoardInfo() {
|
||||||
return this.list.type !== ListType.promotion;
|
return this.list.type !== ListType.promotion;
|
||||||
},
|
},
|
||||||
uniqueKey() {
|
|
||||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
|
||||||
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
|
|
||||||
},
|
|
||||||
listIssues() {
|
listIssues() {
|
||||||
if (!this.glFeatures.graphqlBoardLists) {
|
return this.list.issues;
|
||||||
return this.list.issues;
|
|
||||||
}
|
|
||||||
return this.getIssuesByList(this.list.id);
|
|
||||||
},
|
|
||||||
shouldFetchIssues() {
|
|
||||||
return this.glFeatures.graphqlBoardLists && this.list.type !== ListType.blank;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
filter: {
|
filter: {
|
||||||
handler() {
|
handler() {
|
||||||
if (this.shouldFetchIssues) {
|
this.list.page = 1;
|
||||||
this.fetchIssuesForList({ listId: this.list.id });
|
this.list.getIssues(true).catch(() => {
|
||||||
} else {
|
// TODO: handle request error
|
||||||
this.list.page = 1;
|
});
|
||||||
this.list.getIssues(true).catch(() => {
|
|
||||||
// TODO: handle request error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.shouldFetchIssues) {
|
|
||||||
this.fetchIssuesForList({ listId: this.list.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = this;
|
const instance = this;
|
||||||
|
|
||||||
const sortableOptions = getBoardSortableDefaultOptions({
|
const sortableOptions = getBoardSortableDefaultOptions({
|
||||||
|
@ -109,12 +86,6 @@ export default {
|
||||||
|
|
||||||
Sortable.create(this.$el.parentNode, sortableOptions);
|
Sortable.create(this.$el.parentNode, sortableOptions);
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
...mapActions(['fetchIssuesForList']),
|
|
||||||
showListNewIssueForm(listId) {
|
|
||||||
eventHub.$emit('showForm', listId);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions, mapState } from 'vuex';
|
||||||
|
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue';
|
||||||
|
import BoardPromotionState from 'ee_else_ce/boards/components/board_promotion_state';
|
||||||
|
import BoardList from './board_list_new.vue';
|
||||||
|
import { ListType } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BoardPromotionState,
|
||||||
|
BoardListHeader,
|
||||||
|
BoardList,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
list: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canAdminList: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
boardId: {
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['filterParams']),
|
||||||
|
...mapGetters(['getIssuesByList']),
|
||||||
|
showBoardListAndBoardInfo() {
|
||||||
|
return this.list.type !== ListType.promotion;
|
||||||
|
},
|
||||||
|
listIssues() {
|
||||||
|
return this.getIssuesByList(this.list.id);
|
||||||
|
},
|
||||||
|
shouldFetchIssues() {
|
||||||
|
return this.list.type !== ListType.blank;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
filterParams: {
|
||||||
|
handler() {
|
||||||
|
if (this.shouldFetchIssues) {
|
||||||
|
this.fetchIssuesForList({ listId: this.list.id });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['fetchIssuesForList']),
|
||||||
|
// TODO: Reordering of lists https://gitlab.com/gitlab-org/gitlab/-/issues/280515
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'is-draggable': !list.preset,
|
||||||
|
'is-expandable': list.isExpandable,
|
||||||
|
'is-collapsed': !list.isExpanded,
|
||||||
|
'board-type-assignee': list.type === 'assignee',
|
||||||
|
}"
|
||||||
|
:data-id="list.id"
|
||||||
|
class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal"
|
||||||
|
data-qa-selector="board_list"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
|
||||||
|
>
|
||||||
|
<board-list-header :can-admin-list="canAdminList" :list="list" :disabled="disabled" />
|
||||||
|
<board-list
|
||||||
|
v-if="showBoardListAndBoardInfo"
|
||||||
|
ref="board-list"
|
||||||
|
:disabled="disabled"
|
||||||
|
:issues="listIssues"
|
||||||
|
:list="list"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Will be only available in EE -->
|
||||||
|
<board-promotion-state v-if="list.id === 'promotion'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,13 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||||
import { sortBy } from 'lodash';
|
import { sortBy } from 'lodash';
|
||||||
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
|
|
||||||
import { GlAlert } from '@gitlab/ui';
|
import { GlAlert } from '@gitlab/ui';
|
||||||
|
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
|
||||||
|
import BoardColumnNew from './board_column_new.vue';
|
||||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BoardColumn,
|
BoardColumn: gon.features?.graphqlBoardLists ? BoardColumnNew : BoardColumn,
|
||||||
BoardContentSidebar: () => import('ee_component/boards/components/board_content_sidebar.vue'),
|
BoardContentSidebar: () => import('ee_component/boards/components/board_content_sidebar.vue'),
|
||||||
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
||||||
GlAlert,
|
GlAlert,
|
||||||
|
|
|
@ -17,7 +17,6 @@ import eventHub from '../eventhub';
|
||||||
import sidebarEventHub from '~/sidebar/event_hub';
|
import sidebarEventHub from '~/sidebar/event_hub';
|
||||||
import { inactiveId, LIST, ListType } from '../constants';
|
import { inactiveId, LIST, ListType } from '../constants';
|
||||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -32,7 +31,6 @@ export default {
|
||||||
directives: {
|
directives: {
|
||||||
GlTooltip: GlTooltipDirective,
|
GlTooltip: GlTooltipDirective,
|
||||||
},
|
},
|
||||||
mixins: [glFeatureFlagMixin()],
|
|
||||||
props: {
|
props: {
|
||||||
list: {
|
list: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -121,12 +119,9 @@ export default {
|
||||||
collapsedTooltipTitle() {
|
collapsedTooltipTitle() {
|
||||||
return this.listTitle || this.listAssignee;
|
return this.listTitle || this.listAssignee;
|
||||||
},
|
},
|
||||||
shouldDisplaySwimlanes() {
|
|
||||||
return this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['updateList', 'setActiveId']),
|
...mapActions(['setActiveId']),
|
||||||
openSidebarSettings() {
|
openSidebarSettings() {
|
||||||
if (this.activeId === inactiveId) {
|
if (this.activeId === inactiveId) {
|
||||||
sidebarEventHub.$emit('sidebar.closeAll');
|
sidebarEventHub.$emit('sidebar.closeAll');
|
||||||
|
@ -160,11 +155,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateListFunction() {
|
updateListFunction() {
|
||||||
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
|
this.list.update();
|
||||||
this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded });
|
|
||||||
} else {
|
|
||||||
this.list.update();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -254,7 +245,7 @@ export default {
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="list.type === 'assignee'"
|
v-if="list.type === 'assignee'"
|
||||||
class="board-title-sub-text gl-ml-2 gl-font-weight-normal"
|
class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
|
||||||
:class="{ 'gl-display-none': !list.isExpanded }"
|
:class="{ 'gl-display-none': !list.isExpanded }"
|
||||||
>
|
>
|
||||||
@{{ listAssignee }}
|
@{{ listAssignee }}
|
||||||
|
|
|
@ -0,0 +1,358 @@
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapState } from 'vuex';
|
||||||
|
import {
|
||||||
|
GlButton,
|
||||||
|
GlButtonGroup,
|
||||||
|
GlLabel,
|
||||||
|
GlTooltip,
|
||||||
|
GlIcon,
|
||||||
|
GlSprintf,
|
||||||
|
GlTooltipDirective,
|
||||||
|
} from '@gitlab/ui';
|
||||||
|
import { n__, s__ } from '~/locale';
|
||||||
|
import AccessorUtilities from '../../lib/utils/accessor';
|
||||||
|
import IssueCount from './issue_count.vue';
|
||||||
|
import eventHub from '../eventhub';
|
||||||
|
import sidebarEventHub from '~/sidebar/event_hub';
|
||||||
|
import { inactiveId, LIST, ListType } from '../constants';
|
||||||
|
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GlButtonGroup,
|
||||||
|
GlButton,
|
||||||
|
GlLabel,
|
||||||
|
GlTooltip,
|
||||||
|
GlIcon,
|
||||||
|
GlSprintf,
|
||||||
|
IssueCount,
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
GlTooltip: GlTooltipDirective,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
list: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isSwimlanesHeader: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
boardId: {
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
weightFeatureAvailable: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
scopedLabelsAvailable: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
currentUserId: {
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['activeId']),
|
||||||
|
isLoggedIn() {
|
||||||
|
return Boolean(this.currentUserId);
|
||||||
|
},
|
||||||
|
listType() {
|
||||||
|
return this.list.type;
|
||||||
|
},
|
||||||
|
listAssignee() {
|
||||||
|
return this.list?.assignee?.username || '';
|
||||||
|
},
|
||||||
|
listTitle() {
|
||||||
|
return this.list?.label?.description || this.list.title || '';
|
||||||
|
},
|
||||||
|
showListHeaderButton() {
|
||||||
|
return (
|
||||||
|
!this.disabled &&
|
||||||
|
this.listType !== ListType.closed &&
|
||||||
|
this.listType !== ListType.blank &&
|
||||||
|
this.listType !== ListType.promotion
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showMilestoneListDetails() {
|
||||||
|
return (
|
||||||
|
this.list.type === ListType.milestone &&
|
||||||
|
this.list.milestone &&
|
||||||
|
(this.list.isExpanded || !this.isSwimlanesHeader)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showAssigneeListDetails() {
|
||||||
|
return (
|
||||||
|
this.list.type === ListType.assignee && (this.list.isExpanded || !this.isSwimlanesHeader)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
issuesCount() {
|
||||||
|
return this.list.issuesSize;
|
||||||
|
},
|
||||||
|
issuesTooltipLabel() {
|
||||||
|
return n__(`%d issue`, `%d issues`, this.issuesCount);
|
||||||
|
},
|
||||||
|
chevronTooltip() {
|
||||||
|
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
|
||||||
|
},
|
||||||
|
chevronIcon() {
|
||||||
|
return this.list.isExpanded ? 'chevron-right' : 'chevron-down';
|
||||||
|
},
|
||||||
|
isNewIssueShown() {
|
||||||
|
return this.listType === ListType.backlog || this.showListHeaderButton;
|
||||||
|
},
|
||||||
|
isSettingsShown() {
|
||||||
|
return (
|
||||||
|
this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showBoardListAndBoardInfo() {
|
||||||
|
return this.listType !== ListType.blank && this.listType !== ListType.promotion;
|
||||||
|
},
|
||||||
|
uniqueKey() {
|
||||||
|
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||||
|
return `boards.${this.boardId}.${this.listType}.${this.list.id}`;
|
||||||
|
},
|
||||||
|
collapsedTooltipTitle() {
|
||||||
|
return this.listTitle || this.listAssignee;
|
||||||
|
},
|
||||||
|
headerStyle() {
|
||||||
|
return { borderTopColor: this.list?.label?.color };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['updateList', 'setActiveId']),
|
||||||
|
openSidebarSettings() {
|
||||||
|
if (this.activeId === inactiveId) {
|
||||||
|
sidebarEventHub.$emit('sidebar.closeAll');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setActiveId({ id: this.list.id, sidebarType: LIST });
|
||||||
|
},
|
||||||
|
showScopedLabels(label) {
|
||||||
|
return this.scopedLabelsAvailable && isScopedLabel(label);
|
||||||
|
},
|
||||||
|
|
||||||
|
showNewIssueForm() {
|
||||||
|
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
|
||||||
|
},
|
||||||
|
toggleExpanded() {
|
||||||
|
this.list.isExpanded = !this.list.isExpanded;
|
||||||
|
|
||||||
|
if (!this.isLoggedIn) {
|
||||||
|
this.addToLocalStorage();
|
||||||
|
} else {
|
||||||
|
this.updateListFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
|
||||||
|
// Close all tooltips manually to prevent dangling tooltips.
|
||||||
|
this.$root.$emit('bv::hide::tooltip');
|
||||||
|
},
|
||||||
|
addToLocalStorage() {
|
||||||
|
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
|
localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateListFunction() {
|
||||||
|
this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header
|
||||||
|
:class="{
|
||||||
|
'has-border': list.label && list.label.color,
|
||||||
|
'gl-h-full': !list.isExpanded,
|
||||||
|
'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader,
|
||||||
|
}"
|
||||||
|
:style="headerStyle"
|
||||||
|
class="board-header gl-relative"
|
||||||
|
data-qa-selector="board_list_header"
|
||||||
|
data-testid="board-list-header"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
:class="{
|
||||||
|
'user-can-drag': !disabled && !list.preset,
|
||||||
|
'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader,
|
||||||
|
'gl-border-b-0': !list.isExpanded || isSwimlanesHeader,
|
||||||
|
'gl-py-2': !list.isExpanded && isSwimlanesHeader,
|
||||||
|
'gl-flex-direction-column': !list.isExpanded,
|
||||||
|
}"
|
||||||
|
class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 js-board-handle"
|
||||||
|
>
|
||||||
|
<gl-button
|
||||||
|
v-if="list.isExpandable"
|
||||||
|
v-gl-tooltip.hover
|
||||||
|
:aria-label="chevronTooltip"
|
||||||
|
:title="chevronTooltip"
|
||||||
|
:icon="chevronIcon"
|
||||||
|
class="board-title-caret no-drag gl-cursor-pointer"
|
||||||
|
variant="link"
|
||||||
|
@click="toggleExpanded"
|
||||||
|
/>
|
||||||
|
<!-- EE start -->
|
||||||
|
<span
|
||||||
|
v-if="showMilestoneListDetails"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="milestone-icon"
|
||||||
|
:class="{
|
||||||
|
'gl-mt-3 gl-rotate-90': !list.isExpanded,
|
||||||
|
'gl-mr-2': list.isExpanded,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<gl-icon name="timer" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-if="showAssigneeListDetails"
|
||||||
|
:href="list.assignee.path"
|
||||||
|
class="user-avatar-link js-no-trigger"
|
||||||
|
:class="{
|
||||||
|
'gl-mt-3 gl-rotate-90': !list.isExpanded,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-gl-tooltip.hover.bottom
|
||||||
|
:title="listAssignee"
|
||||||
|
:alt="list.assignee.name"
|
||||||
|
:src="list.assignee.avatar"
|
||||||
|
class="avatar s20"
|
||||||
|
height="20"
|
||||||
|
width="20"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- EE end -->
|
||||||
|
<div
|
||||||
|
class="board-title-text"
|
||||||
|
:class="{
|
||||||
|
'gl-display-none': !list.isExpanded && isSwimlanesHeader,
|
||||||
|
'gl-flex-grow-0 gl-my-3 gl-mx-0': !list.isExpanded,
|
||||||
|
'gl-flex-grow-1': list.isExpanded,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<!-- EE start -->
|
||||||
|
<span
|
||||||
|
v-if="listType !== 'label'"
|
||||||
|
v-gl-tooltip.hover
|
||||||
|
:class="{
|
||||||
|
'gl-display-block': !list.isExpanded || listType === 'milestone',
|
||||||
|
}"
|
||||||
|
:title="listTitle"
|
||||||
|
class="board-title-main-text gl-text-truncate"
|
||||||
|
>
|
||||||
|
{{ list.title }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="listType === 'assignee'"
|
||||||
|
v-show="list.isExpanded"
|
||||||
|
class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
|
||||||
|
>
|
||||||
|
@{{ listAssignee }}
|
||||||
|
</span>
|
||||||
|
<!-- EE end -->
|
||||||
|
<gl-label
|
||||||
|
v-if="listType === 'label'"
|
||||||
|
v-gl-tooltip.hover.bottom
|
||||||
|
:background-color="list.label.color"
|
||||||
|
:description="list.label.description"
|
||||||
|
:scoped="showScopedLabels(list.label)"
|
||||||
|
:size="!list.isExpanded ? 'sm' : ''"
|
||||||
|
:title="list.label.title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EE start -->
|
||||||
|
<span
|
||||||
|
v-if="isSwimlanesHeader && !list.isExpanded"
|
||||||
|
ref="collapsedInfo"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="board-header-collapsed-info-icon gl-mt-2 gl-cursor-pointer gl-text-gray-500"
|
||||||
|
>
|
||||||
|
<gl-icon name="information" />
|
||||||
|
</span>
|
||||||
|
<gl-tooltip v-if="isSwimlanesHeader && !list.isExpanded" :target="() => $refs.collapsedInfo">
|
||||||
|
<div class="gl-font-weight-bold gl-pb-2">{{ collapsedTooltipTitle }}</div>
|
||||||
|
<div v-if="list.maxIssueCount !== 0">
|
||||||
|
•
|
||||||
|
<gl-sprintf :message="__('%{issuesSize} with a limit of %{maxIssueCount}')">
|
||||||
|
<template #issuesSize>{{ issuesTooltipLabel }}</template>
|
||||||
|
<template #maxIssueCount>{{ list.maxIssueCount }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</div>
|
||||||
|
<div v-else>• {{ issuesTooltipLabel }}</div>
|
||||||
|
<div v-if="weightFeatureAvailable">
|
||||||
|
•
|
||||||
|
<gl-sprintf :message="__('%{totalWeight} total weight')">
|
||||||
|
<template #totalWeight>{{ list.totalWeight }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</div>
|
||||||
|
</gl-tooltip>
|
||||||
|
<!-- EE end -->
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="showBoardListAndBoardInfo"
|
||||||
|
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag gl-text-gray-500"
|
||||||
|
:class="{
|
||||||
|
'gl-display-none!': !list.isExpanded && isSwimlanesHeader,
|
||||||
|
'gl-p-0': !list.isExpanded,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="gl-display-inline-flex">
|
||||||
|
<gl-tooltip :target="() => $refs.issueCount" :title="issuesTooltipLabel" />
|
||||||
|
<span ref="issueCount" class="issue-count-badge-count">
|
||||||
|
<gl-icon class="gl-mr-2" name="issues" />
|
||||||
|
<issue-count :issues-size="issuesCount" :max-issue-count="list.maxIssueCount" />
|
||||||
|
</span>
|
||||||
|
<!-- EE start -->
|
||||||
|
<template v-if="weightFeatureAvailable">
|
||||||
|
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
|
||||||
|
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
|
||||||
|
<gl-icon class="gl-mr-2" name="weight" />
|
||||||
|
{{ list.totalWeight }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- EE end -->
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<gl-button-group
|
||||||
|
v-if="isNewIssueShown || isSettingsShown"
|
||||||
|
class="board-list-button-group pl-2"
|
||||||
|
>
|
||||||
|
<gl-button
|
||||||
|
v-if="isNewIssueShown"
|
||||||
|
v-show="list.isExpanded"
|
||||||
|
ref="newIssueBtn"
|
||||||
|
v-gl-tooltip.hover
|
||||||
|
:aria-label="__('New issue')"
|
||||||
|
:title="__('New issue')"
|
||||||
|
class="issue-count-badge-add-button no-drag"
|
||||||
|
icon="plus"
|
||||||
|
@click="showNewIssueForm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<gl-button
|
||||||
|
v-if="isSettingsShown"
|
||||||
|
ref="settingsBtn"
|
||||||
|
v-gl-tooltip.hover
|
||||||
|
:aria-label="__('List settings')"
|
||||||
|
class="no-drag js-board-settings-button"
|
||||||
|
:title="__('List settings')"
|
||||||
|
icon="settings"
|
||||||
|
@click="openSidebarSettings"
|
||||||
|
/>
|
||||||
|
<gl-tooltip :target="() => $refs.settingsBtn">{{ __('List settings') }}</gl-tooltip>
|
||||||
|
</gl-button-group>
|
||||||
|
</h3>
|
||||||
|
</header>
|
||||||
|
</template>
|
|
@ -0,0 +1 @@
|
||||||
|
export default {};
|
|
@ -86,6 +86,7 @@ export default () => {
|
||||||
boardId: $boardApp.dataset.boardId,
|
boardId: $boardApp.dataset.boardId,
|
||||||
groupId: Number($boardApp.dataset.groupId),
|
groupId: Number($boardApp.dataset.groupId),
|
||||||
rootPath: $boardApp.dataset.rootPath,
|
rootPath: $boardApp.dataset.rootPath,
|
||||||
|
currentUserId: gon.current_user_id || null,
|
||||||
canUpdate: $boardApp.dataset.canUpdate,
|
canUpdate: $boardApp.dataset.canUpdate,
|
||||||
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
|
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
|
||||||
labelsManagePath: $boardApp.dataset.labelsManagePath,
|
labelsManagePath: $boardApp.dataset.labelsManagePath,
|
||||||
|
@ -95,6 +96,7 @@ export default () => {
|
||||||
boardWeight: $boardApp.dataset.boardWeight
|
boardWeight: $boardApp.dataset.boardWeight
|
||||||
? parseInt($boardApp.dataset.boardWeight, 10)
|
? parseInt($boardApp.dataset.boardWeight, 10)
|
||||||
: null,
|
: null,
|
||||||
|
scopedLabelsAvailable: parseBoolean($boardApp.dataset.scopedLabels),
|
||||||
},
|
},
|
||||||
store,
|
store,
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
|
|
|
@ -365,7 +365,10 @@ export default {
|
||||||
|
|
||||||
dispatch('createNewIssue', issueInput)
|
dispatch('createNewIssue', issueInput)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
commit(types.ADD_ISSUE_TO_LIST, { list, issue: formatIssue(res) });
|
commit(types.ADD_ISSUE_TO_LIST, {
|
||||||
|
list,
|
||||||
|
issue: formatIssue({ ...res, id: getIdFromGraphQLId(res.id) }),
|
||||||
|
});
|
||||||
commit(types.REMOVE_ISSUE_FROM_LIST, { list, issue });
|
commit(types.REMOVE_ISSUE_FROM_LIST, { list, issue });
|
||||||
})
|
})
|
||||||
.catch(() => commit(types.ADD_ISSUE_TO_LIST_FAILURE, { list, issueId: issueInput.id }));
|
.catch(() => commit(types.ADD_ISSUE_TO_LIST_FAILURE, { list, issueId: issueInput.id }));
|
||||||
|
|
|
@ -2508,6 +2508,9 @@ msgstr ""
|
||||||
msgid "AlertSettings|1. Select integration type"
|
msgid "AlertSettings|1. Select integration type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AlertSettings|2. Add link to your Opsgenie alert list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AlertSettings|2. Name integration"
|
msgid "AlertSettings|2. Name integration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2643,6 +2646,12 @@ msgstr ""
|
||||||
msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send Alerts to GitLab. Review your chosen services documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
|
msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send Alerts to GitLab. Review your chosen services documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your exisiting Opsgenie instance. By selecting this option, you cannot recieve alerts from any other source in GitLab; it will effectivley be turing Alerts within GitLab off as a feature."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AlertSettings|Webhook URL"
|
msgid "AlertSettings|Webhook URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,13 @@ module QA
|
||||||
@retry_later_backoff = QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME
|
@retry_later_backoff = QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
# For prettier failure messages
|
||||||
|
# Eg.: "expected QA::Page::File::Show not to have file "QA Test - File name"
|
||||||
|
# Instead of "expected #<QA::Page::File::Show:0x000055c6511e07b8 @retry_later_backoff=60> not to have file "QA Test - File name"
|
||||||
|
self.class.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def assert_no_element(name)
|
def assert_no_element(name)
|
||||||
assert_no_selector(element_selector_css(name))
|
assert_no_selector(element_selector_css(name))
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
|
exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
|
||||||
"<form class=\\"gl-mt-6\\">
|
"<form class=\\"gl-mt-6\\" canmanageopsgenie=\\"true\\">
|
||||||
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
|
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
|
||||||
<div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
|
<div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
|
||||||
<div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select custom-select\\" id=\\"__BVID__8\\">
|
<div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select custom-select\\" id=\\"__BVID__8\\">
|
||||||
<option value=\\"\\">Select integration type</option>
|
<option value=\\"\\">Select integration type</option>
|
||||||
<option value=\\"HTTP\\">HTTP Endpoint</option>
|
<option value=\\"HTTP\\">HTTP Endpoint</option>
|
||||||
<option value=\\"PROMETHEUS\\">External Prometheus</option>
|
<option value=\\"PROMETHEUS\\">External Prometheus</option>
|
||||||
<option value=\\"OPSGENIE\\">Opsgenie</option>
|
<option disabled=\\"disabled\\" value=\\"OPSGENIE\\">Opsgenie</option>
|
||||||
</select>
|
</select>
|
||||||
<div class=\\"gl-my-4\\"><span class=\\"gl-text-gray-500\\">Learn more about our upcoming <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://gitlab.com/groups/gitlab-org/-/epics/4390\\" class=\\"gl-link gl-display-inline-block\\">integrations</a></span></div>
|
<div class=\\"gl-my-4\\"><span class=\\"gl-text-gray-500\\">Learn more about our upcoming <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://gitlab.com/groups/gitlab-org/-/epics/4390\\" class=\\"gl-link gl-display-inline-block\\">integrations</a></span></div>
|
||||||
<!---->
|
<!---->
|
||||||
|
@ -18,71 +18,73 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=\\"gl-mt-3 collapse\\" style=\\"display: none;\\" id=\\"__BVID__13\\">
|
<div class=\\"gl-mt-3 collapse\\" style=\\"display: none;\\" id=\\"__BVID__13\\">
|
||||||
<div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
|
<div>
|
||||||
<div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__18\\">
|
<div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
|
||||||
<!---->
|
<div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__18\\">
|
||||||
<!---->
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
|
|
||||||
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Utilize the URL and authorization key below to authorize an external service to send Alerts to GitLab. Review your chosen services documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
|
|
||||||
<div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
|
|
||||||
<!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
|
|
||||||
<!---->
|
<!---->
|
||||||
</label>
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
<div class=\\"gl-my-4\\"><span>
|
</div>
|
||||||
Webhook URL
|
</div>
|
||||||
</span>
|
<div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
|
||||||
<div id=\\"url\\" readonly=\\"readonly\\">
|
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Utilize the URL and authorization key below to authorize an external service to send Alerts to GitLab. Review your chosen services documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
|
||||||
<div role=\\"group\\" class=\\"input-group\\">
|
<div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
|
||||||
<!---->
|
<!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
|
||||||
<!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
<!---->
|
||||||
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
</label>
|
||||||
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
|
<!---->
|
||||||
<use href=\\"#copy-to-clipboard\\"></use>
|
<div class=\\"gl-my-4\\"><span>
|
||||||
</svg>
|
Webhook URL
|
||||||
<!----></button></div>
|
</span>
|
||||||
<!---->
|
<div id=\\"url\\" readonly=\\"readonly\\">
|
||||||
|
<div role=\\"group\\" class=\\"input-group\\">
|
||||||
|
<!---->
|
||||||
|
<!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
||||||
|
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
||||||
|
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
|
||||||
|
<use href=\\"#copy-to-clipboard\\"></use>
|
||||||
|
</svg>
|
||||||
|
<!----></button></div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class=\\"gl-my-4\\"><span>
|
||||||
<div class=\\"gl-my-4\\"><span>
|
Authorization key
|
||||||
Authorization key
|
</span>
|
||||||
</span>
|
<div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-2\\">
|
||||||
<div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-2\\">
|
<div role=\\"group\\" class=\\"input-group\\">
|
||||||
<div role=\\"group\\" class=\\"input-group\\">
|
<!---->
|
||||||
|
<!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
||||||
|
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
||||||
|
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
|
||||||
|
<use href=\\"#copy-to-clipboard\\"></use>
|
||||||
|
</svg>
|
||||||
|
<!----></button></div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mt-3 btn-default btn-md disabled gl-button\\">
|
||||||
<!---->
|
<!---->
|
||||||
<!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
|
<!----> <span class=\\"gl-button-text\\">
|
||||||
<div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
|
Reset Key
|
||||||
<!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
|
</span></button>
|
||||||
<use href=\\"#copy-to-clipboard\\"></use>
|
|
||||||
</svg>
|
|
||||||
<!----></button></div>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mt-3 btn-default btn-md disabled gl-button\\">
|
|
||||||
<!---->
|
<!---->
|
||||||
<!----> <span class=\\"gl-button-text\\">
|
</div>
|
||||||
Reset Key
|
<!---->
|
||||||
</span></button>
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
|
||||||
<!---->
|
|
||||||
<!---->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Test integration(optional)</label>
|
||||||
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Test integration(optional)</label>
|
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
|
||||||
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
<!---->
|
||||||
<!---->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
<!---->
|
|
||||||
<div class=\\"gl-display-flex gl-justify-content-end\\"><button type=\\"reset\\" class=\\"btn gl-mr-3 js-no-auto-disable btn-default btn-md gl-button\\">
|
<div class=\\"gl-display-flex gl-justify-content-end\\"><button type=\\"reset\\" class=\\"btn gl-mr-3 js-no-auto-disable btn-default btn-md gl-button\\">
|
||||||
<!---->
|
<!---->
|
||||||
<!----> <span class=\\"gl-button-text\\">Cancel</span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" class=\\"btn gl-mr-1 js-no-auto-disable btn-success btn-md gl-button btn-success-secondary\\">
|
<!----> <span class=\\"gl-button-text\\">Cancel</span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" class=\\"btn gl-mr-1 js-no-auto-disable btn-success btn-md gl-button btn-success-secondary\\">
|
||||||
|
|
|
@ -33,6 +33,9 @@ describe('AlertIntegrationsList', () => {
|
||||||
integrations: mockIntegrations,
|
integrations: mockIntegrations,
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
|
provide: {
|
||||||
|
glFeatures: { httpIntegrationsList: true },
|
||||||
|
},
|
||||||
stubs: {
|
stubs: {
|
||||||
GlIcon: true,
|
GlIcon: true,
|
||||||
GlButton: true,
|
GlButton: true,
|
||||||
|
|
|
@ -27,6 +27,7 @@ describe('AlertsSettingsFormNew', () => {
|
||||||
propsData: {
|
propsData: {
|
||||||
loading: false,
|
loading: false,
|
||||||
canAddIntegration: true,
|
canAddIntegration: true,
|
||||||
|
canManageOpsgenie: true,
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import VueApollo from 'vue-apollo';
|
||||||
import { mount, createLocalVue } from '@vue/test-utils';
|
import { mount, createLocalVue } from '@vue/test-utils';
|
||||||
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
||||||
import waitForPromises from 'helpers/wait_for_promises';
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
import { GlLoadingIcon } from '@gitlab/ui';
|
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
|
||||||
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
|
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
|
||||||
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
|
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
|
||||||
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
|
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
|
||||||
|
@ -21,6 +21,7 @@ import {
|
||||||
RESET_INTEGRATION_TOKEN_ERROR,
|
RESET_INTEGRATION_TOKEN_ERROR,
|
||||||
UPDATE_INTEGRATION_ERROR,
|
UPDATE_INTEGRATION_ERROR,
|
||||||
INTEGRATION_PAYLOAD_TEST_ERROR,
|
INTEGRATION_PAYLOAD_TEST_ERROR,
|
||||||
|
DELETE_INTEGRATION_ERROR,
|
||||||
} from '~/alerts_settings/utils/error_messages';
|
} from '~/alerts_settings/utils/error_messages';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { defaultAlertSettingsConfig } from './util';
|
import { defaultAlertSettingsConfig } from './util';
|
||||||
|
@ -59,6 +60,12 @@ describe('AlertsSettingsWrapper', () => {
|
||||||
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
|
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function awaitApolloDomMock() {
|
||||||
|
await wrapper.vm.$nextTick(); // kick off the DOM update
|
||||||
|
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
|
||||||
|
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
|
||||||
|
}
|
||||||
|
|
||||||
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
|
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
|
||||||
wrapper = mount(AlertsSettingsWrapper, {
|
wrapper = mount(AlertsSettingsWrapper, {
|
||||||
data() {
|
data() {
|
||||||
|
@ -372,12 +379,35 @@ describe('AlertsSettingsWrapper', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await destroyHttpIntegration(wrapper);
|
await destroyHttpIntegration(wrapper);
|
||||||
|
await awaitApolloDomMock();
|
||||||
await wrapper.vm.$nextTick(); // kick off the DOM update
|
|
||||||
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
|
|
||||||
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
|
|
||||||
|
|
||||||
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
|
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('displays flash if mutation had a non-recoverable error', async () => {
|
||||||
|
createComponentWithApollo({
|
||||||
|
destroyHandler: jest.fn().mockRejectedValue('Error'),
|
||||||
|
});
|
||||||
|
|
||||||
|
await destroyHttpIntegration(wrapper);
|
||||||
|
await awaitApolloDomMock();
|
||||||
|
|
||||||
|
expect(createFlash).toHaveBeenCalledWith({
|
||||||
|
message: DELETE_INTEGRATION_ERROR,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
|
||||||
|
describe('Opsgenie integration', () => {
|
||||||
|
it.each([true, false])('it shows/hides the alert when opsgenie is %s', active => {
|
||||||
|
createComponent({
|
||||||
|
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
|
||||||
|
provide: { glFeatures: { httpIntegrationsList: true }, opsgenie: { active } },
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find(GlAlert).exists()).toBe(active);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
|
||||||
|
import { listObj } from 'jest/boards/mock_data';
|
||||||
|
import BoardColumn from '~/boards/components/board_column_new.vue';
|
||||||
|
import List from '~/boards/models/list';
|
||||||
|
import { ListType } from '~/boards/constants';
|
||||||
|
import { createStore } from '~/boards/stores';
|
||||||
|
|
||||||
|
describe('Board Column Component', () => {
|
||||||
|
let wrapper;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
wrapper = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const createComponent = ({ listType = ListType.backlog, collapsed = false } = {}) => {
|
||||||
|
const boardId = '1';
|
||||||
|
|
||||||
|
const listMock = {
|
||||||
|
...listObj,
|
||||||
|
list_type: listType,
|
||||||
|
collapsed,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (listType === ListType.assignee) {
|
||||||
|
delete listMock.label;
|
||||||
|
listMock.user = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = new List({ ...listMock, doNotFetchIssues: true });
|
||||||
|
|
||||||
|
store = createStore();
|
||||||
|
|
||||||
|
wrapper = shallowMount(BoardColumn, {
|
||||||
|
store,
|
||||||
|
propsData: {
|
||||||
|
disabled: false,
|
||||||
|
list,
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
boardId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isExpandable = () => wrapper.classes('is-expandable');
|
||||||
|
const isCollapsed = () => wrapper.classes('is-collapsed');
|
||||||
|
|
||||||
|
describe('Given different list types', () => {
|
||||||
|
it('is expandable when List Type is `backlog`', () => {
|
||||||
|
createComponent({ listType: ListType.backlog });
|
||||||
|
|
||||||
|
expect(isExpandable()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('expanded / collapsed column', () => {
|
||||||
|
it('has class is-collapsed when list is collapsed', () => {
|
||||||
|
createComponent({ collapsed: false });
|
||||||
|
|
||||||
|
expect(wrapper.vm.list.isExpanded).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not have class is-collapsed when list is expanded', () => {
|
||||||
|
createComponent({ collapsed: true });
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -78,7 +78,7 @@ describe('Board Column Component', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expanded / collaped column', () => {
|
describe('expanded / collapsed column', () => {
|
||||||
it('has class is-collapsed when list is collapsed', () => {
|
it('has class is-collapsed when list is collapsed', () => {
|
||||||
createComponent({ collapsed: false });
|
createComponent({ collapsed: false });
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||||
|
|
||||||
|
import { listObj } from 'jest/boards/mock_data';
|
||||||
|
import BoardListHeader from '~/boards/components/board_list_header_new.vue';
|
||||||
|
import List from '~/boards/models/list';
|
||||||
|
import { ListType } from '~/boards/constants';
|
||||||
|
|
||||||
|
const localVue = createLocalVue();
|
||||||
|
|
||||||
|
localVue.use(Vuex);
|
||||||
|
|
||||||
|
describe('Board List Header Component', () => {
|
||||||
|
let wrapper;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
const updateListSpy = jest.fn();
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
wrapper = null;
|
||||||
|
|
||||||
|
localStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const createComponent = ({
|
||||||
|
listType = ListType.backlog,
|
||||||
|
collapsed = false,
|
||||||
|
withLocalStorage = true,
|
||||||
|
currentUserId = null,
|
||||||
|
} = {}) => {
|
||||||
|
const boardId = '1';
|
||||||
|
|
||||||
|
const listMock = {
|
||||||
|
...listObj,
|
||||||
|
list_type: listType,
|
||||||
|
collapsed,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (listType === ListType.assignee) {
|
||||||
|
delete listMock.label;
|
||||||
|
listMock.user = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = new List({ ...listMock, doNotFetchIssues: true });
|
||||||
|
|
||||||
|
if (withLocalStorage) {
|
||||||
|
localStorage.setItem(
|
||||||
|
`boards.${boardId}.${list.type}.${list.id}.expanded`,
|
||||||
|
(!collapsed).toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
store = new Vuex.Store({
|
||||||
|
state: {},
|
||||||
|
actions: { updateList: updateListSpy },
|
||||||
|
getters: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper = shallowMount(BoardListHeader, {
|
||||||
|
store,
|
||||||
|
localVue,
|
||||||
|
propsData: {
|
||||||
|
disabled: false,
|
||||||
|
list,
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
boardId,
|
||||||
|
weightFeatureAvailable: false,
|
||||||
|
currentUserId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isExpanded = () => wrapper.vm.list.isExpanded;
|
||||||
|
const isCollapsed = () => !isExpanded();
|
||||||
|
|
||||||
|
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
|
||||||
|
const findCaret = () => wrapper.find('.board-title-caret');
|
||||||
|
|
||||||
|
describe('Add issue button', () => {
|
||||||
|
const hasNoAddButton = [ListType.promotion, ListType.blank, ListType.closed];
|
||||||
|
const hasAddButton = [ListType.backlog, ListType.label, ListType.milestone, ListType.assignee];
|
||||||
|
|
||||||
|
it.each(hasNoAddButton)('does not render when List Type is `%s`', listType => {
|
||||||
|
createComponent({ listType });
|
||||||
|
|
||||||
|
expect(findAddIssueButton().exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(hasAddButton)('does render when List Type is `%s`', listType => {
|
||||||
|
createComponent({ listType });
|
||||||
|
|
||||||
|
expect(findAddIssueButton().exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a test for each list type', () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
Object.values(ListType).forEach(value => {
|
||||||
|
expect([...hasAddButton, ...hasNoAddButton]).toContain(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does render when logged out', () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(findAddIssueButton().exists()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('expanding / collapsing the column', () => {
|
||||||
|
it('does not collapse when clicking the header', async () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(false);
|
||||||
|
|
||||||
|
wrapper.find('[data-testid="board-list-header"]').trigger('click');
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('collapses expanded Column when clicking the collapse icon', async () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(isExpanded()).toBe(true);
|
||||||
|
|
||||||
|
findCaret().vm.$emit('click');
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expands collapsed Column when clicking the expand icon', async () => {
|
||||||
|
createComponent({ collapsed: true });
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(true);
|
||||||
|
|
||||||
|
findCaret().vm.$emit('click');
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(isCollapsed()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when logged in it calls list update and doesn't set localStorage", async () => {
|
||||||
|
createComponent({ withLocalStorage: false, currentUserId: 1 });
|
||||||
|
|
||||||
|
findCaret().vm.$emit('click');
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(updateListSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when logged out it doesn't call list update and sets localStorage", async () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
findCaret().vm.$emit('click');
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(updateListSpy).not.toHaveBeenCalled();
|
||||||
|
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -29,10 +29,12 @@ RSpec.describe 'sentry errors requests' do
|
||||||
|
|
||||||
let(:error_data) { graphql_data.dig('project', 'sentryErrors', 'detailedError') }
|
let(:error_data) { graphql_data.dig('project', 'sentryErrors', 'detailedError') }
|
||||||
|
|
||||||
it_behaves_like 'a working graphql query' do
|
it 'returns a successful response', :aggregate_failures, :quarantine do
|
||||||
before do
|
post_graphql(query, current_user: current_user)
|
||||||
post_graphql(query, current_user: current_user)
|
|
||||||
end
|
expect(response).to have_gitlab_http_status(:success)
|
||||||
|
expect(graphql_errors).to be_nil
|
||||||
|
expect(json_response.keys).to include('data')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when data is loading via reactive cache' do
|
context 'when data is loading via reactive cache' do
|
||||||
|
|
Loading…
Reference in New Issue