Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f4d27d532e
commit
f14507e586
|
|
@ -165,7 +165,7 @@ rspec-ee migration pg9:
|
|||
|
||||
rspec-ee unit pg9:
|
||||
extends: .rspec-ee-base-pg9
|
||||
parallel: 5
|
||||
parallel: 10
|
||||
|
||||
rspec-ee integration pg9:
|
||||
extends: .rspec-ee-base-pg9
|
||||
|
|
@ -186,7 +186,7 @@ rspec-ee unit pg10:
|
|||
extends:
|
||||
- .rspec-ee-base-pg10
|
||||
- .only-master
|
||||
parallel: 5
|
||||
parallel: 10
|
||||
|
||||
rspec-ee integration pg10:
|
||||
extends:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable camelcase, guard-for-in, no-restricted-syntax */
|
||||
/* eslint-disable guard-for-in, no-restricted-syntax */
|
||||
/* global NoteModel */
|
||||
|
||||
import $ from 'jquery';
|
||||
|
|
@ -40,13 +40,13 @@ class DiscussionModel {
|
|||
return true;
|
||||
}
|
||||
|
||||
resolveAllNotes(resolved_by) {
|
||||
resolveAllNotes(resolvedBy) {
|
||||
for (const noteId in this.notes) {
|
||||
const note = this.notes[noteId];
|
||||
|
||||
if (!note.resolved) {
|
||||
note.resolved = true;
|
||||
note.resolved_by = resolved_by;
|
||||
note.resolved_by = resolvedBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable camelcase, no-restricted-syntax, guard-for-in */
|
||||
/* eslint-disable no-restricted-syntax, guard-for-in */
|
||||
/* global DiscussionModel */
|
||||
|
||||
import Vue from 'vue';
|
||||
|
|
@ -26,11 +26,11 @@ window.CommentsStore = {
|
|||
|
||||
discussion.createNote(noteObj);
|
||||
},
|
||||
update(discussionId, noteId, resolved, resolved_by) {
|
||||
update(discussionId, noteId, resolved, resolvedBy) {
|
||||
const discussion = this.state[discussionId];
|
||||
const note = discussion.getNote(noteId);
|
||||
note.resolved = resolved;
|
||||
note.resolved_by = resolved_by;
|
||||
note.resolved_by = resolvedBy;
|
||||
},
|
||||
delete(discussionId, noteId) {
|
||||
const discussion = this.state[discussionId];
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default {
|
|||
<div class="environments-container">
|
||||
<gl-loading-icon
|
||||
v-if="isLoading"
|
||||
:size="3"
|
||||
size="md"
|
||||
class="prepend-top-default"
|
||||
label="Loading environments"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ export default {
|
|||
|
||||
<template v-if="shouldRenderFolderContent(model)">
|
||||
<div v-if="model.isLoadingFolderContent" :key="`loading-item-${i}`">
|
||||
<gl-loading-icon :size="2" class="prepend-top-16" />
|
||||
<gl-loading-icon size="md" class="prepend-top-16" />
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro
|
|||
return { api_host: apiHost || null, enabled, token: token || null, project };
|
||||
};
|
||||
|
||||
export const getDisplayName = project => `${project.organizationName} | ${project.name}`;
|
||||
export const getDisplayName = project => `${project.organizationName} | ${project.slug}`;
|
||||
|
||||
export default () => {};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
export function hasInlineLines(diffFile) {
|
||||
return diffFile?.highlighted_diff_lines?.length > 0; /* eslint-disable-line camelcase */
|
||||
return diffFile?.highlighted_diff_lines?.length > 0;
|
||||
}
|
||||
|
||||
export function hasParallelLines(diffFile) {
|
||||
return diffFile?.parallel_diff_lines?.length > 0; /* eslint-disable-line camelcase */
|
||||
return diffFile?.parallel_diff_lines?.length > 0;
|
||||
}
|
||||
|
||||
export function isSingleViewStyle(diffFile) {
|
||||
|
|
@ -11,9 +11,5 @@ export function isSingleViewStyle(diffFile) {
|
|||
}
|
||||
|
||||
export function hasDiff(diffFile) {
|
||||
return (
|
||||
hasInlineLines(diffFile) ||
|
||||
hasParallelLines(diffFile) ||
|
||||
!diffFile?.blob?.readable_text /* eslint-disable-line camelcase */
|
||||
);
|
||||
return hasInlineLines(diffFile) || hasParallelLines(diffFile) || !diffFile?.blob?.readable_text;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ export const loadBranch = ({ dispatch, getters }, { projectId, branchId }) =>
|
|||
ref: branch.commit.id,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
.catch(err => {
|
||||
dispatch('showBranchNotFoundError', branchId);
|
||||
return Promise.reject();
|
||||
throw err;
|
||||
});
|
||||
|
||||
export const openBranch = ({ dispatch, state, getters }, { projectId, branchId, basePath }) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export function resetServiceWorkersPublicPath() {
|
|||
// see: https://webpack.js.org/guides/public-path/
|
||||
const relativeRootPath = (gon && gon.relative_url_root) || '';
|
||||
const webpackAssetPath = joinPaths(relativeRootPath, '/assets/webpack/');
|
||||
__webpack_public_path__ = webpackAssetPath; // eslint-disable-line camelcase
|
||||
__webpack_public_path__ = webpackAssetPath; // eslint-disable-line babel/camelcase
|
||||
|
||||
// monaco-editor-webpack-plugin currently (incorrectly) references the
|
||||
// public path as a property of `window`. Once this is fixed upstream we
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-param-reassign, camelcase, no-nested-ternary, no-continue */
|
||||
/* eslint-disable no-param-reassign, babel/camelcase, no-nested-ternary, no-continue */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, consistent-return, camelcase */
|
||||
/* eslint-disable func-names, consistent-return */
|
||||
|
||||
import $ from 'jquery';
|
||||
import { __ } from '../locale';
|
||||
|
|
@ -270,14 +270,14 @@ export default class BranchGraph {
|
|||
stroke: 'none',
|
||||
});
|
||||
|
||||
const avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10;
|
||||
const avatar_box_y = y - 10;
|
||||
const avatarBoxX = this.offsetX + this.unitSpace * this.mspace + 10;
|
||||
const avatarBoxY = y - 10;
|
||||
|
||||
r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({
|
||||
r.rect(avatarBoxX, avatarBoxY, 20, 20).attr({
|
||||
stroke: this.colors[commit.space],
|
||||
'stroke-width': 2,
|
||||
});
|
||||
r.image(commit.author.icon, avatar_box_x, avatar_box_y, 20, 20);
|
||||
r.image(commit.author.icon, avatarBoxX, avatarBoxY, 20, 20);
|
||||
return r
|
||||
.text(this.offsetX + this.unitSpace * this.mspace + 35, y, commit.message.split('\n')[0])
|
||||
.attr({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable no-restricted-properties, camelcase,
|
||||
/* eslint-disable no-restricted-properties, babel/camelcase,
|
||||
no-unused-expressions, default-case,
|
||||
consistent-return, no-alert, no-param-reassign, no-else-return,
|
||||
no-shadow, no-useless-escape,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { mapActions, mapState } from 'vuex';
|
|||
import { GlAlert } from '@gitlab/ui';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
|
||||
import { FETCH_SETTINGS_ERROR_MESSAGE } from '../constants';
|
||||
import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants';
|
||||
|
||||
import SettingsForm from './settings_form.vue';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,16 @@
|
|||
<script>
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import {
|
||||
GlFormGroup,
|
||||
GlToggle,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlButton,
|
||||
GlCard,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { s__, __, sprintf } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import {
|
||||
NAME_REGEX_LENGTH,
|
||||
UPDATE_SETTINGS_ERROR_MESSAGE,
|
||||
UPDATE_SETTINGS_SUCCESS_MESSAGE,
|
||||
} from '../constants';
|
||||
} from '../../shared/constants';
|
||||
import { mapComputed } from '~/vuex_shared/bindings';
|
||||
import ExpirationPolicyForm from '../../shared/components/expiration_policy_form.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlToggle,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlButton,
|
||||
GlCard,
|
||||
GlLoadingIcon,
|
||||
ExpirationPolicyForm,
|
||||
},
|
||||
mixins: [Tracking.mixin()],
|
||||
labelsConfig: {
|
||||
|
|
@ -43,59 +27,7 @@ export default {
|
|||
computed: {
|
||||
...mapState(['formOptions', 'isLoading']),
|
||||
...mapGetters({ isEdited: 'getIsEdited' }),
|
||||
...mapComputed(
|
||||
[
|
||||
'enabled',
|
||||
{ key: 'cadence', getter: 'getCadence' },
|
||||
{ key: 'older_than', getter: 'getOlderThan' },
|
||||
{ key: 'keep_n', getter: 'getKeepN' },
|
||||
'name_regex',
|
||||
],
|
||||
'updateSettings',
|
||||
'settings',
|
||||
),
|
||||
policyEnabledText() {
|
||||
return this.enabled ? __('enabled') : __('disabled');
|
||||
},
|
||||
toggleDescriptionText() {
|
||||
return sprintf(
|
||||
s__('ContainerRegistry|Docker tag expiration policy is %{toggleStatus}'),
|
||||
{
|
||||
toggleStatus: `<strong>${this.policyEnabledText}</strong>`,
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
regexHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ContainerRegistry|Wildcards such as %{codeStart}*-stable%{codeEnd} or %{codeStart}production/*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}',
|
||||
),
|
||||
{
|
||||
codeStart: '<code>',
|
||||
codeEnd: '</code>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
nameRegexPlaceholder() {
|
||||
return '.*';
|
||||
},
|
||||
nameRegexState() {
|
||||
return this.name_regex ? this.name_regex.length <= NAME_REGEX_LENGTH : null;
|
||||
},
|
||||
formIsInvalid() {
|
||||
return this.nameRegexState === false;
|
||||
},
|
||||
isFormElementDisabled() {
|
||||
return !this.enabled || this.isLoading;
|
||||
},
|
||||
isSubmitButtonDisabled() {
|
||||
return this.formIsInvalid || this.isLoading;
|
||||
},
|
||||
isCancelButtonDisabled() {
|
||||
return !this.isEdited || this.isLoading;
|
||||
},
|
||||
...mapComputed([{ key: 'settings', getter: 'getSettings' }], 'updateSettings'),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['resetSettings', 'saveSettings']),
|
||||
|
|
@ -114,127 +46,12 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<form ref="form-element" @submit.prevent="submit" @reset.prevent="reset">
|
||||
<gl-card>
|
||||
<template #header>
|
||||
{{ s__('ContainerRegistry|Tag expiration policy') }}
|
||||
</template>
|
||||
<template>
|
||||
<gl-form-group
|
||||
id="expiration-policy-toggle-group"
|
||||
:label-cols="$options.labelsConfig.cols"
|
||||
:label-align="$options.labelsConfig.align"
|
||||
label-for="expiration-policy-toggle"
|
||||
:label="s__('ContainerRegistry|Expiration policy:')"
|
||||
>
|
||||
<div class="d-flex align-items-start">
|
||||
<gl-toggle id="expiration-policy-toggle" v-model="enabled" :disabled="isLoading" />
|
||||
<span class="mb-2 ml-1 lh-2" v-html="toggleDescriptionText"></span>
|
||||
</div>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
id="expiration-policy-interval-group"
|
||||
:label-cols="$options.labelsConfig.cols"
|
||||
:label-align="$options.labelsConfig.align"
|
||||
label-for="expiration-policy-interval"
|
||||
:label="s__('ContainerRegistry|Expiration interval:')"
|
||||
>
|
||||
<gl-form-select
|
||||
id="expiration-policy-interval"
|
||||
v-model="older_than"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.olderThan" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
id="expiration-policy-schedule-group"
|
||||
:label-cols="$options.labelsConfig.cols"
|
||||
:label-align="$options.labelsConfig.align"
|
||||
label-for="expiration-policy-schedule"
|
||||
:label="s__('ContainerRegistry|Expiration schedule:')"
|
||||
>
|
||||
<gl-form-select
|
||||
id="expiration-policy-schedule"
|
||||
v-model="cadence"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.cadence" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
id="expiration-policy-latest-group"
|
||||
:label-cols="$options.labelsConfig.cols"
|
||||
:label-align="$options.labelsConfig.align"
|
||||
label-for="expiration-policy-latest"
|
||||
:label="s__('ContainerRegistry|Number of tags to retain:')"
|
||||
>
|
||||
<gl-form-select
|
||||
id="expiration-policy-latest"
|
||||
v-model="keep_n"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.keepN" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
id="expiration-policy-name-matching-group"
|
||||
:label-cols="$options.labelsConfig.cols"
|
||||
:label-align="$options.labelsConfig.align"
|
||||
label-for="expiration-policy-name-matching"
|
||||
:label="
|
||||
s__('ContainerRegistry|Docker tags with names matching this regex pattern will expire:')
|
||||
"
|
||||
:state="nameRegexState"
|
||||
:invalid-feedback="
|
||||
s__('ContainerRegistry|The value of this input should be less than 255 characters')
|
||||
"
|
||||
>
|
||||
<gl-form-textarea
|
||||
id="expiration-policy-name-matching"
|
||||
v-model="name_regex"
|
||||
:placeholder="nameRegexPlaceholder"
|
||||
:state="nameRegexState"
|
||||
:disabled="isFormElementDisabled"
|
||||
trim
|
||||
/>
|
||||
<template #description>
|
||||
<span ref="regex-description" v-html="regexHelpText"></span>
|
||||
</template>
|
||||
</gl-form-group>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gl-button
|
||||
ref="cancel-button"
|
||||
type="reset"
|
||||
:disabled="isCancelButtonDisabled"
|
||||
class="mr-2 d-block"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
ref="save-button"
|
||||
type="submit"
|
||||
:disabled="isSubmitButtonDisabled"
|
||||
variant="success"
|
||||
class="d-flex justify-content-center align-items-center js-no-auto-disable"
|
||||
>
|
||||
{{ __('Save expiration policy') }}
|
||||
<gl-loading-icon v-if="isLoading" class="ml-2" />
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
</gl-card>
|
||||
</form>
|
||||
<expiration-policy-form
|
||||
v-model="settings"
|
||||
:form-options="formOptions"
|
||||
:is-loading="isLoading"
|
||||
:disable-cancel-button="!isEdited"
|
||||
@submit="submit"
|
||||
@reset="reset"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
import { isEqual } from 'lodash';
|
||||
import { findDefaultOption } from '../utils';
|
||||
import { findDefaultOption } from '../../shared/utils';
|
||||
|
||||
export const getCadence = state =>
|
||||
state.settings.cadence || findDefaultOption(state.formOptions.cadence);
|
||||
|
||||
export const getKeepN = state =>
|
||||
state.settings.keep_n || findDefaultOption(state.formOptions.keepN);
|
||||
|
||||
export const getOlderThan = state =>
|
||||
state.settings.older_than || findDefaultOption(state.formOptions.olderThan);
|
||||
|
||||
export const getSettings = (state, getters) => ({
|
||||
enabled: state.settings.enabled,
|
||||
cadence: getters.getCadence,
|
||||
older_than: getters.getOlderThan,
|
||||
keep_n: getters.getKeepN,
|
||||
name_regex: state.settings.name_regex,
|
||||
});
|
||||
|
||||
export const getIsEdited = state => !isEqual(state.original, state.settings);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export default {
|
|||
olderThan: JSON.parse(initialState.olderThanOptions),
|
||||
};
|
||||
},
|
||||
[types.UPDATE_SETTINGS](state, settings) {
|
||||
state.settings = { ...state.settings, ...settings };
|
||||
[types.UPDATE_SETTINGS](state, data) {
|
||||
state.settings = { ...state.settings, ...data.settings };
|
||||
},
|
||||
[types.SET_SETTINGS](state, settings) {
|
||||
state.settings = settings;
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
export const findDefaultOption = options => {
|
||||
const item = options.find(o => o.default);
|
||||
return item ? item.key : null;
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
<script>
|
||||
import { uniqueId } from 'lodash';
|
||||
import {
|
||||
GlFormGroup,
|
||||
GlToggle,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlButton,
|
||||
GlCard,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { s__, __, sprintf } from '~/locale';
|
||||
import { NAME_REGEX_LENGTH } from '../constants';
|
||||
import { mapComputedToEvent } from '../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlToggle,
|
||||
GlFormSelect,
|
||||
GlFormTextarea,
|
||||
GlButton,
|
||||
GlCard,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
formOptions: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
labelCols: {
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: 3,
|
||||
},
|
||||
labelAlign: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'right',
|
||||
},
|
||||
disableCancelButton: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
nameRegexPlaceholder: '.*',
|
||||
data() {
|
||||
return {
|
||||
uniqueId: uniqueId(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapComputedToEvent(['enabled', 'cadence', 'older_than', 'keep_n', 'name_regex'], 'value'),
|
||||
policyEnabledText() {
|
||||
return this.enabled ? __('enabled') : __('disabled');
|
||||
},
|
||||
toggleDescriptionText() {
|
||||
return sprintf(
|
||||
s__('ContainerRegistry|Docker tag expiration policy is %{toggleStatus}'),
|
||||
{
|
||||
toggleStatus: `<strong>${this.policyEnabledText}</strong>`,
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
regexHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ContainerRegistry|Wildcards such as %{codeStart}*-stable%{codeEnd} or %{codeStart}production/*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}',
|
||||
),
|
||||
{
|
||||
codeStart: '<code>',
|
||||
codeEnd: '</code>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
nameRegexState() {
|
||||
return this.name_regex ? this.name_regex.length <= NAME_REGEX_LENGTH : null;
|
||||
},
|
||||
formIsInvalid() {
|
||||
return this.nameRegexState === false;
|
||||
},
|
||||
isFormElementDisabled() {
|
||||
return !this.enabled || this.isLoading;
|
||||
},
|
||||
isSubmitButtonDisabled() {
|
||||
return this.formIsInvalid || this.isLoading;
|
||||
},
|
||||
isCancelButtonDisabled() {
|
||||
return this.disableCancelButton || this.isLoading;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
idGenerator(id) {
|
||||
return `${id}_${this.uniqueId}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form
|
||||
ref="form-element"
|
||||
class="lh-2"
|
||||
@submit.prevent="$emit('submit')"
|
||||
@reset.prevent="$emit('reset')"
|
||||
>
|
||||
<gl-card>
|
||||
<template #header>
|
||||
{{ s__('ContainerRegistry|Tag expiration policy') }}
|
||||
</template>
|
||||
<template>
|
||||
<gl-form-group
|
||||
:id="idGenerator('expiration-policy-toggle-group')"
|
||||
:label-cols="labelCols"
|
||||
:label-align="labelAlign"
|
||||
:label-for="idGenerator('expiration-policy-toggle')"
|
||||
:label="s__('ContainerRegistry|Expiration policy:')"
|
||||
>
|
||||
<div class="d-flex align-items-start">
|
||||
<gl-toggle
|
||||
:id="idGenerator('expiration-policy-toggle')"
|
||||
v-model="enabled"
|
||||
:disabled="isLoading"
|
||||
/>
|
||||
<span class="mb-2 ml-1 lh-2" v-html="toggleDescriptionText"></span>
|
||||
</div>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
:id="idGenerator('expiration-policy-interval-group')"
|
||||
:label-cols="labelCols"
|
||||
:label-align="labelAlign"
|
||||
:label-for="idGenerator('expiration-policy-interval')"
|
||||
:label="s__('ContainerRegistry|Expiration interval:')"
|
||||
>
|
||||
<gl-form-select
|
||||
:id="idGenerator('expiration-policy-interval')"
|
||||
v-model="older_than"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.olderThan" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
:id="idGenerator('expiration-policy-schedule-group')"
|
||||
:label-cols="labelCols"
|
||||
:label-align="labelAlign"
|
||||
:label-for="idGenerator('expiration-policy-schedule')"
|
||||
:label="s__('ContainerRegistry|Expiration schedule:')"
|
||||
>
|
||||
<gl-form-select
|
||||
:id="idGenerator('expiration-policy-schedule')"
|
||||
v-model="cadence"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.cadence" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
:id="idGenerator('expiration-policy-latest-group')"
|
||||
:label-cols="labelCols"
|
||||
:label-align="labelAlign"
|
||||
:label-for="idGenerator('expiration-policy-latest')"
|
||||
:label="s__('ContainerRegistry|Number of tags to retain:')"
|
||||
>
|
||||
<gl-form-select
|
||||
:id="idGenerator('expiration-policy-latest')"
|
||||
v-model="keep_n"
|
||||
:disabled="isFormElementDisabled"
|
||||
>
|
||||
<option v-for="option in formOptions.keepN" :key="option.key" :value="option.key">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</gl-form-select>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
:id="idGenerator('expiration-policy-name-matching-group')"
|
||||
:label-cols="labelCols"
|
||||
:label-align="labelAlign"
|
||||
:label-for="idGenerator('expiration-policy-name-matching')"
|
||||
:label="
|
||||
s__('ContainerRegistry|Docker tags with names matching this regex pattern will expire:')
|
||||
"
|
||||
:state="nameRegexState"
|
||||
:invalid-feedback="
|
||||
s__('ContainerRegistry|The value of this input should be less than 255 characters')
|
||||
"
|
||||
>
|
||||
<gl-form-textarea
|
||||
:id="idGenerator('expiration-policy-name-matching')"
|
||||
v-model="name_regex"
|
||||
:placeholder="$options.nameRegexPlaceholder"
|
||||
:state="nameRegexState"
|
||||
:disabled="isFormElementDisabled"
|
||||
trim
|
||||
/>
|
||||
<template #description>
|
||||
<span ref="regex-description" v-html="regexHelpText"></span>
|
||||
</template>
|
||||
</gl-form-group>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="d-flex justify-content-end">
|
||||
<gl-button
|
||||
ref="cancel-button"
|
||||
type="reset"
|
||||
class="mr-2 d-block"
|
||||
:disabled="isCancelButtonDisabled"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
ref="save-button"
|
||||
type="submit"
|
||||
:disabled="isSubmitButtonDisabled"
|
||||
variant="success"
|
||||
class="d-flex justify-content-center align-items-center js-no-auto-disable"
|
||||
>
|
||||
{{ __('Save expiration policy') }}
|
||||
<gl-loading-icon v-if="isLoading" class="ml-2" />
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
</gl-card>
|
||||
</form>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
export const findDefaultOption = options => {
|
||||
const item = options.find(o => o.default);
|
||||
return item ? item.key : null;
|
||||
};
|
||||
|
||||
export const mapComputedToEvent = (list, root) => {
|
||||
const result = {};
|
||||
list.forEach(e => {
|
||||
result[e] = {
|
||||
get() {
|
||||
return this[root][e];
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('input', { ...this[root], [e]: value });
|
||||
},
|
||||
};
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, prefer-rest-params, consistent-return, no-shadow, no-else-return, no-self-compare, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */
|
||||
/* eslint-disable func-names, prefer-rest-params, consistent-return, no-shadow, no-else-return, no-self-compare, no-unused-expressions, yoda, prefer-spread, babel/camelcase, no-param-reassign */
|
||||
/* global Issuable */
|
||||
/* global emitSidebarEvent */
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export default {
|
|||
:title="title"
|
||||
>
|
||||
<slot>
|
||||
<icon name="duplicate" />
|
||||
<icon name="copy-to-clipboard" />
|
||||
</slot>
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
*/
|
||||
|
||||
if (gon && gon.webpack_public_path) {
|
||||
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line camelcase
|
||||
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line babel/camelcase
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable consistent-return, camelcase, class-methods-use-this */
|
||||
/* eslint-disable consistent-return, class-methods-use-this */
|
||||
|
||||
// Zen Mode (full screen) textarea
|
||||
//
|
||||
|
|
@ -91,8 +91,8 @@ export default class ZenMode {
|
|||
}
|
||||
}
|
||||
|
||||
scrollTo(zen_area) {
|
||||
return $.scrollTo(zen_area, 0, {
|
||||
scrollTo(zenArea) {
|
||||
return $.scrollTo(zenArea, 0, {
|
||||
offset: -150,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ module ButtonHelper
|
|||
}
|
||||
|
||||
content_tag :button, button_attributes do
|
||||
concat(sprite_icon('duplicate')) unless hide_button_icon
|
||||
concat(sprite_icon('copy-to-clipboard')) unless hide_button_icon
|
||||
concat(button_text)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DeleteWithLimit
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def delete_with_limit(maximum)
|
||||
limit(maximum).delete_all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,8 @@ class Event < ApplicationRecord
|
|||
include Sortable
|
||||
include FromUnion
|
||||
include Presentable
|
||||
include DeleteWithLimit
|
||||
include CreatedAtFilterable
|
||||
|
||||
default_scope { reorder(nil) }
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
class WebHookLog < ApplicationRecord
|
||||
include SafeUrl
|
||||
include Presentable
|
||||
include DeleteWithLimit
|
||||
include CreatedAtFilterable
|
||||
|
||||
belongs_to :web_hook
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ class EmailsOnPushService < Service
|
|||
end
|
||||
|
||||
def initialize_properties
|
||||
if properties.nil?
|
||||
self.properties = {}
|
||||
self.branches_to_be_notified ||= "all"
|
||||
end
|
||||
super
|
||||
|
||||
self.branches_to_be_notified = 'all' if branches_to_be_notified.nil?
|
||||
end
|
||||
|
||||
def execute(push_data)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
.card-header
|
||||
.float-right
|
||||
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
|
||||
= sprite_icon('duplicate')
|
||||
= sprite_icon('copy-to-clipboard')
|
||||
%pre.hidden
|
||||
= @query.formatted_query
|
||||
%strong
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
.card-header
|
||||
.float-right
|
||||
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
|
||||
= sprite_icon('duplicate')
|
||||
= sprite_icon('copy-to-clipboard')
|
||||
%pre.hidden
|
||||
= @query.explain
|
||||
%strong
|
||||
|
|
|
|||
|
|
@ -6,18 +6,12 @@ class PruneOldEventsWorker
|
|||
|
||||
feature_category_not_owned!
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
DELETE_LIMIT = 10_000
|
||||
|
||||
def perform
|
||||
# Contribution calendar shows maximum 12 months of events, we retain 3 years for data integrity.
|
||||
# Double nested query is used because MySQL doesn't allow DELETE subqueries on the same table.
|
||||
Event.unscoped.where(
|
||||
'(id IN (SELECT id FROM (?) ids_to_remove))',
|
||||
Event.unscoped.where(
|
||||
'created_at < ?',
|
||||
(3.years + 1.day).ago)
|
||||
.select(:id)
|
||||
.limit(10_000))
|
||||
.delete_all
|
||||
cutoff_date = (3.years + 1.day).ago
|
||||
|
||||
Event.unscoped.created_before(cutoff_date).delete_with_limit(DELETE_LIMIT)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,20 +11,9 @@ class PruneWebHookLogsWorker
|
|||
# The maximum number of rows to remove in a single job.
|
||||
DELETE_LIMIT = 50_000
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform
|
||||
# MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner
|
||||
# query refers to the same table. To work around this we wrap the IN body in
|
||||
# another sub query.
|
||||
WebHookLog
|
||||
.where(
|
||||
'id IN (SELECT id FROM (?) ids_to_remove)',
|
||||
WebHookLog
|
||||
.select(:id)
|
||||
.where('created_at < ?', 90.days.ago.beginning_of_day)
|
||||
.limit(DELETE_LIMIT)
|
||||
)
|
||||
.delete_all
|
||||
cutoff_date = 90.days.ago.beginning_of_day
|
||||
|
||||
WebHookLog.created_before(cutoff_date).delete_with_limit(DELETE_LIMIT)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix emails on push integrations created before 12.7
|
||||
merge_request: 23699
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change vague copy to clipboard icon to a clearer icon.
|
||||
merge_request: 23983
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -6,6 +6,8 @@ GraphQL::Field.accepts_definitions(authorize: GraphQL::Define.assign_metadata_ke
|
|||
GraphQL::Schema::Object.accepts_definition(:authorize)
|
||||
GraphQL::Schema::Field.accepts_definition(:authorize)
|
||||
|
||||
GitlabSchema.middleware << GraphQL::Schema::TimeoutMiddleware.new(max_seconds: ENV.fetch('GITLAB_RAILS_GRAPHQL_TIMEOUT', 30).to_i) do |timeout_error, query|
|
||||
Gitlab::GraphqlLogger.error(message: timeout_error.to_s, query: query.query_string, query_variables: query.provided_variables)
|
||||
Gitlab::Application.config.after_initialize do
|
||||
GitlabSchema.middleware << GraphQL::Schema::TimeoutMiddleware.new(max_seconds: ENV.fetch('GITLAB_RAILS_GRAPHQL_TIMEOUT', 30).to_i) do |timeout_error, query|
|
||||
Gitlab::GraphqlLogger.error(message: timeout_error.to_s, query: query.query_string, query_variables: query.provided_variables)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.5.0",
|
||||
"@gitlab/eslint-config": "^2.0.0",
|
||||
"@gitlab/eslint-config": "^2.1.1",
|
||||
"@gitlab/eslint-plugin-i18n": "^1.1.0",
|
||||
"@gitlab/eslint-plugin-vue-i18n": "^1.2.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.30",
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ describe 'Projects > Settings > For a forked project', :js do
|
|||
|
||||
within('div#project-dropdown') do
|
||||
click_button('Select project')
|
||||
click_button('Sentry | Internal')
|
||||
click_button('Sentry | internal')
|
||||
end
|
||||
|
||||
click_button('Save changes')
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ describe 'Project > Settings > CI/CD > Container registry tag expiration policy'
|
|||
it 'saves expiration policy submit the form' do
|
||||
within '#js-registry-policies' do
|
||||
within '.card-body' do
|
||||
find('#expiration-policy-toggle button:not(.is-disabled)').click
|
||||
select('7 days until tags are automatically removed', from: 'expiration-policy-interval')
|
||||
select('Every day', from: 'expiration-policy-schedule')
|
||||
select('50 tags per image name', from: 'expiration-policy-latest')
|
||||
fill_in('expiration-policy-name-matching', with: '*-production')
|
||||
find('.gl-toggle-wrapper button:not(.is-disabled)').click
|
||||
select('7 days until tags are automatically removed', from: 'Expiration interval:')
|
||||
select('Every day', from: 'Expiration schedule:')
|
||||
select('50 tags per image name', from: 'Number of tags to retain:')
|
||||
fill_in('Docker tags with names matching this regex pattern will expire:', with: '*-production')
|
||||
end
|
||||
submit_button = find('.card-footer .btn.btn-success')
|
||||
expect(submit_button).not_to be_disabled
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ describe('Error Tracking Settings - Getters', () => {
|
|||
it('should display correctly when a project is selected', () => {
|
||||
[state.selectedProject] = projectList;
|
||||
|
||||
expect(getters.dropdownLabel(state, mockGetters)).toEqual('organizationName | name');
|
||||
expect(getters.dropdownLabel(state, mockGetters)).toEqual('organizationName | slug');
|
||||
});
|
||||
|
||||
it('should display correctly when no project is selected', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Vuex from 'vuex';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import IdeStatusList from '~/ide/components/ide_status_list';
|
||||
import IdeStatusList from '~/ide/components/ide_status_list.vue';
|
||||
|
||||
const TEST_FILE = {
|
||||
name: 'lorem.md',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import MRPopover from '~/mr_popover/components/mr_popover';
|
||||
import MRPopover from '~/mr_popover/components/mr_popover.vue';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
|
||||
describe('MR Popover', () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import component from '~/registry/settings/components/registry_settings_app.vue'
|
|||
import SettingsForm from '~/registry/settings/components/settings_form.vue';
|
||||
import { createStore } from '~/registry/settings/store/';
|
||||
import { SET_IS_DISABLED } from '~/registry/settings/store/mutation_types';
|
||||
import { FETCH_SETTINGS_ERROR_MESSAGE } from '~/registry/settings/constants';
|
||||
import { FETCH_SETTINGS_ERROR_MESSAGE } from '~/registry/shared/constants';
|
||||
|
||||
describe('Registry Settings App', () => {
|
||||
let wrapper;
|
||||
|
|
|
|||
|
|
@ -1,49 +1,33 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Tracking from '~/tracking';
|
||||
import stubChildren from 'helpers/stub_children';
|
||||
import component from '~/registry/settings/components/settings_form.vue';
|
||||
import expirationPolicyForm from '~/registry/shared/components/expiration_policy_form.vue';
|
||||
import { createStore } from '~/registry/settings/store/';
|
||||
import {
|
||||
NAME_REGEX_LENGTH,
|
||||
UPDATE_SETTINGS_ERROR_MESSAGE,
|
||||
UPDATE_SETTINGS_SUCCESS_MESSAGE,
|
||||
} from '~/registry/settings/constants';
|
||||
import { stringifiedFormOptions } from '../mock_data';
|
||||
} from '~/registry/shared/constants';
|
||||
import { stringifiedFormOptions } from '../../shared/mock_data';
|
||||
|
||||
describe('Settings Form', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let dispatchSpy;
|
||||
|
||||
const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
|
||||
const trackingPayload = {
|
||||
label: 'docker_container_retention_and_expiration_policies',
|
||||
};
|
||||
|
||||
const GlLoadingIcon = { name: 'gl-loading-icon-stub', template: '<svg></svg>' };
|
||||
const findForm = () => wrapper.find(expirationPolicyForm);
|
||||
|
||||
const findFormGroup = name => wrapper.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}-group`);
|
||||
const findFormElements = (name, parent = wrapper) =>
|
||||
parent.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}`);
|
||||
const findCancelButton = () => wrapper.find({ ref: 'cancel-button' });
|
||||
const findSaveButton = () => wrapper.find({ ref: 'save-button' });
|
||||
const findForm = () => wrapper.find({ ref: 'form-element' });
|
||||
const findLoadingIcon = (parent = wrapper) => parent.find(GlLoadingIcon);
|
||||
|
||||
const mountComponent = (options = {}) => {
|
||||
wrapper = mount(component, {
|
||||
stubs: {
|
||||
...stubChildren(component),
|
||||
GlCard: false,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
const mountComponent = () => {
|
||||
wrapper = shallowMount(component, {
|
||||
mocks: {
|
||||
$toast: {
|
||||
show: jest.fn(),
|
||||
},
|
||||
},
|
||||
store,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -59,170 +43,50 @@ describe('Settings Form', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe.each`
|
||||
elementName | modelName | value | disabledByToggle
|
||||
${'toggle'} | ${'enabled'} | ${true} | ${'not disabled'}
|
||||
${'interval'} | ${'older_than'} | ${'foo'} | ${'disabled'}
|
||||
${'schedule'} | ${'cadence'} | ${'foo'} | ${'disabled'}
|
||||
${'latest'} | ${'keep_n'} | ${'foo'} | ${'disabled'}
|
||||
${'name-matching'} | ${'name_regex'} | ${'foo'} | ${'disabled'}
|
||||
`(
|
||||
`${FORM_ELEMENTS_ID_PREFIX}-$elementName form element`,
|
||||
({ elementName, modelName, value, disabledByToggle }) => {
|
||||
let formGroup;
|
||||
beforeEach(() => {
|
||||
formGroup = findFormGroup(elementName);
|
||||
});
|
||||
it(`${elementName} form group exist in the dom`, () => {
|
||||
expect(formGroup.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-for property`, () => {
|
||||
expect(formGroup.attributes('label-for')).toBe(`expiration-policy-${elementName}`);
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-cols property`, () => {
|
||||
expect(formGroup.attributes('label-cols')).toBe(`${wrapper.vm.$options.labelsConfig.cols}`);
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-align property`, () => {
|
||||
expect(formGroup.attributes('label-align')).toBe(
|
||||
`${wrapper.vm.$options.labelsConfig.align}`,
|
||||
);
|
||||
});
|
||||
|
||||
it(`${elementName} form group contains an input element`, () => {
|
||||
expect(findFormElements(elementName, formGroup).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it(`${elementName} form element change updated ${modelName} with ${value}`, () => {
|
||||
const element = findFormElements(elementName, formGroup);
|
||||
const modelUpdateEvent = element.vm.$options.model
|
||||
? element.vm.$options.model.event
|
||||
: 'input';
|
||||
element.vm.$emit(modelUpdateEvent, value);
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.vm[modelName]).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
it(`${elementName} is ${disabledByToggle} by enabled set to false`, () => {
|
||||
store.dispatch('updateSettings', { enabled: false });
|
||||
const expectation = disabledByToggle === 'disabled' ? 'true' : undefined;
|
||||
expect(findFormElements(elementName, formGroup).attributes('disabled')).toBe(expectation);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
describe('form actions', () => {
|
||||
describe('form', () => {
|
||||
let form;
|
||||
beforeEach(() => {
|
||||
form = findForm();
|
||||
});
|
||||
|
||||
describe('cancel button', () => {
|
||||
it('has type reset', () => {
|
||||
expect(findCancelButton().attributes('type')).toBe('reset');
|
||||
});
|
||||
|
||||
it('is disabled the form was not changed from his original value', () => {
|
||||
store.dispatch('receiveSettingsSuccess', { foo: 'bar' });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
it('is disabled when the form data is loading', () => {
|
||||
store.dispatch('toggleLoading');
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
it('is enabled when the user changed something in the form and the data is not being loaded', () => {
|
||||
store.dispatch('receiveSettingsSuccess', { foo: 'bar' });
|
||||
store.dispatch('updateSettings', { foo: 'baz' });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe(undefined);
|
||||
});
|
||||
describe('data binding', () => {
|
||||
it('v-model change update the settings property', () => {
|
||||
dispatchSpy.mockReturnValue();
|
||||
form.vm.$emit('input', 'foo');
|
||||
expect(dispatchSpy).toHaveBeenCalledWith('updateSettings', { settings: 'foo' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('form cancel event', () => {
|
||||
describe('form reset event', () => {
|
||||
it('calls the appropriate function', () => {
|
||||
dispatchSpy.mockReturnValue();
|
||||
form.trigger('reset');
|
||||
form.vm.$emit('reset');
|
||||
expect(dispatchSpy).toHaveBeenCalledWith('resetSettings');
|
||||
});
|
||||
|
||||
it('tracks the reset event', () => {
|
||||
dispatchSpy.mockReturnValue();
|
||||
form.trigger('reset');
|
||||
form.vm.$emit('reset');
|
||||
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'reset_form', trackingPayload);
|
||||
});
|
||||
});
|
||||
|
||||
it('save has type submit', () => {
|
||||
expect(findSaveButton().attributes('type')).toBe('submit');
|
||||
});
|
||||
|
||||
describe('when isLoading is true', () => {
|
||||
beforeEach(() => {
|
||||
store.dispatch('toggleLoading');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
store.dispatch('toggleLoading');
|
||||
});
|
||||
|
||||
it.each`
|
||||
elementName
|
||||
${'toggle'}
|
||||
${'interval'}
|
||||
${'schedule'}
|
||||
${'latest'}
|
||||
${'name-matching'}
|
||||
`(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
|
||||
expect(findFormElements(elementName).attributes('disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('submit button is disabled and shows a spinner', () => {
|
||||
const button = findSaveButton();
|
||||
expect(button.attributes('disabled')).toBeTruthy();
|
||||
expect(findLoadingIcon(button)).toExist();
|
||||
});
|
||||
|
||||
it('cancel button is disabled', () => {
|
||||
expect(findCancelButton().attributes('disabled')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form submit event ', () => {
|
||||
it('calls the appropriate function', () => {
|
||||
dispatchSpy.mockResolvedValue();
|
||||
form.trigger('submit');
|
||||
expect(dispatchSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('dispatches the saveSettings action', () => {
|
||||
dispatchSpy.mockResolvedValue();
|
||||
form.trigger('submit');
|
||||
form.vm.$emit('submit');
|
||||
expect(dispatchSpy).toHaveBeenCalledWith('saveSettings');
|
||||
});
|
||||
|
||||
it('tracks the submit event', () => {
|
||||
dispatchSpy.mockResolvedValue();
|
||||
form.trigger('submit');
|
||||
form.vm.$emit('submit');
|
||||
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'submit_form', trackingPayload);
|
||||
});
|
||||
|
||||
it('show a success toast when submit succeed', () => {
|
||||
dispatchSpy.mockResolvedValue();
|
||||
form.trigger('submit');
|
||||
form.vm.$emit('submit');
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE, {
|
||||
type: 'success',
|
||||
|
|
@ -232,7 +96,7 @@ describe('Settings Form', () => {
|
|||
|
||||
it('show an error toast when submit fails', () => {
|
||||
dispatchSpy.mockRejectedValue();
|
||||
form.trigger('submit');
|
||||
form.vm.$emit('submit');
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_ERROR_MESSAGE, {
|
||||
type: 'error',
|
||||
|
|
@ -241,45 +105,4 @@ describe('Settings Form', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form validation', () => {
|
||||
describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => {
|
||||
const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(',');
|
||||
beforeEach(() => {
|
||||
store.dispatch('updateSettings', { name_regex: invalidString });
|
||||
});
|
||||
|
||||
it('save btn is disabled', () => {
|
||||
expect(findSaveButton().attributes('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('nameRegexState is false', () => {
|
||||
expect(wrapper.vm.nameRegexState).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('if the user did not type validation is null', () => {
|
||||
store.dispatch('updateSettings', { name_regex: null });
|
||||
expect(wrapper.vm.nameRegexState).toBe(null);
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findSaveButton().attributes('disabled')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it(`if the user typed and is less than ${NAME_REGEX_LENGTH} state is true`, () => {
|
||||
store.dispatch('updateSettings', { name_regex: 'abc' });
|
||||
expect(wrapper.vm.nameRegexState).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('help text', () => {
|
||||
it('toggleDescriptionText text reflects enabled property', () => {
|
||||
const toggleHelpText = findFormGroup('toggle').find('span');
|
||||
expect(toggleHelpText.html()).toContain('disabled');
|
||||
wrapper.setData({ enabled: true });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(toggleHelpText.html()).toContain('enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,11 +10,14 @@ describe('Actions Registry Store', () => {
|
|||
${'updateSettings'} | ${types.UPDATE_SETTINGS} | ${'foo'}
|
||||
${'toggleLoading'} | ${types.TOGGLE_LOADING} | ${undefined}
|
||||
${'resetSettings'} | ${types.RESET_SETTINGS} | ${undefined}
|
||||
`('%s action invokes %s mutation with payload %s', ({ actionName, mutationName, payload }) => {
|
||||
it('should set the initial state', done => {
|
||||
testAction(actions[actionName], payload, {}, [{ type: mutationName, payload }], [], done);
|
||||
});
|
||||
});
|
||||
`(
|
||||
'$actionName invokes $mutationName with payload $payload',
|
||||
({ actionName, mutationName, payload }) => {
|
||||
it('should set state', done => {
|
||||
testAction(actions[actionName], payload, {}, [{ type: mutationName, payload }], [], done);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
describe('receiveSettingsSuccess', () => {
|
||||
it('calls SET_SETTINGS when data is present', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as getters from '~/registry/settings/store/getters';
|
||||
import * as utils from '~/registry/settings/utils';
|
||||
import { formOptions } from '../mock_data';
|
||||
import * as utils from '~/registry/shared/utils';
|
||||
import { formOptions } from '../../shared/mock_data';
|
||||
|
||||
describe('Getters registry settings store', () => {
|
||||
const settings = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import mutations from '~/registry/settings/store/mutations';
|
||||
import * as types from '~/registry/settings/store/mutation_types';
|
||||
import createState from '~/registry/settings/store/state';
|
||||
import { formOptions, stringifiedFormOptions } from '../mock_data';
|
||||
import { formOptions, stringifiedFormOptions } from '../../shared/mock_data';
|
||||
|
||||
describe('Mutations Registry Store', () => {
|
||||
let mockState;
|
||||
|
|
@ -28,7 +28,7 @@ describe('Mutations Registry Store', () => {
|
|||
mockState.settings = { foo: 'bar' };
|
||||
const payload = { foo: 'baz' };
|
||||
const expectedState = { ...mockState, settings: payload };
|
||||
mutations[types.UPDATE_SETTINGS](mockState, payload);
|
||||
mutations[types.UPDATE_SETTINGS](mockState, { settings: payload });
|
||||
expect(mockState.settings).toEqual(expectedState.settings);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Settings Form renders 1`] = `
|
||||
<form>
|
||||
exports[`Expiration Policy Form renders 1`] = `
|
||||
<form
|
||||
class="lh-2"
|
||||
>
|
||||
<div
|
||||
class="card"
|
||||
>
|
||||
|
|
@ -56,7 +58,6 @@ exports[`Settings Form renders 1`] = `
|
|||
<glformselect-stub
|
||||
disabled="true"
|
||||
id="expiration-policy-interval"
|
||||
value="bar"
|
||||
>
|
||||
<option
|
||||
value="foo"
|
||||
|
|
@ -85,7 +86,6 @@ exports[`Settings Form renders 1`] = `
|
|||
<glformselect-stub
|
||||
disabled="true"
|
||||
id="expiration-policy-schedule"
|
||||
value="bar"
|
||||
>
|
||||
<option
|
||||
value="foo"
|
||||
|
|
@ -114,7 +114,6 @@ exports[`Settings Form renders 1`] = `
|
|||
<glformselect-stub
|
||||
disabled="true"
|
||||
id="expiration-policy-latest"
|
||||
value="bar"
|
||||
>
|
||||
<option
|
||||
value="foo"
|
||||
|
|
@ -159,7 +158,6 @@ exports[`Settings Form renders 1`] = `
|
|||
>
|
||||
<glbutton-stub
|
||||
class="mr-2 d-block"
|
||||
disabled="true"
|
||||
size="md"
|
||||
type="reset"
|
||||
variant="secondary"
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import stubChildren from 'helpers/stub_children';
|
||||
import component from '~/registry/shared/components/expiration_policy_form.vue';
|
||||
|
||||
import { NAME_REGEX_LENGTH } from '~/registry/shared/constants';
|
||||
import { formOptions } from '../mock_data';
|
||||
|
||||
describe('Expiration Policy Form', () => {
|
||||
let wrapper;
|
||||
|
||||
const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
|
||||
|
||||
const GlLoadingIcon = { name: 'gl-loading-icon-stub', template: '<svg></svg>' };
|
||||
|
||||
const findFormGroup = name => wrapper.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}-group`);
|
||||
const findFormElements = (name, parent = wrapper) =>
|
||||
parent.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}`);
|
||||
const findCancelButton = () => wrapper.find({ ref: 'cancel-button' });
|
||||
const findSaveButton = () => wrapper.find({ ref: 'save-button' });
|
||||
const findForm = () => wrapper.find({ ref: 'form-element' });
|
||||
const findLoadingIcon = (parent = wrapper) => parent.find(GlLoadingIcon);
|
||||
|
||||
const mountComponent = props => {
|
||||
wrapper = mount(component, {
|
||||
stubs: {
|
||||
...stubChildren(component),
|
||||
GlCard: false,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
propsData: {
|
||||
formOptions,
|
||||
...props,
|
||||
},
|
||||
methods: {
|
||||
// override idGenerator to avoid having to test with dynamic uid
|
||||
idGenerator: value => value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
mountComponent();
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe.each`
|
||||
elementName | modelName | value | disabledByToggle
|
||||
${'toggle'} | ${'enabled'} | ${true} | ${'not disabled'}
|
||||
${'interval'} | ${'older_than'} | ${'foo'} | ${'disabled'}
|
||||
${'schedule'} | ${'cadence'} | ${'foo'} | ${'disabled'}
|
||||
${'latest'} | ${'keep_n'} | ${'foo'} | ${'disabled'}
|
||||
${'name-matching'} | ${'name_regex'} | ${'foo'} | ${'disabled'}
|
||||
`(
|
||||
`${FORM_ELEMENTS_ID_PREFIX}-$elementName form element`,
|
||||
({ elementName, modelName, value, disabledByToggle }) => {
|
||||
it(`${elementName} form group exist in the dom`, () => {
|
||||
mountComponent();
|
||||
const formGroup = findFormGroup(elementName);
|
||||
expect(formGroup.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-for property`, () => {
|
||||
mountComponent();
|
||||
const formGroup = findFormGroup(elementName);
|
||||
expect(formGroup.attributes('label-for')).toBe(`expiration-policy-${elementName}`);
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-cols property`, () => {
|
||||
mountComponent({ labelCols: '1' });
|
||||
const formGroup = findFormGroup(elementName);
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(formGroup.attributes('label-cols')).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
it(`${elementName} form group has a label-align property`, () => {
|
||||
mountComponent({ labelAlign: 'foo' });
|
||||
const formGroup = findFormGroup(elementName);
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(formGroup.attributes('label-align')).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
it(`${elementName} form group contains an input element`, () => {
|
||||
mountComponent();
|
||||
const formGroup = findFormGroup(elementName);
|
||||
expect(findFormElements(elementName, formGroup).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it(`${elementName} form element change updated ${modelName} with ${value}`, () => {
|
||||
mountComponent();
|
||||
const formGroup = findFormGroup(elementName);
|
||||
const element = findFormElements(elementName, formGroup);
|
||||
|
||||
const modelUpdateEvent = element.vm.$options.model
|
||||
? element.vm.$options.model.event
|
||||
: 'input';
|
||||
element.vm.$emit(modelUpdateEvent, value);
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.emitted('input')).toEqual([[{ [modelName]: value }]]);
|
||||
});
|
||||
});
|
||||
|
||||
it(`${elementName} is ${disabledByToggle} by enabled set to false`, () => {
|
||||
mountComponent({ settings: { enabled: false } });
|
||||
const formGroup = findFormGroup(elementName);
|
||||
const expectation = disabledByToggle === 'disabled' ? 'true' : undefined;
|
||||
expect(findFormElements(elementName, formGroup).attributes('disabled')).toBe(expectation);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
describe('form actions', () => {
|
||||
describe('cancel button', () => {
|
||||
it('has type reset', () => {
|
||||
mountComponent();
|
||||
expect(findCancelButton().attributes('type')).toBe('reset');
|
||||
});
|
||||
|
||||
it('is disabled when disableCancelButton is true', () => {
|
||||
mountComponent({ disableCancelButton: true });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
it('is disabled isLoading is true', () => {
|
||||
mountComponent({ isLoading: true });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
it('is enabled when isLoading and disableCancelButton are false', () => {
|
||||
mountComponent({ disableCancelButton: false, isLoading: false });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findCancelButton().attributes('disabled')).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form cancel event', () => {
|
||||
it('calls the appropriate function', () => {
|
||||
mountComponent();
|
||||
findForm().trigger('reset');
|
||||
expect(wrapper.emitted('reset')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('save has type submit', () => {
|
||||
mountComponent();
|
||||
expect(findSaveButton().attributes('type')).toBe('submit');
|
||||
});
|
||||
|
||||
describe('when isLoading is true', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({ isLoading: true });
|
||||
});
|
||||
|
||||
it.each`
|
||||
elementName
|
||||
${'toggle'}
|
||||
${'interval'}
|
||||
${'schedule'}
|
||||
${'latest'}
|
||||
${'name-matching'}
|
||||
`(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
|
||||
expect(findFormElements(elementName).attributes('disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('submit button is disabled and shows a spinner', () => {
|
||||
const button = findSaveButton();
|
||||
expect(button.attributes('disabled')).toBeTruthy();
|
||||
expect(findLoadingIcon(button)).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
describe('form submit event ', () => {
|
||||
it('calls the appropriate function', () => {
|
||||
mountComponent();
|
||||
findForm().trigger('submit');
|
||||
expect(wrapper.emitted('submit')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form validation', () => {
|
||||
describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => {
|
||||
const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(',');
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponent({ value: { name_regex: invalidString } });
|
||||
});
|
||||
|
||||
it('save btn is disabled', () => {
|
||||
expect(findSaveButton().attributes('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('nameRegexState is false', () => {
|
||||
expect(wrapper.vm.nameRegexState).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('if the user did not type validation is null', () => {
|
||||
mountComponent({ value: { name_regex: '' } });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.vm.nameRegexState).toBe(null);
|
||||
expect(findSaveButton().attributes('disabled')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it(`if the user typed and is less than ${NAME_REGEX_LENGTH} state is true`, () => {
|
||||
mountComponent({ value: { name_regex: 'foo' } });
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.vm.nameRegexState).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('help text', () => {
|
||||
it('toggleDescriptionText show disabled when settings.enabled is false', () => {
|
||||
mountComponent();
|
||||
const toggleHelpText = findFormGroup('toggle').find('span');
|
||||
expect(toggleHelpText.html()).toContain('disabled');
|
||||
});
|
||||
|
||||
it('toggleDescriptionText show enabled when settings.enabled is true', () => {
|
||||
mountComponent({ value: { enabled: true } });
|
||||
const toggleHelpText = findFormGroup('toggle').find('span');
|
||||
expect(toggleHelpText.html()).toContain('enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import Vuex from 'vuex';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import ReleaseDetailApp from '~/releases/detail/components/app';
|
||||
import ReleaseDetailApp from '~/releases/detail/components/app.vue';
|
||||
import { release } from '../../mock_data';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ConfidentialIssueSidebar from '~/sidebar/components/confidential/confiden
|
|||
import EditForm from '~/sidebar/components/confidential/edit_form.vue';
|
||||
import SidebarService from '~/sidebar/services/sidebar_service';
|
||||
import createFlash from '~/flash';
|
||||
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal';
|
||||
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue';
|
||||
|
||||
jest.mock('~/flash');
|
||||
jest.mock('~/sidebar/services/sidebar_service');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
|
|||
|
||||
import { eventHub } from '~/vue_shared/components/recaptcha_eventhub';
|
||||
|
||||
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal';
|
||||
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue';
|
||||
|
||||
describe('RecaptchaModal', () => {
|
||||
const recaptchaFormId = 'recaptcha-form';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import SlotSwitch from '~/vue_shared/components/slot_switch';
|
||||
import SlotSwitch from '~/vue_shared/components/slot_switch.vue';
|
||||
|
||||
describe('SlotSwitch', () => {
|
||||
const slots = {
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ describe ButtonHelper do
|
|||
expect(element.attr('data-clipboard-text')).to eq(nil)
|
||||
expect(element.inner_text).to eq("")
|
||||
|
||||
expect(element.to_html).to include sprite_icon('duplicate')
|
||||
expect(element.to_html).to include sprite_icon('copy-to-clipboard')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe DeleteWithLimit do
|
||||
describe '.delete_with_limit' do
|
||||
it 'deletes a limited amount of rows' do
|
||||
create_list(:web_hook_log, 4)
|
||||
|
||||
expect do
|
||||
WebHookLog.delete_with_limit(2)
|
||||
end.to change { WebHookLog.count }.by(-2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -21,6 +21,22 @@ describe EmailsOnPushService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when properties is missing branches_to_be_notified' do
|
||||
subject { described_class.new(properties: {}) }
|
||||
|
||||
it 'sets the default value to all' do
|
||||
expect(subject.branches_to_be_notified).to eq('all')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when branches_to_be_notified is already set' do
|
||||
subject { described_class.new(properties: { branches_to_be_notified: 'protected' }) }
|
||||
|
||||
it 'does not overwrite it with the default value' do
|
||||
expect(subject.branches_to_be_notified).to eq('protected')
|
||||
end
|
||||
end
|
||||
|
||||
context 'project emails' do
|
||||
let(:push_data) { { object_kind: 'push' } }
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
|
|
|||
|
|
@ -127,21 +127,6 @@ describe API::Services do
|
|||
expect(json_response['properties'].keys).to match_array(service_instance.api_field_names)
|
||||
end
|
||||
|
||||
it "returns empty hash or nil values if properties and data fields are empty" do
|
||||
# deprecated services are not valid for update
|
||||
initialized_service.update_attribute(:properties, {})
|
||||
|
||||
if initialized_service.data_fields_present?
|
||||
initialized_service.data_fields.destroy
|
||||
initialized_service.reload
|
||||
end
|
||||
|
||||
get api("/projects/#{project.id}/services/#{dashed_service}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(json_response['properties'].values.compact).to be_empty
|
||||
end
|
||||
|
||||
it "returns error when authenticated but not a project owner" do
|
||||
project.add_developer(user2)
|
||||
get api("/projects/#{project.id}/services/#{dashed_service}", user2)
|
||||
|
|
|
|||
224
yarn.lock
224
yarn.lock
|
|
@ -705,18 +705,22 @@
|
|||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@gitlab/eslint-config@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-2.0.0.tgz#e30dbf2b170a7a4ca003a321de9f4170a2512510"
|
||||
integrity sha512-3Zw3ww8Q4hhVYxO7vliByD0yTeAQn4iBxOyqlASAZepZgdu/OmM4NPbWyntpTfDyHGoRGxmzEaCqv7DS6ubACA==
|
||||
"@gitlab/eslint-config@^2.1.1":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-2.1.1.tgz#64fcc8135f1a6055181fd64b991e33eb43913153"
|
||||
integrity sha512-+rQA+gIcZbkaQ7GIjDjfMnYz41fFtsEaF0cRmk0KSqXWTKmOi4gcYZppIPdRvJWKhNPRS735Y5Of3gdIObINYQ==
|
||||
dependencies:
|
||||
babel-eslint "^10.0.1"
|
||||
eslint-config-airbnb-base "^13.1.0"
|
||||
eslint-config-prettier "^3.3.0"
|
||||
"@gitlab/eslint-plugin-i18n" "^1.1.0"
|
||||
"@gitlab/eslint-plugin-vue-i18n" "^1.2.0"
|
||||
babel-eslint "^10.0.3"
|
||||
eslint-config-airbnb-base "^13.2.0"
|
||||
eslint-config-prettier "^3.6.0"
|
||||
eslint-plugin-babel "^5.3.0"
|
||||
eslint-plugin-filenames "^1.3.2"
|
||||
eslint-plugin-import "^2.18.2"
|
||||
eslint-plugin-promise "^4.1.1"
|
||||
eslint-plugin-vue "^5.0.0"
|
||||
eslint-plugin-import "^2.20.0"
|
||||
eslint-plugin-no-jquery "^2.3.1"
|
||||
eslint-plugin-promise "^4.2.1"
|
||||
eslint-plugin-vue "^5.2.3"
|
||||
|
||||
"@gitlab/eslint-plugin-i18n@^1.1.0":
|
||||
version "1.1.0"
|
||||
|
|
@ -1675,6 +1679,14 @@ array-unique@^0.3.2:
|
|||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
||||
|
||||
array.prototype.flat@^1.2.1:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
|
||||
integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.0-next.1"
|
||||
|
||||
arraybuffer.slice@~0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
|
||||
|
|
@ -1832,17 +1844,17 @@ babel-code-frame@^6.26.0:
|
|||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
babel-eslint@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
|
||||
integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==
|
||||
babel-eslint@^10.0.3:
|
||||
version "10.0.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
|
||||
integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@babel/parser" "^7.0.0"
|
||||
"@babel/traverse" "^7.0.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
eslint-scope "3.7.1"
|
||||
eslint-visitor-keys "^1.0.0"
|
||||
resolve "^1.12.0"
|
||||
|
||||
babel-jest@^24.1.0, babel-jest@^24.8.0:
|
||||
version "24.8.0"
|
||||
|
|
@ -2878,6 +2890,11 @@ configstore@^3.0.0:
|
|||
write-file-atomic "^2.0.0"
|
||||
xdg-basedir "^3.0.0"
|
||||
|
||||
confusing-browser-globals@^1.0.5:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd"
|
||||
integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==
|
||||
|
||||
connect-history-api-fallback@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
|
||||
|
|
@ -4156,21 +4173,22 @@ error-ex@^1.2.0, error-ex@^1.3.1:
|
|||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.2.tgz#4e874331645e9925edef141e74fc4bd144669d34"
|
||||
integrity sha512-jYo/J8XU2emLXl3OLwfwtuFfuF2w6DYPs+xy9ZfVyPkDcrauu6LYrw/q2TyCtrbc/KUdCiC5e9UajRhgNkVopA==
|
||||
es-abstract@^1.12.0, es-abstract@^1.17.0-next.1, es-abstract@^1.5.1, es-abstract@^1.7.0:
|
||||
version "1.17.4"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
|
||||
integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.1.4"
|
||||
is-regex "^1.0.4"
|
||||
is-callable "^1.1.5"
|
||||
is-regex "^1.0.5"
|
||||
object-inspect "^1.7.0"
|
||||
object-keys "^1.1.1"
|
||||
string.prototype.trimleft "^2.1.0"
|
||||
string.prototype.trimright "^2.1.0"
|
||||
object.assign "^4.1.0"
|
||||
string.prototype.trimleft "^2.1.1"
|
||||
string.prototype.trimright "^2.1.1"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
|
|
@ -4213,19 +4231,19 @@ escodegen@^1.9.1:
|
|||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-airbnb-base@^13.1.0:
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz#b5a1b480b80dfad16433d6c4ad84e6605052c05c"
|
||||
integrity sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==
|
||||
eslint-config-airbnb-base@^13.2.0:
|
||||
version "13.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz#f6ea81459ff4dec2dda200c35f1d8f7419d57943"
|
||||
integrity sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w==
|
||||
dependencies:
|
||||
eslint-restricted-globals "^0.1.1"
|
||||
confusing-browser-globals "^1.0.5"
|
||||
object.assign "^4.1.0"
|
||||
object.entries "^1.0.4"
|
||||
object.entries "^1.1.0"
|
||||
|
||||
eslint-config-prettier@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.3.0.tgz#41afc8d3b852e757f06274ed6c44ca16f939a57d"
|
||||
integrity sha512-Bc3bh5bAcKNvs3HOpSi6EfGA2IIp7EzWcg2tS4vP7stnXu/J1opihHDM7jI9JCIckyIDTgZLSWn7J3HY0j2JfA==
|
||||
eslint-config-prettier@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz#8ca3ffac4bd6eeef623a0651f9d754900e3ec217"
|
||||
integrity sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
|
|
@ -4262,14 +4280,21 @@ eslint-import-resolver-webpack@^0.10.1:
|
|||
resolve "^1.4.0"
|
||||
semver "^5.3.0"
|
||||
|
||||
eslint-module-utils@^2.4.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c"
|
||||
integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==
|
||||
eslint-module-utils@^2.4.1:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708"
|
||||
integrity sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==
|
||||
dependencies:
|
||||
debug "^2.6.8"
|
||||
debug "^2.6.9"
|
||||
pkg-dir "^2.0.0"
|
||||
|
||||
eslint-plugin-babel@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.3.0.tgz#2e7f251ccc249326da760c1a4c948a91c32d0023"
|
||||
integrity sha512-HPuNzSPE75O+SnxHIafbW5QB45r2w78fxqwK3HmjqIUoPfPzVrq6rD+CINU3yzoDSzEhUkX07VUphbF73Lth/w==
|
||||
dependencies:
|
||||
eslint-rule-composer "^0.3.0"
|
||||
|
||||
eslint-plugin-filenames@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz#7094f00d7aefdd6999e3ac19f72cea058e590cf7"
|
||||
|
|
@ -4280,22 +4305,23 @@ eslint-plugin-filenames@^1.3.2:
|
|||
lodash.snakecase "4.1.1"
|
||||
lodash.upperfirst "4.3.1"
|
||||
|
||||
eslint-plugin-import@^2.18.2:
|
||||
version "2.18.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
|
||||
integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
|
||||
eslint-plugin-import@^2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz#d749a7263fb6c29980def8e960d380a6aa6aecaa"
|
||||
integrity sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ==
|
||||
dependencies:
|
||||
array-includes "^3.0.3"
|
||||
array.prototype.flat "^1.2.1"
|
||||
contains-path "^0.1.0"
|
||||
debug "^2.6.9"
|
||||
doctrine "1.5.0"
|
||||
eslint-import-resolver-node "^0.3.2"
|
||||
eslint-module-utils "^2.4.0"
|
||||
eslint-module-utils "^2.4.1"
|
||||
has "^1.0.3"
|
||||
minimatch "^3.0.4"
|
||||
object.values "^1.1.0"
|
||||
read-pkg-up "^2.0.0"
|
||||
resolve "^1.11.0"
|
||||
resolve "^1.12.0"
|
||||
|
||||
eslint-plugin-jasmine@^2.10.1:
|
||||
version "2.10.1"
|
||||
|
|
@ -4307,35 +4333,27 @@ eslint-plugin-jest@^22.3.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.3.0.tgz#a10f10dedfc92def774ec9bb5bfbd2fb8e1c96d2"
|
||||
integrity sha512-P1mYVRNlOEoO5T9yTqOfucjOYf1ktmJ26NjwjH8sxpCFQa6IhBGr5TpKl3hcAAT29hOsRJVuMWmTsHoUVo9FoA==
|
||||
|
||||
eslint-plugin-no-jquery@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.3.0.tgz#fccdad84afa61baa4c0527dd6249cdcbfa0f74a8"
|
||||
integrity sha512-XQQZM5yKO72Y8QAojNhH8oYLnLZU34FovNHVoJlPLBuBPJk0kkiPNOS/K6wRFbVgn47iZHsT6E+7mSLwbcQEsg==
|
||||
eslint-plugin-no-jquery@^2.3.0, eslint-plugin-no-jquery@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.3.1.tgz#1c364cb863a38cc1570c8020155b6004cca62178"
|
||||
integrity sha512-/fiQUBSOMUETnfBuiK5ewvtRbek1IRTy5ov/6RZ6nlybvZ337vyGaNPWM1KgaIoIeN7dairNrPfq0h7A0tpT3A==
|
||||
|
||||
eslint-plugin-promise@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.1.1.tgz#1e08cb68b5b2cd8839f8d5864c796f56d82746db"
|
||||
integrity sha512-faAHw7uzlNPy7b45J1guyjazw28M+7gJokKUjC5JSFoYfUEyy6Gw/i7YQvmv2Yk00sUjWcmzXQLpU1Ki/C2IZQ==
|
||||
eslint-plugin-promise@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
|
||||
integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==
|
||||
|
||||
eslint-plugin-vue@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.0.0.tgz#4a2cc1c0e71ea45e1bd9c1a60f925bfe68bb5710"
|
||||
integrity sha512-mSv2Ebz3RaPP+XJO/mu7F+SdR9lrMyGISSExnarLFqqf3pF5wTmwWNrhHW1o9zKzKI811UVTIIkWJJvgO6SsUQ==
|
||||
eslint-plugin-vue@^5.2.3:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz#3ee7597d823b5478804b2feba9863b1b74273961"
|
||||
integrity sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==
|
||||
dependencies:
|
||||
vue-eslint-parser "^4.0.2"
|
||||
vue-eslint-parser "^5.0.0"
|
||||
|
||||
eslint-restricted-globals@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
|
||||
integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=
|
||||
|
||||
eslint-scope@3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
|
||||
integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
|
||||
dependencies:
|
||||
esrecurse "^4.1.0"
|
||||
estraverse "^4.1.1"
|
||||
eslint-rule-composer@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
|
||||
integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
|
||||
|
||||
eslint-scope@^4.0.0, eslint-scope@^4.0.3:
|
||||
version "4.0.3"
|
||||
|
|
@ -5033,7 +5051,7 @@ fstream@^1.0.0, fstream@^1.0.12:
|
|||
mkdirp ">=0.5 0"
|
||||
rimraf "2"
|
||||
|
||||
function-bind@^1.1.0, function-bind@^1.1.1:
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
|
@ -5932,10 +5950,10 @@ is-buffer@^2.0.0, is-buffer@^2.0.2:
|
|||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
|
||||
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
|
||||
|
||||
is-callable@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
|
||||
integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
|
||||
is-callable@^1.1.4, is-callable@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
|
||||
integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
|
||||
|
||||
is-ci@^1.0.10:
|
||||
version "1.2.1"
|
||||
|
|
@ -6153,12 +6171,12 @@ is-redirect@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
|
||||
integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
|
||||
|
||||
is-regex@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
|
||||
integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
|
||||
is-regex@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
|
||||
integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
|
||||
dependencies:
|
||||
has "^1.0.1"
|
||||
has "^1.0.3"
|
||||
|
||||
is-regexp@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
@ -8254,15 +8272,15 @@ object.assign@^4.1.0:
|
|||
has-symbols "^1.0.0"
|
||||
object-keys "^1.0.11"
|
||||
|
||||
object.entries@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
|
||||
integrity sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=
|
||||
object.entries@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b"
|
||||
integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==
|
||||
dependencies:
|
||||
define-properties "^1.1.2"
|
||||
es-abstract "^1.6.1"
|
||||
function-bind "^1.1.0"
|
||||
has "^1.0.1"
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.0-next.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
|
||||
object.getownpropertydescriptors@^2.0.3:
|
||||
version "2.0.3"
|
||||
|
|
@ -9811,10 +9829,10 @@ resolve@1.1.7:
|
|||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
|
||||
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
|
||||
|
||||
resolve@1.x, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
|
||||
integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
|
||||
resolve@1.x, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5"
|
||||
integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
|
|
@ -10593,18 +10611,18 @@ string-width@^4.1.0:
|
|||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^5.2.0"
|
||||
|
||||
string.prototype.trimleft@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
|
||||
integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
|
||||
string.prototype.trimleft@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
|
||||
integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
function-bind "^1.1.1"
|
||||
|
||||
string.prototype.trimright@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
|
||||
integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
|
||||
string.prototype.trimright@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
|
||||
integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
function-bind "^1.1.1"
|
||||
|
|
@ -11659,10 +11677,10 @@ vue-apollo@^3.0.0-beta.28:
|
|||
chalk "^2.4.1"
|
||||
throttle-debounce "^2.0.0"
|
||||
|
||||
vue-eslint-parser@^4.0.2:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-4.0.3.tgz#80cf162e484387b2640371ad21ba1f86e0c10a61"
|
||||
integrity sha512-AUeQsYdO6+7QXCems+WvGlrXd37PHv/zcRQSQdY1xdOMwdFAPEnMBsv7zPvk0TPGulXkK/5p/ITgrjiYB7k3ag==
|
||||
vue-eslint-parser@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1"
|
||||
integrity sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
eslint-scope "^4.0.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue