Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
62d12a8a8d
commit
a075eff323
|
|
@ -17,7 +17,6 @@ Gitlab/DocUrl:
|
|||
- 'ee/app/graphql/types/vulnerability_state_enum.rb'
|
||||
- 'ee/app/mailers/emails/user_cap.rb'
|
||||
- 'ee/app/workers/concerns/elastic/migration_obsolete.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/system_check/app/advanced_search_migrations_check.rb'
|
||||
- 'ee/lib/tasks/gitlab/geo.rake'
|
||||
- 'lib/feature.rb'
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ Gitlab/EeOnlyClass:
|
|||
- 'ee/lib/ee/gitlab/checks/push_rules/commit_check.rb'
|
||||
- 'ee/lib/ee/gitlab/checks/push_rules/file_size_check.rb'
|
||||
- 'ee/lib/ee/gitlab/checks/push_rules/tag_check.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/variables/builder/scan_execution_policies.rb'
|
||||
- 'ee/lib/ee/gitlab/import_export/after_export_strategies/custom_template_export_import_strategy.rb'
|
||||
- 'ee/lib/ee/gitlab/namespace_storage_size_error_message.rb'
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/lib/ee/gitlab/checks/diff_check.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/matching/runner_matcher.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/external.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/ee/gitlab/etag_caching/router/rails.rb'
|
||||
- 'ee/lib/ee/gitlab/git_access.rb'
|
||||
- 'ee/lib/ee/gitlab/gitaly_client/with_feature_flag_actors.rb'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ Layout/ExtraSpacing:
|
|||
- 'ee/app/models/merge_request/predictions.rb'
|
||||
- 'ee/app/services/dependencies/export_serializers/group_dependencies_service.rb'
|
||||
- 'ee/app/workers/pull_mirrors/reenable_configuration_worker.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/gitlab/usage/metrics/instrumentations/license_metric.rb'
|
||||
- 'ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb'
|
||||
- 'ee/spec/requests/api/internal/base_spec.rb'
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ Layout/LineContinuationSpacing:
|
|||
- 'ee/lib/api/ldap_group_links.rb'
|
||||
- 'ee/lib/api/vulnerability_findings.rb'
|
||||
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/ee/gitlab/git_access.rb'
|
||||
- 'ee/lib/tasks/gitlab/geo.rake'
|
||||
- 'ee/spec/controllers/groups/group_members_controller_spec.rb'
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ Layout/LineEndStringConcatenationIndentation:
|
|||
- 'ee/lib/ee/api/helpers/groups_helpers.rb'
|
||||
- 'ee/lib/ee/gitlab/auth/ldap/access.rb'
|
||||
- 'ee/lib/ee/gitlab/auth/o_auth/user.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/yaml_processor.rb'
|
||||
- 'ee/lib/ee/gitlab/git_access.rb'
|
||||
- 'ee/lib/ee/gitlab/namespace_storage_size_error_message.rb'
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
<!-- eslint-disable vue/multi-word-component-names -->
|
||||
<script>
|
||||
import { getDocument, GlobalWorkerOptions } from 'pdfjs-dist/legacy/build/pdf';
|
||||
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import Page from './page/index.vue';
|
||||
|
||||
GlobalWorkerOptions.workerSrc = process.env.PDF_JS_WORKER_PUBLIC_PATH;
|
||||
let pdfjs;
|
||||
let getDocument;
|
||||
let GlobalWorkerOptions;
|
||||
|
||||
export default {
|
||||
components: { Page },
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
pdf: {
|
||||
type: [String, Uint8Array],
|
||||
|
|
@ -35,13 +37,26 @@ export default {
|
|||
if (this.hasPDF) this.load();
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
async loadPDFJS() {
|
||||
pdfjs = this.glFeatures.upgradePdfjs
|
||||
? // eslint-disable-next-line import/extensions
|
||||
await import('pdfjs-dist-v4/legacy/build/pdf.mjs')
|
||||
: await import('pdfjs-dist-v3/legacy/build/pdf');
|
||||
({ getDocument, GlobalWorkerOptions } = pdfjs);
|
||||
GlobalWorkerOptions.workerSrc = this.glFeatures.upgradePdfjs
|
||||
? process.env.PDF_JS_WORKER_V4_PUBLIC_PATH
|
||||
: process.env.PDF_JS_WORKER_V3_PUBLIC_PATH;
|
||||
},
|
||||
async load() {
|
||||
await this.loadPDFJS();
|
||||
this.pages = [];
|
||||
return getDocument({
|
||||
url: this.document,
|
||||
cMapUrl: '/assets/webpack/pdfjs/cmaps/',
|
||||
cMapUrl: this.glFeatures.upgradePdfjs
|
||||
? process.env.PDF_JS_CMAPS_V4_PUBLIC_PATH
|
||||
: process.env.PDF_JS_CMAPS_V3_PUBLIC_PATH,
|
||||
cMapPacked: true,
|
||||
isEvalSupported: false,
|
||||
isEvalSupported: this.glFeatures.upgradePdfjs,
|
||||
})
|
||||
.promise.then(this.renderPages)
|
||||
.then((pages) => {
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-border-t">
|
||||
<div class="gl-border-t gl-border-t-section">
|
||||
<div class="well-segment">
|
||||
<refs-list
|
||||
:has-containing-refs="hasContainingBranches"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormTextarea,
|
||||
GlToggle,
|
||||
GlFormCheckbox,
|
||||
} from '@gitlab/ui';
|
||||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -33,7 +33,7 @@ export default {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormTextarea,
|
||||
GlToggle,
|
||||
GlFormCheckbox,
|
||||
},
|
||||
i18n: {
|
||||
DIR_LABEL,
|
||||
|
|
@ -165,12 +165,9 @@ export default {
|
|||
>
|
||||
<gl-form-input v-model="target" :disabled="loading" name="branch_name" />
|
||||
</gl-form-group>
|
||||
<gl-toggle
|
||||
v-if="showCreateNewMrToggle"
|
||||
v-model="createNewMr"
|
||||
:disabled="loading"
|
||||
:label="$options.i18n.TOGGLE_CREATE_MR_LABEL"
|
||||
/>
|
||||
<gl-form-checkbox v-if="showCreateNewMrToggle" v-model="createNewMr" :disabled="loading">
|
||||
{{ $options.i18n.TOGGLE_CREATE_MR_LABEL }}
|
||||
</gl-form-checkbox>
|
||||
<gl-alert v-if="!canPushCode" variant="info" :dismissible="false" class="gl-mt-3">
|
||||
{{ $options.i18n.NEW_BRANCH_IN_FORK }}
|
||||
</gl-alert>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormTextarea,
|
||||
GlToggle,
|
||||
GlButton,
|
||||
GlAlert,
|
||||
GlFormCheckbox,
|
||||
} from '@gitlab/ui';
|
||||
import FileIcon from '~/vue_shared/components/file_icon.vue';
|
||||
import { createAlert } from '~/alert';
|
||||
|
|
@ -39,11 +39,11 @@ export default {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormTextarea,
|
||||
GlToggle,
|
||||
GlButton,
|
||||
UploadDropzone,
|
||||
GlAlert,
|
||||
FileIcon,
|
||||
GlFormCheckbox,
|
||||
},
|
||||
i18n: {
|
||||
COMMIT_LABEL,
|
||||
|
|
@ -250,12 +250,9 @@ export default {
|
|||
>
|
||||
<gl-form-input v-model="target" :disabled="loading" name="branch_name" />
|
||||
</gl-form-group>
|
||||
<gl-toggle
|
||||
v-if="showCreateNewMrToggle"
|
||||
v-model="createNewMr"
|
||||
:disabled="loading"
|
||||
:label="$options.i18n.TOGGLE_CREATE_MR_LABEL"
|
||||
/>
|
||||
<gl-form-checkbox v-if="showCreateNewMrToggle" v-model="createNewMr" :disabled="loading">
|
||||
{{ $options.i18n.TOGGLE_CREATE_MR_LABEL }}
|
||||
</gl-form-checkbox>
|
||||
<gl-alert v-if="!canPushCode" variant="info" :dismissible="false" class="gl-mt-3">
|
||||
{{ $options.i18n.NEW_BRANCH_IN_FORK }}
|
||||
</gl-alert>
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
const headerLabel = s__('GlobalSearch|Archived');
|
||||
const checkboxLabel = s__('GlobalSearch|Include archived');
|
||||
export const TRACKING_NAMESPACE = 'search:archived:select';
|
||||
export const TRACKING_LABEL_CHECKBOX = 'checkbox';
|
||||
|
||||
const scopes = [
|
||||
'projects',
|
||||
'issues',
|
||||
'merge_requests',
|
||||
'notes',
|
||||
'blobs',
|
||||
'commits',
|
||||
'milestones',
|
||||
'wiki_blobs',
|
||||
];
|
||||
|
||||
const filterParam = 'include_archived';
|
||||
|
||||
export const archivedFilterData = {
|
||||
headerLabel,
|
||||
checkboxLabel,
|
||||
scopes,
|
||||
filterParam,
|
||||
};
|
||||
|
|
@ -5,8 +5,13 @@ import { mapState, mapActions } from 'vuex';
|
|||
import { s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
|
||||
import { archivedFilterData, TRACKING_NAMESPACE, TRACKING_LABEL_CHECKBOX } from './data';
|
||||
import {
|
||||
ARCHIVED_TRACKING_NAMESPACE,
|
||||
ARCHIVED_TRACKING_LABEL_CHECKBOX,
|
||||
ARCHIVED_TRACKING_LABEL_CHECKBOX_LABEL,
|
||||
LABEL_DEFAULT_CLASSES,
|
||||
INCLUDE_ARCHIVED_FILTER_PARAM,
|
||||
} from '../../constants';
|
||||
|
||||
export default {
|
||||
name: 'ArchivedFilter',
|
||||
|
|
@ -19,6 +24,8 @@ export default {
|
|||
},
|
||||
i18n: {
|
||||
tooltip: s__('GlobalSearch|Include search results from archived projects'),
|
||||
headerLabel: s__('GlobalSearch|Archived'),
|
||||
checkboxLabel: s__('GlobalSearch|Include archived'),
|
||||
},
|
||||
computed: {
|
||||
...mapState(['urlQuery']),
|
||||
|
|
@ -28,7 +35,10 @@ export default {
|
|||
},
|
||||
set(value) {
|
||||
const includeArchived = [...value].pop() ?? false;
|
||||
this.setQuery({ key: archivedFilterData.filterParam, value: includeArchived?.toString() });
|
||||
this.setQuery({
|
||||
key: INCLUDE_ARCHIVED_FILTER_PARAM,
|
||||
value: includeArchived?.toString(),
|
||||
});
|
||||
this.trackSelectCheckbox(includeArchived);
|
||||
},
|
||||
},
|
||||
|
|
@ -36,20 +46,20 @@ export default {
|
|||
methods: {
|
||||
...mapActions(['setQuery']),
|
||||
trackSelectCheckbox(value) {
|
||||
Tracking.event(TRACKING_NAMESPACE, TRACKING_LABEL_CHECKBOX, {
|
||||
label: archivedFilterData.checkboxLabel,
|
||||
Tracking.event(ARCHIVED_TRACKING_NAMESPACE, ARCHIVED_TRACKING_LABEL_CHECKBOX, {
|
||||
label: ARCHIVED_TRACKING_LABEL_CHECKBOX_LABEL,
|
||||
property: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
archivedFilterData,
|
||||
LABEL_DEFAULT_CLASSES,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-checkbox-group v-model="selectedFilter">
|
||||
<div class="gl-mb-2 gl-text-sm gl-font-bold" data-testid="archived-filter-title">
|
||||
{{ $options.archivedFilterData.headerLabel }}
|
||||
{{ $options.i18n.headerLabel }}
|
||||
</div>
|
||||
<gl-form-checkbox
|
||||
class="gl-inline-flex gl-w-full gl-grow gl-justify-between"
|
||||
|
|
@ -57,7 +67,7 @@ export default {
|
|||
:value="true"
|
||||
>
|
||||
<span v-gl-tooltip="$options.i18n.tooltip" data-testid="label">
|
||||
{{ $options.archivedFilterData.checkboxLabel }}
|
||||
{{ $options.i18n.checkboxLabel }}
|
||||
</span>
|
||||
</gl-form-checkbox>
|
||||
</gl-form-checkbox-group>
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
import Tracking from '~/tracking';
|
||||
|
||||
export const TRACKING_CATEGORY = 'Language filters';
|
||||
export const TRACKING_LABEL_FILTERS = 'Filters';
|
||||
|
||||
export const TRACKING_LABEL_MAX = 'Max Shown';
|
||||
export const TRACKING_LABEL_SHOW_MORE = 'Show More';
|
||||
export const TRACKING_LABEL_APPLY = 'Apply Filters';
|
||||
export const TRACKING_LABEL_RESET = 'Reset Filters';
|
||||
export const TRACKING_LABEL_ALL = 'All Filters';
|
||||
export const TRACKING_PROPERTY_MAX = `More than filters to show`;
|
||||
|
||||
export const TRACKING_ACTION_CLICK = 'search:agreggations:language:click';
|
||||
export const TRACKING_ACTION_SHOW = 'search:agreggations:language:show';
|
||||
|
||||
// select is imported and used in checkbox_filter.vue
|
||||
export const TRACKING_ACTION_SELECT = 'search:agreggations:language:select';
|
||||
|
||||
export const trackShowMore = () =>
|
||||
Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_SHOW_MORE, {
|
||||
label: TRACKING_LABEL_ALL,
|
||||
});
|
||||
|
||||
export const trackShowHasOverMax = () =>
|
||||
Tracking.event(TRACKING_ACTION_SHOW, TRACKING_LABEL_FILTERS, {
|
||||
label: TRACKING_LABEL_MAX,
|
||||
property: TRACKING_PROPERTY_MAX,
|
||||
});
|
||||
|
||||
export const trackSubmitQuery = () =>
|
||||
Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, {
|
||||
label: TRACKING_CATEGORY,
|
||||
});
|
||||
|
||||
export const trackResetQuery = () =>
|
||||
Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_RESET, {
|
||||
label: TRACKING_CATEGORY,
|
||||
});
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
const header = __('Confidentiality');
|
||||
|
||||
const filters = {
|
||||
ANY: {
|
||||
label: __('Any'),
|
||||
value: null,
|
||||
},
|
||||
CONFIDENTIAL: {
|
||||
label: __('Confidential'),
|
||||
value: 'yes',
|
||||
},
|
||||
NOT_CONFIDENTIAL: {
|
||||
label: __('Not confidential'),
|
||||
value: 'no',
|
||||
},
|
||||
};
|
||||
|
||||
const scopes = {
|
||||
ISSUES: 'issues',
|
||||
};
|
||||
|
||||
const filterByScope = {
|
||||
[scopes.ISSUES]: [filters.ANY, filters.CONFIDENTIAL, filters.NOT_CONFIDENTIAL],
|
||||
};
|
||||
|
||||
const filterParam = 'confidential';
|
||||
|
||||
export const confidentialFilterData = {
|
||||
header,
|
||||
filters,
|
||||
scopes,
|
||||
filterByScope,
|
||||
filterParam,
|
||||
};
|
||||
|
|
@ -1,18 +1,41 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
SCOPE_ISSUES,
|
||||
CONFIDENTAL_FILTER_PARAM,
|
||||
CONFIDENTIAL_FILTERS,
|
||||
} from '~/search/sidebar/constants';
|
||||
import RadioFilter from '../shared/radio_filter.vue';
|
||||
import { confidentialFilterData } from './data';
|
||||
|
||||
export default {
|
||||
name: 'ConfidentialityFilter',
|
||||
components: {
|
||||
RadioFilter,
|
||||
},
|
||||
confidentialFilterData,
|
||||
i18n: {
|
||||
header: __('Confidentiality'),
|
||||
},
|
||||
computed: {
|
||||
filtersArray() {
|
||||
return {
|
||||
[SCOPE_ISSUES]: [
|
||||
CONFIDENTIAL_FILTERS.ANY,
|
||||
CONFIDENTIAL_FILTERS.CONFIDENTIAL,
|
||||
CONFIDENTIAL_FILTERS.NOT_CONFIDENTIAL,
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
CONFIDENTAL_FILTER_PARAM,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<radio-filter :filter-data="$options.confidentialFilterData" />
|
||||
<radio-filter
|
||||
:filters-array="filtersArray"
|
||||
:header="$options.i18n.header"
|
||||
:filter-param="$options.CONFIDENTAL_FILTER_PARAM"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ import { mapState, mapActions } from 'vuex';
|
|||
import { s__ } from '~/locale';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { EVENT_CLICK_ZOEKT_INCLUDE_FORKS_ON_SEARCH_RESULTS_PAGE } from '~/search/sidebar/constants';
|
||||
import {
|
||||
EVENT_CLICK_ZOEKT_INCLUDE_FORKS_ON_SEARCH_RESULTS_PAGE,
|
||||
INCLUDE_FORKED_FILTER_PARAM,
|
||||
} from '~/search/sidebar/constants';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export const INCLUDE_FORKED_FILTER_PARAM = 'include_forked';
|
||||
|
||||
export default {
|
||||
name: 'ForksFilter',
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
export const FIRST_DROPDOWN_INDEX = 0;
|
||||
|
||||
export const SEARCH_BOX_INDEX = 0;
|
||||
|
||||
export const SEARCH_INPUT_DESCRIPTION = 'label-search-input-description';
|
||||
|
||||
export const SEARCH_RESULTS_DESCRIPTION = 'label-search-results-description';
|
||||
|
||||
export const LABEL_FILTER_HEADER = __('Labels');
|
||||
|
||||
export const LABEL_FILTER_PARAM = 'label_name';
|
||||
|
||||
export const LABEL_AGREGATION_NAME = 'labels';
|
||||
|
|
@ -19,16 +19,15 @@ import { slugify } from '~/lib/utils/text_utility';
|
|||
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
|
||||
|
||||
import { I18N } from '~/vue_shared/global_search/constants';
|
||||
import LabelDropdownItems from './label_dropdown_items.vue';
|
||||
|
||||
import {
|
||||
FIRST_DROPDOWN_INDEX,
|
||||
SEARCH_BOX_INDEX,
|
||||
SEARCH_RESULTS_DESCRIPTION,
|
||||
SEARCH_INPUT_DESCRIPTION,
|
||||
LABEL_FILTER_PARAM,
|
||||
SEARCH_RESULTS_DESCRIPTION,
|
||||
LABEL_FILTER_HEADER,
|
||||
} from './data';
|
||||
LABEL_FILTER_PARAM,
|
||||
} from '../../constants';
|
||||
import LabelDropdownItems from './label_dropdown_items.vue';
|
||||
|
||||
import { trackSelectCheckbox, trackOpenDropdown } from './tracking';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ import { intersection } from 'lodash';
|
|||
import Tracking from '~/tracking';
|
||||
import { NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES } from '../../constants';
|
||||
import { formatSearchResultCount } from '../../../store/utils';
|
||||
|
||||
export const TRACKING_LABEL_SET = 'set';
|
||||
export const TRACKING_LABEL_CHECKBOX = 'checkbox';
|
||||
import { TRACKING_LABEL_SET, TRACKING_LABEL_CHECKBOX } from './tracking';
|
||||
|
||||
export default {
|
||||
name: 'CheckboxFilter',
|
||||
|
|
@ -26,6 +24,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
queryParam: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['queryLanguageFilters']),
|
||||
|
|
@ -40,7 +42,7 @@ export default {
|
|||
return intersection(this.flatDataFilterValues, this.queryLanguageFilters);
|
||||
},
|
||||
async set(value) {
|
||||
this.setQuery({ key: this.filtersData?.filterParam, value });
|
||||
this.setQuery({ key: this.queryParam, value });
|
||||
|
||||
await Vue.nextTick();
|
||||
this.trackSelectCheckbox();
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export const DEFAULT_ITEM_LENGTH = 10;
|
||||
export const MAX_ITEM_LENGTH = 100;
|
||||
|
||||
const header = s__('GlobalSearch|Language');
|
||||
|
||||
const scopes = {
|
||||
BLOBS: 'blobs',
|
||||
};
|
||||
|
||||
const filterParam = 'language';
|
||||
|
||||
export const languageFilterData = {
|
||||
header,
|
||||
scopes,
|
||||
filterParam,
|
||||
};
|
||||
|
|
@ -4,11 +4,14 @@ import { GlButton, GlAlert } from '@gitlab/ui';
|
|||
import { mapState, mapActions, mapGetters } from 'vuex';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { convertFiltersData } from '../../utils';
|
||||
import {
|
||||
LANGUAGE_DEFAULT_ITEM_LENGTH,
|
||||
LANGUAGE_MAX_ITEM_LENGTH,
|
||||
LANGUAGE_FILTER_PARAM,
|
||||
} from '../../constants';
|
||||
import CheckboxFilter from './checkbox_filter.vue';
|
||||
import { trackShowMore, trackShowHasOverMax, TRACKING_ACTION_SELECT } from './tracking';
|
||||
|
||||
import { DEFAULT_ITEM_LENGTH, MAX_ITEM_LENGTH, languageFilterData } from './data';
|
||||
|
||||
export default {
|
||||
name: 'LanguageFilter',
|
||||
components: {
|
||||
|
|
@ -23,8 +26,11 @@ export default {
|
|||
},
|
||||
i18n: {
|
||||
showMore: s__('GlobalSearch|Show more'),
|
||||
showingMax: sprintf(s__('GlobalSearch|Showing top %{maxItems}'), { maxItems: MAX_ITEM_LENGTH }),
|
||||
showingMax: sprintf(s__('GlobalSearch|Showing top %{maxItems}'), {
|
||||
maxItems: LANGUAGE_MAX_ITEM_LENGTH,
|
||||
}),
|
||||
loadError: s__('GlobalSearch|Aggregations load error.'),
|
||||
headerLabel: s__('GlobalSearch|Language'),
|
||||
},
|
||||
computed: {
|
||||
...mapState(['aggregations']),
|
||||
|
|
@ -40,15 +46,15 @@ export default {
|
|||
return this.languageAggregationBuckets;
|
||||
}
|
||||
if (this.showAll) {
|
||||
return this.trimBuckets(MAX_ITEM_LENGTH);
|
||||
return this.trimBuckets(LANGUAGE_MAX_ITEM_LENGTH);
|
||||
}
|
||||
return this.trimBuckets(DEFAULT_ITEM_LENGTH);
|
||||
return this.trimBuckets(LANGUAGE_DEFAULT_ITEM_LENGTH);
|
||||
},
|
||||
hasShowMore() {
|
||||
return this.languageAggregationBuckets.length > DEFAULT_ITEM_LENGTH;
|
||||
return this.languageAggregationBuckets.length > LANGUAGE_DEFAULT_ITEM_LENGTH;
|
||||
},
|
||||
hasOverMax() {
|
||||
return this.languageAggregationBuckets.length > MAX_ITEM_LENGTH;
|
||||
return this.languageAggregationBuckets.length > LANGUAGE_MAX_ITEM_LENGTH;
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
|
|
@ -68,15 +74,15 @@ export default {
|
|||
return this.languageAggregationBuckets.slice(0, length);
|
||||
},
|
||||
},
|
||||
LANGUAGE_FILTER_PARAM,
|
||||
TRACKING_ACTION_SELECT,
|
||||
languageFilterData,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="hasBuckets" class="language-filter-checkbox">
|
||||
<div class="gl-mb-2 gl-text-sm gl-font-bold">
|
||||
{{ $options.languageFilterData.header }}
|
||||
{{ $options.i18n.headerLabel }}
|
||||
</div>
|
||||
<div
|
||||
v-if="!aggregations.error"
|
||||
|
|
@ -86,6 +92,7 @@ export default {
|
|||
<checkbox-filter
|
||||
:filters-data="filtersData"
|
||||
:tracking-namespace="$options.TRACKING_ACTION_SELECT"
|
||||
:query-param="$options.LANGUAGE_FILTER_PARAM"
|
||||
/>
|
||||
<span v-if="showAll && hasOverMax" data-testid="has-over-max-text">{{
|
||||
$options.i18n.showingMax
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Tracking from '~/tracking';
|
||||
import { MAX_ITEM_LENGTH } from './data';
|
||||
import { LANGUAGE_DEFAULT_ITEM_LENGTH } from '../../constants';
|
||||
|
||||
export const TRACKING_CATEGORY = 'Language filters';
|
||||
export const TRACKING_LABEL_FILTERS = 'Filters';
|
||||
|
|
@ -9,7 +9,7 @@ export const TRACKING_LABEL_SHOW_MORE = 'Show More';
|
|||
export const TRACKING_LABEL_APPLY = 'Apply Filters';
|
||||
export const TRACKING_LABEL_RESET = 'Reset Filters';
|
||||
export const TRACKING_LABEL_ALL = 'All Filters';
|
||||
export const TRACKING_PROPERTY_MAX = `More than ${MAX_ITEM_LENGTH} filters to show`;
|
||||
export const TRACKING_PROPERTY_MAX = `More than ${LANGUAGE_DEFAULT_ITEM_LENGTH} filters to show`;
|
||||
|
||||
export const TRACKING_ACTION_CLICK = 'search:agreggations:language:click';
|
||||
export const TRACKING_ACTION_SHOW = 'search:agreggations:language:show';
|
||||
|
|
@ -27,3 +27,6 @@ export const trackShowHasOverMax = () =>
|
|||
label: TRACKING_LABEL_MAX,
|
||||
property: TRACKING_PROPERTY_MAX,
|
||||
});
|
||||
|
||||
export const TRACKING_LABEL_SET = 'set';
|
||||
export const TRACKING_LABEL_CHECKBOX = 'checkbox';
|
||||
|
|
|
|||
|
|
@ -4,9 +4,13 @@ import { isEmpty } from 'lodash';
|
|||
import { mapState, mapActions, mapGetters } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
|
||||
import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/sidebar/constants';
|
||||
import {
|
||||
ANY_OPTION,
|
||||
GROUP_DATA,
|
||||
PROJECT_DATA,
|
||||
INCLUDE_ARCHIVED_FILTER_PARAM,
|
||||
} from '~/search/sidebar/constants';
|
||||
import SearchableDropdown from './shared/searchable_dropdown.vue';
|
||||
import { archivedFilterData } from './archived_filter/data';
|
||||
|
||||
export default {
|
||||
name: 'ProjectFilter',
|
||||
|
|
@ -64,7 +68,7 @@ export default {
|
|||
[PROJECT_DATA.queryParam]: project.id,
|
||||
nav_source: null,
|
||||
scope: this.currentScope,
|
||||
[archivedFilterData.filterParam]: null,
|
||||
[INCLUDE_ARCHIVED_FILTER_PARAM]: null,
|
||||
};
|
||||
|
||||
visitUrl(setUrlParams(queryParams));
|
||||
|
|
|
|||
|
|
@ -11,36 +11,44 @@ export default {
|
|||
GlFormRadio,
|
||||
},
|
||||
props: {
|
||||
filterData: {
|
||||
filtersArray: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
header: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
filterParam: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['query']),
|
||||
...mapGetters(['currentScope']),
|
||||
ANY() {
|
||||
return this.filterData.filters.ANY;
|
||||
const AnyIndex = this.filtersArray[this.currentScope].findIndex(
|
||||
(item) => item.value === null,
|
||||
);
|
||||
return this.filtersArray[this.currentScope][AnyIndex];
|
||||
},
|
||||
initialFilter() {
|
||||
return this.query[this.filterData.filterParam];
|
||||
return this.query[this.filterParam];
|
||||
},
|
||||
filter() {
|
||||
return this.initialFilter || this.ANY.value;
|
||||
},
|
||||
filtersArray() {
|
||||
return this.filterData.filterByScope[this.currentScope];
|
||||
},
|
||||
selectedFilter: {
|
||||
get() {
|
||||
if (this.filtersArray.some(({ value }) => value === this.filter)) {
|
||||
if (this.filtersArray[this.currentScope].some(({ value }) => value === this.filter)) {
|
||||
return this.filter;
|
||||
}
|
||||
|
||||
return this.ANY.value;
|
||||
},
|
||||
set(value) {
|
||||
this.setQuery({ key: this.filterData.filterParam, value });
|
||||
this.setQuery({ key: this.filterParam, value });
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -48,7 +56,7 @@ export default {
|
|||
...mapActions(['setQuery']),
|
||||
radioLabel(filter) {
|
||||
return filter.value === this.ANY.value
|
||||
? sprintf(__('Any %{header}'), { header: this.filterData.header.toLowerCase() })
|
||||
? sprintf(__('Any %{header}'), { header: this.header.toLowerCase() })
|
||||
: filter.label;
|
||||
},
|
||||
},
|
||||
|
|
@ -58,10 +66,10 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="gl-mb-2 gl-text-sm gl-font-bold">
|
||||
{{ filterData.header }}
|
||||
{{ header }}
|
||||
</div>
|
||||
<gl-form-radio-group v-model="selectedFilter">
|
||||
<gl-form-radio v-for="f in filtersArray" :key="f.value" :value="f.value">
|
||||
<gl-form-radio v-for="f in filtersArray[currentScope]" :key="f.value" :value="f.value">
|
||||
{{ radioLabel(f) }}
|
||||
</gl-form-radio>
|
||||
</gl-form-radio-group>
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ import { BRANCH_REF_TYPE_ICON } from '~/ref/constants';
|
|||
import {
|
||||
SEARCH_ICON,
|
||||
EVENT_SELECT_SOURCE_BRANCH_FILTER_ON_MERGE_REQUEST_PAGE,
|
||||
SOURCE_BRANCH_PARAM,
|
||||
NOT_SOURCE_BRANCH_PARAM,
|
||||
SOURCE_BRANCH_ENDPOINT_PATH,
|
||||
} from '../../constants';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export const SOURCE_BRANCH_PARAM = 'source_branch';
|
||||
export const NOT_SOURCE_BRANCH_PARAM = 'not[source_branch]';
|
||||
export const SOURCE_BRANCH_ENDPOINT_PATH = '/-/autocomplete/merge_request_source_branches.json';
|
||||
|
||||
export default {
|
||||
name: 'SourceBranchFilter',
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
const header = __('Status');
|
||||
|
||||
const filters = {
|
||||
ANY: {
|
||||
label: __('Any'),
|
||||
value: null,
|
||||
},
|
||||
OPEN: {
|
||||
label: __('Open'),
|
||||
value: 'opened',
|
||||
},
|
||||
CLOSED: {
|
||||
label: __('Closed'),
|
||||
value: 'closed',
|
||||
},
|
||||
MERGED: {
|
||||
label: __('Merged'),
|
||||
value: 'merged',
|
||||
},
|
||||
};
|
||||
|
||||
const scopes = {
|
||||
ISSUES: 'issues',
|
||||
MERGE_REQUESTS: 'merge_requests',
|
||||
};
|
||||
|
||||
const filterByScope = {
|
||||
[scopes.ISSUES]: [filters.ANY, filters.OPEN, filters.CLOSED],
|
||||
[scopes.MERGE_REQUESTS]: [filters.ANY, filters.OPEN, filters.MERGED, filters.CLOSED],
|
||||
};
|
||||
|
||||
const filterParam = 'state';
|
||||
|
||||
export const statusFilterData = {
|
||||
header,
|
||||
filters,
|
||||
scopes,
|
||||
filterByScope,
|
||||
filterParam,
|
||||
};
|
||||
|
|
@ -1,16 +1,42 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
SCOPE_ISSUES,
|
||||
SCOPE_MERGE_REQUESTS,
|
||||
STATE_FILTER_PARAM,
|
||||
STATE_FILTERS,
|
||||
} from '~/search/sidebar/constants';
|
||||
import RadioFilter from '../shared/radio_filter.vue';
|
||||
import { statusFilterData } from './data';
|
||||
|
||||
export default {
|
||||
name: 'StatusFilter',
|
||||
components: {
|
||||
RadioFilter,
|
||||
},
|
||||
statusFilterData,
|
||||
i18n: {
|
||||
header: __('Status'),
|
||||
},
|
||||
computed: {
|
||||
filtersArray() {
|
||||
return {
|
||||
[SCOPE_ISSUES]: [STATE_FILTERS.ANY, STATE_FILTERS.OPEN, STATE_FILTERS.CLOSED],
|
||||
[SCOPE_MERGE_REQUESTS]: [
|
||||
STATE_FILTERS.ANY,
|
||||
STATE_FILTERS.OPEN,
|
||||
STATE_FILTERS.MERGED,
|
||||
STATE_FILTERS.CLOSED,
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
STATE_FILTER_PARAM,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<radio-filter :filter-data="$options.statusFilterData" />
|
||||
<radio-filter
|
||||
:filters-array="filtersArray"
|
||||
:header="$options.i18n.header"
|
||||
:filter-param="$options.STATE_FILTER_PARAM"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ export const TRACKING_ACTION_CLICK = 'search:filters:click';
|
|||
export const TRACKING_LABEL_APPLY = 'Apply Filters';
|
||||
export const TRACKING_LABEL_RESET = 'Reset Filters';
|
||||
|
||||
export const ARCHIVED_TRACKING_NAMESPACE = 'search:archived:select';
|
||||
export const ARCHIVED_TRACKING_LABEL_CHECKBOX = 'checkbox';
|
||||
export const ARCHIVED_TRACKING_LABEL_CHECKBOX_LABEL = 'Include archived';
|
||||
|
||||
export const SEARCH_TYPE_BASIC = 'basic';
|
||||
export const SEARCH_TYPE_ADVANCED = 'advanced';
|
||||
export const SEARCH_TYPE_ZOEKT = 'zoekt';
|
||||
|
|
@ -57,3 +61,59 @@ export const EVENT_SELECT_SOURCE_BRANCH_FILTER = 'select_source_branch_filter';
|
|||
|
||||
export const EVENT_SELECT_SOURCE_BRANCH_FILTER_ON_MERGE_REQUEST_PAGE =
|
||||
'select_source_branch_filter_on_merge_request_page';
|
||||
|
||||
export const LANGUAGE_DEFAULT_ITEM_LENGTH = 10;
|
||||
export const LANGUAGE_MAX_ITEM_LENGTH = 100;
|
||||
|
||||
export const INCLUDE_ARCHIVED_FILTER_PARAM = 'include_archived';
|
||||
export const CONFIDENTAL_FILTER_PARAM = 'confidential';
|
||||
export const LABEL_FILTER_PARAM = 'label_name';
|
||||
export const INCLUDE_FORKED_FILTER_PARAM = 'include_forked';
|
||||
export const LANGUAGE_FILTER_PARAM = 'language';
|
||||
export const SOURCE_BRANCH_PARAM = 'source_branch';
|
||||
export const NOT_SOURCE_BRANCH_PARAM = 'not[source_branch]';
|
||||
|
||||
// label filter data
|
||||
export const FIRST_DROPDOWN_INDEX = 0;
|
||||
export const SEARCH_BOX_INDEX = 0;
|
||||
export const SEARCH_INPUT_DESCRIPTION = 'label-search-input-description';
|
||||
export const SEARCH_RESULTS_DESCRIPTION = 'label-search-results-description';
|
||||
export const LABEL_FILTER_HEADER = __('Labels');
|
||||
export const LABEL_AGREGATION_NAME = 'labels';
|
||||
|
||||
export const SOURCE_BRANCH_ENDPOINT_PATH = '/-/autocomplete/merge_request_source_branches.json';
|
||||
|
||||
export const CONFIDENTIAL_FILTERS = {
|
||||
ANY: {
|
||||
label: __('Any'),
|
||||
value: null,
|
||||
},
|
||||
CONFIDENTIAL: {
|
||||
label: __('Confidential'),
|
||||
value: 'yes',
|
||||
},
|
||||
NOT_CONFIDENTIAL: {
|
||||
label: __('Not confidential'),
|
||||
value: 'no',
|
||||
},
|
||||
};
|
||||
|
||||
export const STATE_FILTER_PARAM = 'state';
|
||||
export const STATE_FILTERS = {
|
||||
ANY: {
|
||||
label: __('Any'),
|
||||
value: null,
|
||||
},
|
||||
OPEN: {
|
||||
label: __('Open'),
|
||||
value: 'opened',
|
||||
},
|
||||
CLOSED: {
|
||||
label: __('Closed'),
|
||||
value: 'closed',
|
||||
},
|
||||
MERGED: {
|
||||
label: __('Merged'),
|
||||
value: 'merged',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
|
||||
|
||||
export const convertFiltersData = (rawBuckets) =>
|
||||
rawBuckets.reduce(
|
||||
(acc, bucket) => ({
|
||||
|
|
@ -13,5 +11,5 @@ export const convertFiltersData = (rawBuckets) =>
|
|||
},
|
||||
},
|
||||
}),
|
||||
{ ...languageFilterData, filters: {} },
|
||||
{ filters: {} },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import axios from '~/lib/utils/axios_utils';
|
|||
import { visitUrl, setUrlParams, getNormalizedURL, updateHistory } from '~/lib/utils/url_utility';
|
||||
import { logError } from '~/lib/logger';
|
||||
import { __ } from '~/locale';
|
||||
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
|
||||
import { SCOPE_BLOB, SEARCH_TYPE_ZOEKT } from '~/search/sidebar/constants';
|
||||
import { SCOPE_BLOB, SEARCH_TYPE_ZOEKT, LABEL_FILTER_PARAM } from '~/search/sidebar/constants';
|
||||
import {
|
||||
GROUPS_LOCAL_STORAGE_KEY,
|
||||
PROJECTS_LOCAL_STORAGE_KEY,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { statusFilterData } from '~/search/sidebar/components/status_filter/data';
|
||||
import { confidentialFilterData } from '~/search/sidebar/components/confidentiality_filter/data';
|
||||
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
|
||||
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
|
||||
import { archivedFilterData } from '~/search/sidebar/components/archived_filter/data';
|
||||
import { INCLUDE_FORKED_FILTER_PARAM } from '~/search/sidebar/components/forks_filter/index.vue';
|
||||
import { s__ } from '~/locale';
|
||||
import {
|
||||
CONFIDENTAL_FILTER_PARAM,
|
||||
INCLUDE_ARCHIVED_FILTER_PARAM,
|
||||
LABEL_FILTER_PARAM,
|
||||
INCLUDE_FORKED_FILTER_PARAM,
|
||||
LANGUAGE_FILTER_PARAM,
|
||||
SOURCE_BRANCH_PARAM,
|
||||
NOT_SOURCE_BRANCH_PARAM,
|
||||
} from '~/search/sidebar/components/source_branch_filter/index.vue';
|
||||
STATE_FILTER_PARAM,
|
||||
} from '~/search/sidebar/constants';
|
||||
|
||||
export const MAX_FREQUENT_ITEMS = 5;
|
||||
|
||||
|
|
@ -19,11 +19,11 @@ export const GROUPS_LOCAL_STORAGE_KEY = 'global-search-frequent-groups';
|
|||
export const PROJECTS_LOCAL_STORAGE_KEY = 'global-search-frequent-projects';
|
||||
|
||||
export const SIDEBAR_PARAMS = [
|
||||
statusFilterData.filterParam,
|
||||
confidentialFilterData.filterParam,
|
||||
languageFilterData.filterParam,
|
||||
STATE_FILTER_PARAM,
|
||||
CONFIDENTAL_FILTER_PARAM,
|
||||
LANGUAGE_FILTER_PARAM,
|
||||
LABEL_FILTER_PARAM,
|
||||
archivedFilterData.filterParam,
|
||||
INCLUDE_ARCHIVED_FILTER_PARAM,
|
||||
INCLUDE_FORKED_FILTER_PARAM,
|
||||
SOURCE_BRANCH_PARAM,
|
||||
NOT_SOURCE_BRANCH_PARAM,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { findKey, intersection, difference } from 'lodash';
|
||||
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
|
||||
import {
|
||||
LABEL_FILTER_PARAM,
|
||||
LABEL_AGREGATION_NAME,
|
||||
} from '~/search/sidebar/components/label_filter/data';
|
||||
import {
|
||||
formatSearchResultCount,
|
||||
addCountOverLimit,
|
||||
injectRegexSearch,
|
||||
} from '~/search/store/utils';
|
||||
|
||||
import { SCOPE_BLOB } from '~/search/sidebar/constants';
|
||||
import {
|
||||
SCOPE_BLOB,
|
||||
LABEL_FILTER_PARAM,
|
||||
LABEL_AGREGATION_NAME,
|
||||
LANGUAGE_FILTER_PARAM,
|
||||
} from '~/search/sidebar/constants';
|
||||
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, ICON_MAP } from './constants';
|
||||
|
||||
const queryLabelFilters = (state) => state?.query?.[LABEL_FILTER_PARAM] || [];
|
||||
|
|
@ -28,7 +28,7 @@ const unappliedNewLabelKeys = (state) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const queryLanguageFilters = (state) => state.query[languageFilterData.filterParam] || [];
|
||||
export const queryLanguageFilters = (state) => state.query[LANGUAGE_FILTER_PARAM] || [];
|
||||
|
||||
export const frequentGroups = (state) => {
|
||||
return state.frequentItems[GROUPS_LOCAL_STORAGE_KEY];
|
||||
|
|
@ -40,9 +40,8 @@ export const frequentProjects = (state) => {
|
|||
|
||||
export const languageAggregationBuckets = (state) => {
|
||||
return (
|
||||
state.aggregations.data.find(
|
||||
(aggregation) => aggregation.name === languageFilterData.filterParam,
|
||||
)?.buckets || []
|
||||
state.aggregations.data.find((aggregation) => aggregation.name === LANGUAGE_FILTER_PARAM)
|
||||
?.buckets || []
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import { isEqual, orderBy, isEmpty } from 'lodash';
|
|||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import { formatNumber } from '~/locale';
|
||||
import { joinPaths, queryToObject, objectToQuery, getBaseURL } from '~/lib/utils/url_utility';
|
||||
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
|
||||
import { LABEL_AGREGATION_NAME } from '~/search/sidebar/components/label_filter/data';
|
||||
import { LABEL_AGREGATION_NAME, LANGUAGE_FILTER_PARAM } from '~/search/sidebar/constants';
|
||||
import {
|
||||
MAX_FREQUENT_ITEMS,
|
||||
MAX_FREQUENCY,
|
||||
|
|
@ -13,8 +12,6 @@ import {
|
|||
LS_REGEX_HANDLE,
|
||||
} from './constants';
|
||||
|
||||
const LANGUAGE_AGGREGATION_NAME = languageFilterData.filterParam;
|
||||
|
||||
function extractKeys(object, keyList) {
|
||||
return Object.fromEntries(keyList.map((key) => [key, object[key]]));
|
||||
}
|
||||
|
|
@ -141,7 +138,7 @@ export const getAggregationsUrl = () => {
|
|||
};
|
||||
|
||||
const sortLanguages = (state, entries) => {
|
||||
const queriedLanguages = state.query?.[LANGUAGE_AGGREGATION_NAME] || [];
|
||||
const queriedLanguages = state.query?.[LANGUAGE_FILTER_PARAM] || [];
|
||||
|
||||
if (!Array.isArray(queriedLanguages) || !queriedLanguages.length) {
|
||||
return entries;
|
||||
|
|
@ -160,7 +157,7 @@ const getUniqueNamesOnly = (items) => {
|
|||
|
||||
export const prepareSearchAggregations = (state, aggregationData) =>
|
||||
aggregationData.map((item) => {
|
||||
if (item?.name === LANGUAGE_AGGREGATION_NAME) {
|
||||
if (item?.name === LANGUAGE_FILTER_PARAM) {
|
||||
return {
|
||||
...item,
|
||||
buckets: sortLanguages(state, item.buckets),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import SearchAndSortBar from './search_and_sort_bar.vue';
|
|||
|
||||
export default {
|
||||
component: SearchAndSortBar,
|
||||
title: 'usage_quotas/components/search_bar',
|
||||
title: 'usage_quotas/search_bar',
|
||||
};
|
||||
|
||||
const Template = (_, { argTypes }) => ({
|
||||
|
|
|
|||
|
|
@ -112,14 +112,12 @@ input[type='file'] {
|
|||
}
|
||||
|
||||
.info-well {
|
||||
background: $gray-10;
|
||||
color: $gl-text-color;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 4px;
|
||||
@apply gl-bg-subtle gl-text-default gl-border gl-border-section gl-rounded-base;
|
||||
|
||||
margin-bottom: 16px;
|
||||
|
||||
.ref-list {
|
||||
border-color: $gray-100;
|
||||
border-color: var(--gl-border-color-section);
|
||||
}
|
||||
|
||||
.well-segment {
|
||||
|
|
@ -133,7 +131,7 @@ input[type='file'] {
|
|||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
border-top: 1px solid $gray-100;
|
||||
@apply gl-border-t gl-border-t-section;
|
||||
}
|
||||
|
||||
p,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
.info-well {
|
||||
background: $gray-10;
|
||||
color: $gl-text-color;
|
||||
border: 1px solid $gray-100;
|
||||
border-radius: $gl-border-radius-base;
|
||||
@apply gl-bg-subtle gl-text-default gl-border gl-border-section gl-rounded-base;
|
||||
|
||||
.icon-container {
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:explain_code_chat, current_user)
|
||||
push_frontend_feature_flag(:upgrade_pdfjs, current_user)
|
||||
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class Projects::TreeController < Projects::ApplicationController
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:explain_code_chat, current_user)
|
||||
push_frontend_feature_flag(:upgrade_pdfjs, current_user)
|
||||
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
before_action do
|
||||
push_frontend_feature_flag(:remove_monitor_metrics, @project)
|
||||
push_frontend_feature_flag(:explain_code_chat, current_user)
|
||||
push_frontend_feature_flag(:upgrade_pdfjs, current_user)
|
||||
push_frontend_feature_flag(:issue_email_participants, @project)
|
||||
push_frontend_feature_flag(:edit_branch_rules, @project)
|
||||
# TODO: We need to remove the FF eventually when we rollout page_specific_styles
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Merge request approval policy `policy_tuning`",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"unblock_rules_using_execution_policies": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,6 @@
|
|||
= f.label :scopes
|
||||
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes, f: f
|
||||
|
||||
.gl-mt-5
|
||||
.settings-sticky-footer.gl-flex.gl-gap-3
|
||||
= f.submit _('Save application'), pajamas_button: true, data: { testid: 'save-application-button' }
|
||||
= link_button_to _('Cancel'), admin_applications_path
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
- add_to_breadcrumbs _("Applications"), admin_applications_path
|
||||
- breadcrumb_title @application.name
|
||||
- add_to_breadcrumbs @application.name, admin_application_path(@application)
|
||||
- breadcrumb_title _("Edit")
|
||||
- page_title _("Edit"), @application.name, _("Applications")
|
||||
|
||||
= render 'web_ide_oauth_application_callout', application: @application
|
||||
|
||||
%h1.page-title.gl-text-size-h-display
|
||||
= _('Edit application')
|
||||
= render ::Layouts::PageHeadingComponent.new(_('Edit application'))
|
||||
- @url = admin_application_path(@application)
|
||||
= render 'form', application: @application
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- add_to_breadcrumbs _("Applications"), admin_applications_path
|
||||
- breadcrumb_title _("Add new application")
|
||||
- page_title _("Add new application")
|
||||
|
||||
%h1.page-title.gl-text-size-h-display
|
||||
= _("Add new application")
|
||||
= render ::Layouts::PageHeadingComponent.new(_("Add new application"))
|
||||
- @url = admin_applications_path
|
||||
= render 'form', application: @application
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- page_title @application.name, _("Applications")
|
||||
- add_to_breadcrumbs _("Applications"), admin_applications_path
|
||||
- page_title @application.name
|
||||
|
||||
%h1.page-title.gl-text-size-h-display
|
||||
Application: #{@application.name}
|
||||
= render ::Layouts::PageHeadingComponent.new(_('Application: %{application_name}') % { application_name: @application.name })
|
||||
|
||||
= render 'shared/doorkeeper/applications/show',
|
||||
edit_path: edit_admin_application_path(@application),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
- add_to_breadcrumbs @label.name, admin_labels_path
|
||||
- breadcrumb_title _("Edit")
|
||||
- page_title _("Edit"), @label.name, _("Labels")
|
||||
%h1.page-title.gl-text-size-h-display
|
||||
= _('Edit label')
|
||||
|
||||
= render ::Layouts::PageHeadingComponent.new(_('Edit label'))
|
||||
= render 'shared/labels/form', url: admin_label_path(@label), back_path: admin_labels_path
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
- add_to_breadcrumbs _("Labels"), admin_labels_path
|
||||
- breadcrumb_title _("New label")
|
||||
- page_title _("New Label")
|
||||
%h1.page-title.gl-text-size-h-display
|
||||
= _('New Label')
|
||||
|
||||
= render ::Layouts::PageHeadingComponent.new(_('New label'))
|
||||
= render 'shared/labels/form', url: admin_labels_path, back_path: admin_labels_path
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
%span.gl-text-primary= n_('parent', 'parents', @commit.parents.count)
|
||||
- @commit.parents.each do |parent|
|
||||
= link_to parent.short_id, project_commit_path(@project, parent), class: "commit-sha"
|
||||
#js-commit-branches-and-tags.gl-border-t{ data: { full_path: @project.full_path, commit_sha: @commit.short_id } }
|
||||
#js-commit-branches-and-tags.gl-border-t.gl-border-t-section{ data: { full_path: @project.full_path, commit_sha: @commit.short_id } }
|
||||
.well-segment
|
||||
= sprite_icon('branch', css_class: "gl-ml-2 gl-mr-3")
|
||||
= gl_loading_icon(inline: true, css_class: 'gl-align-middle')
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@
|
|||
|
||||
= render "shared/tokens/scopes_list", token: @application
|
||||
|
||||
.gl-flex.gl-justify-between
|
||||
.gl-flex.gl-gap-3
|
||||
= render 'shared/doorkeeper/applications/delete_form', path: delete_path
|
||||
%div
|
||||
- if @created
|
||||
= link_button_to _('Continue'), index_path, class: 'gl-mr-3', variant: :confirm
|
||||
= link_button_to _('Edit'), edit_path
|
||||
= render 'shared/doorkeeper/applications/delete_form', path: delete_path
|
||||
|
||||
-# Create a hidden field to save the ID of application created
|
||||
= hidden_field_tag(:id_of_application, @application.id, data: { testid: 'id-of-application-field' })
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const plugins = [
|
|||
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/336216
|
||||
'@babel/plugin-transform-nullish-coalescing-operator',
|
||||
'lodash',
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
];
|
||||
|
||||
// Jest is running in node environment
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/159610
|
|||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/483039
|
||||
milestone: '17.5'
|
||||
group: group::static analysis
|
||||
type: wip
|
||||
default_enabled: false
|
||||
type: beta
|
||||
default_enabled: true
|
||||
|
|
@ -6,4 +6,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/465776
|
|||
milestone: '17.2'
|
||||
group: group::static analysis
|
||||
type: beta
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: upgrade_pdfjs
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/462822
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165557
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/491426
|
||||
milestone: '17.5'
|
||||
group: group::source code
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
const path = require('path');
|
||||
|
||||
const ROOT_PATH = path.resolve(__dirname, '..');
|
||||
const WEBPACK_OUTPUT_PATH = path.join(ROOT_PATH, 'public/assets/webpack');
|
||||
const WEBPACK_PUBLIC_PATH = '/assets/webpack/';
|
||||
|
||||
const PDFJS_PACKAGE_V3 = 'pdfjs-dist-v3';
|
||||
const PDFJS_PACKAGE_V4 = 'pdfjs-dist-v4';
|
||||
|
||||
const PDF_JS_V3_VERSION = require('pdfjs-dist-v3/package.json').version;
|
||||
const PDF_JS_V4_VERSION = require('pdfjs-dist-v4/package.json').version;
|
||||
|
||||
const PDF_JS_WORKER_V3_FILE_NAME = 'pdf.worker.min.js';
|
||||
const PDF_JS_WORKER_V4_FILE_NAME = 'pdf.worker.min.mjs';
|
||||
const PDF_JS_WORKER_V3_PATH = path.join('pdfjs', PDF_JS_V3_VERSION, '/');
|
||||
const PDF_JS_WORKER_V4_PATH = path.join('pdfjs', PDF_JS_V4_VERSION, '/');
|
||||
const PDF_JS_WORKER_V3_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_WORKER_V3_PATH);
|
||||
const PDF_JS_WORKER_V4_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_WORKER_V4_PATH);
|
||||
const PDF_JS_WORKER_V3_PUBLIC_PATH = path.join(
|
||||
WEBPACK_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_V3_PATH,
|
||||
PDF_JS_WORKER_V3_FILE_NAME,
|
||||
);
|
||||
const PDF_JS_WORKER_V4_PUBLIC_PATH = path.join(
|
||||
WEBPACK_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_V4_PATH,
|
||||
PDF_JS_WORKER_V4_FILE_NAME,
|
||||
);
|
||||
const PDF_JS_CMAPS_V3_PATH = path.join('pdfjs', PDF_JS_V3_VERSION, 'cmaps/');
|
||||
const PDF_JS_CMAPS_V4_PATH = path.join('pdfjs', PDF_JS_V4_VERSION, 'cmaps/');
|
||||
const PDF_JS_CMAPS_V3_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_CMAPS_V3_PATH);
|
||||
const PDF_JS_CMAPS_V4_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_CMAPS_V4_PATH);
|
||||
const PDF_JS_CMAPS_V3_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, PDF_JS_CMAPS_V3_PATH);
|
||||
const PDF_JS_CMAPS_V4_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, PDF_JS_CMAPS_V4_PATH);
|
||||
|
||||
const pdfJsCopyFilesPatterns = [
|
||||
{
|
||||
from: path.join(ROOT_PATH, 'node_modules', PDFJS_PACKAGE_V4, 'cmaps'),
|
||||
to: PDF_JS_CMAPS_V4_OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
from: path.join(ROOT_PATH, 'node_modules', PDFJS_PACKAGE_V3, 'cmaps'),
|
||||
to: PDF_JS_CMAPS_V3_OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
from: path.join(
|
||||
ROOT_PATH,
|
||||
'node_modules',
|
||||
PDFJS_PACKAGE_V4,
|
||||
'legacy',
|
||||
'build',
|
||||
PDF_JS_WORKER_V4_FILE_NAME,
|
||||
),
|
||||
to: PDF_JS_WORKER_V4_OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
from: path.join(
|
||||
ROOT_PATH,
|
||||
'node_modules',
|
||||
PDFJS_PACKAGE_V3,
|
||||
'legacy',
|
||||
'build',
|
||||
PDF_JS_WORKER_V3_FILE_NAME,
|
||||
),
|
||||
to: PDF_JS_WORKER_V3_OUTPUT_PATH,
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
pdfJsCopyFilesPatterns,
|
||||
PDF_JS_WORKER_V3_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_V4_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_V3_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_V4_PUBLIC_PATH,
|
||||
};
|
||||
|
|
@ -41,11 +41,15 @@ const {
|
|||
WEBPACK_OUTPUT_PATH,
|
||||
WEBPACK_PUBLIC_PATH,
|
||||
SOURCEGRAPH_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_PUBLIC_PATH,
|
||||
GITLAB_WEB_IDE_PUBLIC_PATH,
|
||||
copyFilesPatterns,
|
||||
} = require('./webpack.constants');
|
||||
const {
|
||||
PDF_JS_WORKER_V3_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_V4_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_V3_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_V4_PUBLIC_PATH,
|
||||
} = require('./pdfjs.constants');
|
||||
const { generateEntries } = require('./webpack.helpers');
|
||||
|
||||
const createIncrementalWebpackCompiler = require('./helpers/incremental_webpack_compiler');
|
||||
|
|
@ -303,7 +307,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js'],
|
||||
extensions: ['.mjs', '.js'],
|
||||
alias,
|
||||
},
|
||||
|
||||
|
|
@ -312,20 +316,25 @@ module.exports = {
|
|||
rules: [
|
||||
{
|
||||
type: 'javascript/auto',
|
||||
exclude: /pdfjs-dist/,
|
||||
exclude: /pdfjs-dist-v[34]/,
|
||||
test: /\.mjs$/,
|
||||
use: [],
|
||||
},
|
||||
{
|
||||
test: /(pdfjs).*\.js?$/,
|
||||
test: /(pdfjs).*\.m?js?$/,
|
||||
type: 'javascript/auto',
|
||||
include: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['@babel/preset-env', { targets: { esmodules: true }, modules: 'commonjs' }],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-optional-chaining',
|
||||
'@babel/plugin-transform-logical-assignment-operators',
|
||||
'@babel/plugin-transform-classes',
|
||||
],
|
||||
...defaultJsOptions,
|
||||
},
|
||||
|
|
@ -736,6 +745,14 @@ module.exports = {
|
|||
});
|
||||
}),
|
||||
|
||||
new webpack.ContextReplacementPlugin(/^\.$/, (context) => {
|
||||
if (/\/node_modules\/pdfjs-dist-v[34]/.test(context.context)) {
|
||||
for (const d of context.dependencies) {
|
||||
if (d.critical) d.critical = false;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
!IS_JH &&
|
||||
new webpack.NormalModuleReplacementPlugin(/^jh_component\/(.*)\.vue/, (resource) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
|
@ -860,10 +877,12 @@ module.exports = {
|
|||
// This is used by Sourcegraph because these assets are loaded dnamically
|
||||
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
|
||||
'process.env.GITLAB_WEB_IDE_PUBLIC_PATH': JSON.stringify(GITLAB_WEB_IDE_PUBLIC_PATH),
|
||||
'process.env.PDF_JS_WORKER_PUBLIC_PATH': JSON.stringify(PDF_JS_WORKER_PUBLIC_PATH),
|
||||
'process.env.PDF_JS_CMAPS_PUBLIC_PATH': JSON.stringify(PDF_JS_CMAPS_PUBLIC_PATH),
|
||||
'window.IS_VITE': JSON.stringify(false),
|
||||
...(IS_PRODUCTION ? {} : { LIVE_RELOAD: DEV_SERVER_LIVERELOAD }),
|
||||
'process.env.PDF_JS_WORKER_V3_PUBLIC_PATH': JSON.stringify(PDF_JS_WORKER_V3_PUBLIC_PATH),
|
||||
'process.env.PDF_JS_WORKER_V4_PUBLIC_PATH': JSON.stringify(PDF_JS_WORKER_V4_PUBLIC_PATH),
|
||||
'process.env.PDF_JS_CMAPS_V3_PUBLIC_PATH': JSON.stringify(PDF_JS_CMAPS_V3_PUBLIC_PATH),
|
||||
'process.env.PDF_JS_CMAPS_V4_PUBLIC_PATH': JSON.stringify(PDF_JS_CMAPS_V4_PUBLIC_PATH),
|
||||
}),
|
||||
|
||||
/* Pikaday has a optional dependency to moment.
|
||||
|
|
|
|||
|
|
@ -11,47 +11,20 @@ const SOURCEGRAPH_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, SOURCEGRAPH_PATH)
|
|||
|
||||
const GITLAB_WEB_IDE_VERSION = require('@gitlab/web-ide/package.json').version;
|
||||
|
||||
const { pdfJsCopyFilesPatterns } = require('./pdfjs.constants');
|
||||
|
||||
const GITLAB_WEB_IDE_PATH = path.join('gitlab-vscode', GITLAB_WEB_IDE_VERSION, '/');
|
||||
const GITLAB_WEB_IDE_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, GITLAB_WEB_IDE_PATH);
|
||||
const GITLAB_WEB_IDE_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, GITLAB_WEB_IDE_PATH);
|
||||
|
||||
const PDF_JS_VERSION = require('pdfjs-dist/package.json').version;
|
||||
|
||||
const PDF_JS_WORKER_FILE_NAME = 'pdf.worker.min.js';
|
||||
const PDF_JS_WORKER_PATH = path.join('pdfjs', PDF_JS_VERSION, '/');
|
||||
const PDF_JS_WORKER_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_WORKER_PATH);
|
||||
const PDF_JS_WORKER_PUBLIC_PATH = path.join(
|
||||
WEBPACK_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_PATH,
|
||||
PDF_JS_WORKER_FILE_NAME,
|
||||
);
|
||||
const PDF_JS_CMAPS_PATH = path.join('pdfjs', PDF_JS_VERSION, 'cmaps/');
|
||||
const PDF_JS_CMAPS_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, PDF_JS_CMAPS_PATH);
|
||||
const PDF_JS_CMAPS_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, PDF_JS_CMAPS_PATH);
|
||||
|
||||
const IS_EE = require('./helpers/is_ee_env');
|
||||
const IS_JH = require('./helpers/is_jh_env');
|
||||
|
||||
const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration';
|
||||
const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide';
|
||||
const PDFJS_PACKAGE = 'pdfjs-dist';
|
||||
|
||||
const copyFilesPatterns = [
|
||||
{
|
||||
from: path.join(ROOT_PATH, 'node_modules', PDFJS_PACKAGE, 'cmaps'),
|
||||
to: PDF_JS_CMAPS_OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
from: path.join(
|
||||
ROOT_PATH,
|
||||
'node_modules',
|
||||
PDFJS_PACKAGE,
|
||||
'legacy',
|
||||
'build',
|
||||
PDF_JS_WORKER_FILE_NAME,
|
||||
),
|
||||
to: PDF_JS_WORKER_OUTPUT_PATH,
|
||||
},
|
||||
...pdfJsCopyFilesPatterns,
|
||||
{
|
||||
from: path.join(ROOT_PATH, 'node_modules', SOURCEGRAPH_PACKAGE, '/'),
|
||||
to: SOURCEGRAPH_OUTPUT_PATH,
|
||||
|
|
@ -71,8 +44,6 @@ module.exports = {
|
|||
ROOT_PATH,
|
||||
WEBPACK_OUTPUT_PATH,
|
||||
WEBPACK_PUBLIC_PATH,
|
||||
PDF_JS_WORKER_PUBLIC_PATH,
|
||||
PDF_JS_CMAPS_PUBLIC_PATH,
|
||||
SOURCEGRAPH_PUBLIC_PATH,
|
||||
GITLAB_WEB_IDE_PUBLIC_PATH,
|
||||
copyFilesPatterns,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPolicyTuningToPolicies < Gitlab::Database::Migration[2.2]
|
||||
enable_lock_retries!
|
||||
milestone '17.6'
|
||||
|
||||
def change
|
||||
add_column :scan_result_policies, :policy_tuning, :jsonb, null: false, default: {}
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
1f0ac3229c91252bffb7fb11d3ea610c11e4f56dc62affa6d9eb1be3e22c0d19
|
||||
|
|
@ -18978,6 +18978,7 @@ CREATE TABLE scan_result_policies (
|
|||
commits smallint,
|
||||
send_bot_message jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
fallback_behavior jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
policy_tuning jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
CONSTRAINT age_value_null_or_positive CHECK (((age_value IS NULL) OR (age_value >= 0))),
|
||||
CONSTRAINT check_scan_result_policies_rule_idx_positive CHECK (((rule_idx IS NULL) OR (rule_idx >= 0)))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ It is also possible to specify a [custom CI/CD configuration file for a specific
|
|||
## Set CI/CD limits
|
||||
|
||||
> - **Maximum number of active pipelines per project** setting [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/368195) in GitLab 16.0.
|
||||
> - **Maximum number of jobs in a single pipeline** setting [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/287669) from GitLab Enterprise Edition to GitLab Community Edition in 17.6.
|
||||
|
||||
You can configure some [CI/CD limits](../../administration/instance_limits.md#cicd-limits)
|
||||
from the **Admin** area:
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ Example response:
|
|||
"commit": {
|
||||
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
|
||||
"short_id": "7b5c3cc",
|
||||
"created_at": "2012-06-28T03:44:20-07:00",
|
||||
"created_at": "2024-06-28T03:44:20-07:00",
|
||||
"parent_ids": [
|
||||
"4ad91d3c1144c406e50c7b33bae684bd6837faf8"
|
||||
],
|
||||
|
|
@ -65,11 +65,12 @@ Example response:
|
|||
"message": "add projects API",
|
||||
"author_name": "John Smith",
|
||||
"author_email": "john@example.com",
|
||||
"authored_date": "2012-06-27T05:51:39-07:00",
|
||||
"authored_date": "2024-06-27T05:51:39-07:00",
|
||||
"committer_name": "John Smith",
|
||||
"committer_email": "john@example.com",
|
||||
"committed_date": "2012-06-28T03:44:20-07:00",
|
||||
"committed_date": "2024-06-28T03:44:20-07:00",
|
||||
"trailers": {},
|
||||
"extended_trailers": {},
|
||||
"web_url": "https://gitlab.example.com/my-group/my-project/-/commit/7b5c3cc8be40ee161ae89a06bba6229da1032a0c"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ In each example, replace:
|
|||
|
||||
### Use a CI/CD job
|
||||
|
||||
You can use a CI/CD job with a pipeline triggers token to trigger pipelines when another pipeline
|
||||
You can use a CI/CD job with a pipeline trigger token to trigger pipelines when another pipeline
|
||||
runs.
|
||||
|
||||
For example, to trigger a pipeline on the `main` branch of `project-B` when a tag
|
||||
|
|
|
|||
|
|
@ -110,6 +110,39 @@ Chat is not aware of pipelines or commits. However, you can use
|
|||
[root cause analysis](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis)
|
||||
to troubleshoot the jobs in your pipeline.
|
||||
|
||||
## Ask about a specific commit
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Ultimate with GitLab Duo Enterprise - [Start a trial](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/?type=free-trial)
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** GitLab UI
|
||||
**LLM:** Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/468460) in GitLab 17.6.
|
||||
|
||||
You can ask about a specific GitLab commit. For example:
|
||||
|
||||
- `Generate a summary for the commit identified with this link: <link to your commit>`
|
||||
- `How can I improve the description of this commit?`
|
||||
- When you are viewing a commit in GitLab, you can ask `Generate a summary of the current commit.`
|
||||
|
||||
## Ask about a specific pipeline job
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Ultimate with GitLab Duo Enterprise - [Start a trial](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/?type=free-trial)
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** GitLab UI
|
||||
**LLM:** Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/468461) in GitLab 17.6.
|
||||
|
||||
You can ask about a specific GitLab pipeline job. For example:
|
||||
|
||||
- `Generate a summary for the pipeline job identified via this link: <link to your pipeline job>`
|
||||
- `Can you suggest ways to fix this failed pipeline job?`
|
||||
- `What are the main steps executed in this pipeline job?`
|
||||
- When you are viewing a pipeline job in GitLab, you can ask `Generate a summary of the current pipeline job.`
|
||||
|
||||
## Explain selected code
|
||||
|
||||
DETAILS:
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ In the GitLab UI, GitLab Duo Chat knows about these areas:
|
|||
| Issues | From the issue, ask about `this issue`, `this`, or the URL. From any UI area, ask about the URL. |
|
||||
| Code files | From the single file, ask about `this code` or `this file`. From any UI area, ask about the URL. |
|
||||
| Merge requests | From the merge request, ask about `this merge request`, `this`, or the URL. For more information, see [Ask about a specific merge request](examples.md#ask-about-a-specific-merge-request). |
|
||||
| Commits | From the commit, ask about `this commit` or `this`. From any UI area, ask about the URL. |
|
||||
| Pipeline jobs | From the pipeline job, ask about `this pipeline job` or `this`. From any UI area, ask about the URL. |
|
||||
|
||||
In the IDEs, GitLab Duo Chat knows about these areas:
|
||||
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ module.exports = (path, options = {}) => {
|
|||
'monaco-marker-data-provider',
|
||||
'monaco-worker-manager',
|
||||
'fast-mersenne-twister',
|
||||
'pdfjs-dist-v4',
|
||||
'prosemirror-markdown',
|
||||
'marked',
|
||||
'fault',
|
||||
|
|
@ -256,7 +257,7 @@ module.exports = (path, options = {}) => {
|
|||
transform: {
|
||||
'^.+\\.(gql|graphql)$': './spec/frontend/__helpers__/graphql_transformer.js',
|
||||
'^.+_worker\\.js$': './spec/frontend/__helpers__/web_worker_transformer.js',
|
||||
'^.+\\.js$': 'babel-jest',
|
||||
'^.+\\.m?js$': 'babel-jest',
|
||||
'^.+\\.vue$': VUE_JEST_TRANSFORMER,
|
||||
'spec/frontend/editor/schema/ci/yaml_tests/.+\\.(yml|yaml)$':
|
||||
'./spec/frontend/__helpers__/yaml_transformer.js',
|
||||
|
|
|
|||
|
|
@ -6,12 +6,40 @@ module Gitlab
|
|||
module Chain
|
||||
module Limit
|
||||
class Size < Chain::Base
|
||||
include ::Gitlab::Ci::Pipeline::Chain::Helpers
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
|
||||
@limit = Gitlab::Ci::Pipeline::Quota::Size
|
||||
.new(project.namespace, pipeline, command)
|
||||
end
|
||||
|
||||
def perform!
|
||||
# to be overridden in EE
|
||||
if limit.exceeded?
|
||||
limit.log_error!(log_attrs)
|
||||
error(limit.message, failure_reason: :size_limit_exceeded)
|
||||
elsif limit.log_exceeded_limit?
|
||||
limit.log_error!(log_attrs)
|
||||
end
|
||||
end
|
||||
|
||||
def break?
|
||||
false # to be overridden in EE
|
||||
limit.exceeded?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :limit
|
||||
|
||||
def log_attrs
|
||||
{
|
||||
jobs_count: pipeline.statuses.count,
|
||||
pipeline_source: pipeline.source,
|
||||
plan: project.actual_plan_name,
|
||||
project_id: project.id,
|
||||
project_full_path: project.full_path
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -19,5 +47,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::Ci::Pipeline::Chain::Limit::Size.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Limit::Size')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Pipeline
|
||||
module Quota
|
||||
class Size < ::Gitlab::Ci::Limit
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
LOGGABLE_JOBS_COUNT = 2000 # log large pipelines to determine a future global pipeline size limit
|
||||
|
||||
def initialize(namespace, pipeline, command)
|
||||
@namespace = namespace
|
||||
@pipeline = pipeline
|
||||
@command = command
|
||||
end
|
||||
|
||||
def enabled?
|
||||
ci_pipeline_size_limit > 0
|
||||
end
|
||||
|
||||
def exceeded?
|
||||
return false unless enabled?
|
||||
|
||||
seeds_size > ci_pipeline_size_limit
|
||||
end
|
||||
|
||||
def log_exceeded_limit?
|
||||
seeds_size > LOGGABLE_JOBS_COUNT
|
||||
end
|
||||
|
||||
def message
|
||||
doc_link = Rails.application.routes.url_helpers.help_page_url('ci/debugging.md',
|
||||
anchor: 'pipeline-with-many-jobs-fails-to-start')
|
||||
"The number of jobs has exceeded the limit of #{ci_pipeline_size_limit}. " \
|
||||
"Try splitting the configuration with parent-child-pipelines #{doc_link}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ci_pipeline_size_limit
|
||||
@namespace.actual_limits.ci_pipeline_size
|
||||
end
|
||||
strong_memoize_attr :ci_pipeline_size_limit
|
||||
|
||||
def seeds_size
|
||||
@command.pipeline_seed.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5762,9 +5762,6 @@ msgstr ""
|
|||
msgid "Allows projects to track errors using an Opstrace integration."
|
||||
msgstr ""
|
||||
|
||||
msgid "Almost there"
|
||||
msgstr ""
|
||||
|
||||
msgid "Almost there..."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6748,6 +6745,9 @@ msgstr ""
|
|||
msgid "Application was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "Application: %{application_name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Application: %{name}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8705,9 +8705,6 @@ msgstr ""
|
|||
msgid "BillingPlans|Introducing GitLab Duo Enterprise"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Ultimate."
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Learn more about each plan by visiting our %{pricing_page_link}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8753,9 +8750,6 @@ msgstr ""
|
|||
msgid "BillingPlans|Priority support"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Ready to explore the value of paid features today?"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Recommended"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8774,12 +8768,6 @@ msgstr ""
|
|||
msgid "BillingPlans|Start a free GitLab Duo Pro trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Start a free Ultimate trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Start a free Ultimate trial. No credit card required."
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|Start an Ultimate trial with GitLab Duo Enterprise to try the complete set of features from GitLab. GitLab Duo Enterprise gives you access to the full product offering from GitLab, including AI-powered features."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8858,9 +8846,6 @@ msgstr ""
|
|||
msgid "BillingPlans|billed annually at %{price_per_year}"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|frequently asked questions"
|
||||
msgstr ""
|
||||
|
||||
msgid "BillingPlans|group"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21221,9 +21206,6 @@ msgstr ""
|
|||
msgid "Enabled OAuth authentication sources"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enables a free GitLab Ultimate trial when you create a new project."
|
||||
msgstr ""
|
||||
|
||||
msgid "Enables a free Ultimate + GitLab Duo Enterprise trial when you create a new project."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -23972,9 +23954,6 @@ msgstr ""
|
|||
msgid "Frameworks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Free Trial of GitLab.com Ultimate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Free Ultimate Trial with GitLab Duo Enterprise"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -24007,7 +23986,7 @@ msgstr ""
|
|||
msgid "FreeUserCap|Start a trial:"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeUserCap|To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_user_limit} users or less. You can also %{upgrade_start}upgrade%{upgrade_end} to a paid tier, which do not have user limits. If you need additional time, you can %{trial_start}start a free 30-day trial%{trial_end} which includes unlimited users."
|
||||
msgid "FreeUserCap|To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_user_limit} users or less. You can also %{upgrade_start}upgrade%{upgrade_end} to a paid tier, which do not have user limits. If you need additional time, you can %{trial_start}start a free 60-day trial%{trial_end} which includes unlimited users."
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeUserCap|Upgrade:"
|
||||
|
|
@ -28567,27 +28546,15 @@ msgstr ""
|
|||
msgid "In use"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|%{upper_start}Free 30-day trial%{upper_end} %{lower_start}GitLab Ultimate%{lower_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|%{upper_start}Free 60-day trial%{upper_end} %{lower_start}GitLab Ultimate & GitLab Duo Enterprise%{lower_end} %{last_start}Sign up for a free trial of the most comprehensive AI-powered DevSecOps Platform%{last_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Accelerate your digital transformation"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Boost efficiency and collaboration"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Build in security"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Built-in security"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Deliver software faster"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|End-to-end security and compliance"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28600,9 +28567,6 @@ msgstr ""
|
|||
msgid "InProductMarketing|GitLab Duo Enterprise: AI across the software development lifecycle"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Improve collaboration and visibility"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Invite unlimited colleagues"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28621,9 +28585,6 @@ msgstr ""
|
|||
msgid "InProductMarketing|Start a Self-Managed trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Start your 30-day free trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "InProductMarketing|Start your 60-day free trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -32188,7 +32149,7 @@ msgstr ""
|
|||
msgid "LearnGitLab|Try GitLab Ultimate for free"
|
||||
msgstr ""
|
||||
|
||||
msgid "LearnGitLab|Try all GitLab features for 30 days, no credit card required."
|
||||
msgid "LearnGitLab|Try all GitLab features for 60 days, no credit card required."
|
||||
msgstr ""
|
||||
|
||||
msgid "LearnGitLab|Use GitLab to deploy your application, monitor its health, and keep it secure:"
|
||||
|
|
@ -53192,9 +53153,6 @@ msgstr ""
|
|||
msgid "Start time"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start your Free Ultimate Trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start your Free Ultimate and GitLab Duo Enterprise Trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -56712,7 +56670,7 @@ msgstr ""
|
|||
msgid "TierBadgePopover|This project uses the %{tier} GitLab tier. %{copyEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "TierBadgePopover|Want to enhance team productivity and access advanced features like Merge Approvals, Push rules, Epics, Code Review Analytics, and Container Scanning? Try all GitLab has to offer for free for 30 days. No credit card required."
|
||||
msgid "TierBadgePopover|Want to enhance team productivity and access advanced features like Merge Approvals, Push rules, Epics, Code Review Analytics, and Container Scanning? Try all GitLab has to offer for free for 60 days. No credit card required."
|
||||
msgstr ""
|
||||
|
||||
msgid "TierBadge|Free"
|
||||
|
|
@ -57217,8 +57175,8 @@ msgstr ""
|
|||
msgid "To get started, use the link below to confirm your account."
|
||||
msgstr ""
|
||||
|
||||
msgid "To invite more users, you can reduce the number of users in your top-level group to %{free_limit} user or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
|
||||
msgid_plural "To invite more users, you can reduce the number of users in your top-level group to %{free_limit} users or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
|
||||
msgid "To invite more users, you can reduce the number of users in your top-level group to %{free_limit} user or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 60-day trial which includes unlimited users."
|
||||
msgid_plural "To invite more users, you can reduce the number of users in your top-level group to %{free_limit} users or less. You can also upgrade to a paid tier which do not have user limits. If you need additional time, you can start a free 60-day trial which includes unlimited users."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
|
|
@ -57264,7 +57222,7 @@ msgstr ""
|
|||
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, ask your top-level group owner(s) to reduce the number of users in your top-level group to %{free_limit} users or less, or to upgrade to a paid tier which do not have user limits."
|
||||
msgstr ""
|
||||
|
||||
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 30-day trial which includes unlimited users."
|
||||
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 60-day trial which includes unlimited users."
|
||||
msgstr ""
|
||||
|
||||
msgid "To resolve the problem, refine your search criteria. Select a group or project or use double quotes for multiple keywords (for example, %{code_open}\"your search\"%{code_close})."
|
||||
|
|
@ -58245,15 +58203,9 @@ msgstr ""
|
|||
msgid "Trials|Create a new group and start your trial of Ultimate with GitLab Duo Enterprise."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trials|Create a new group to start your GitLab Ultimate trial."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trials|You can apply your trial of Ultimate with GitLab Duo Enterprise to a group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trials|You can apply your trial to a new group or an existing group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|Activate my trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58287,9 +58239,6 @@ msgstr ""
|
|||
msgid "Trial|Select state or province"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|Start free GitLab Ultimate trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|Start free Ultimate + GitLab Duo Enterprise trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58299,12 +58248,6 @@ msgstr ""
|
|||
msgid "Trial|To activate your trial, we need additional details from you."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|You don't need a credit card to start a trial. After the 30-day trial period, your account automatically becomes a GitLab Free account. You can use your GitLab Free account forever, or upgrade to a paid tier."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial|Your combined Ultimate and GitLab Duo Enterprise trial lasts for 60 days. We just need some additional information to activate it."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58380,9 +58323,6 @@ msgstr ""
|
|||
msgid "Try again?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Try all GitLab has to offer for 30 days. No credit card required."
|
||||
msgstr ""
|
||||
|
||||
msgid "Try grouping with different labels"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,8 @@
|
|||
"orderedmap": "^2.1.1",
|
||||
"papaparse": "^5.3.1",
|
||||
"patch-package": "6.5.1",
|
||||
"pdfjs-dist": "^3.11.174",
|
||||
"pdfjs-dist-v3": "npm:pdfjs-dist@3.11.174",
|
||||
"pdfjs-dist-v4": "npm:pdfjs-dist@4.3.136",
|
||||
"pikaday": "^1.8.0",
|
||||
"pinia": "^2.2.2",
|
||||
"popper.js": "^1.16.1",
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ tests = [
|
|||
},
|
||||
{
|
||||
explanation: 'Internal Events test cases map to internal events tooling',
|
||||
changed_file: 'spec/fixtures/scripts/internal_events/new_events.yml',
|
||||
changed_file: 'spec/fixtures/scripts/internal_events/event_definer_examples.yml',
|
||||
expected: %w[
|
||||
spec/scripts/internal_events/cli_spec.rb
|
||||
spec/support_specs/matchers/internal_events_matchers_spec.rb
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ FactoryBot.define do
|
|||
factory :file_uploader do
|
||||
skip_create
|
||||
|
||||
project
|
||||
secret { nil }
|
||||
|
||||
transient do
|
||||
|
|
@ -14,11 +13,12 @@ FactoryBot.define do
|
|||
end
|
||||
|
||||
after(:build) do |uploader, evaluator|
|
||||
uploader.store!(evaluator.file) if evaluator.project&.persisted?
|
||||
uploader.store!(evaluator.file) if evaluator.model&.persisted?
|
||||
end
|
||||
|
||||
initialize_with do
|
||||
new(project, secret)
|
||||
klass = container.is_a?(Group) ? NamespaceFileUploader : FileUploader
|
||||
klass.new(container, nil, secret: secret)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlobalWorkerOptions } from 'pdfjs-dist/legacy/build/pdf';
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { GlobalWorkerOptions as GlobalWorkerOptionsV4 } from 'pdfjs-dist-v4/legacy/build/pdf.mjs';
|
||||
import { GlobalWorkerOptions as GlobalWorkerOptionsV3 } from 'pdfjs-dist-v3/legacy/build/pdf';
|
||||
import { FIXTURES_PATH } from 'spec/test_constants';
|
||||
import PDFLab from '~/pdf/index.vue';
|
||||
|
||||
describe('PDFLab component', () => {
|
||||
let wrapper;
|
||||
|
||||
const mountComponent = ({ pdf }) => shallowMount(PDFLab, { propsData: { pdf } });
|
||||
const mountComponent = ({ pdf, flagValue = true }) =>
|
||||
shallowMount(PDFLab, {
|
||||
propsData: { pdf },
|
||||
provide: { glFeatures: { upgradePdfjs: flagValue } },
|
||||
});
|
||||
|
||||
describe('without PDF data', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -19,18 +25,58 @@ describe('PDFLab component', () => {
|
|||
});
|
||||
|
||||
describe('with PDF data', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent({ pdf: `${FIXTURES_PATH}/blob/pdf/test.pdf` });
|
||||
describe('when upgradePdfjs flag is on', () => {
|
||||
let mockGetDocument;
|
||||
beforeEach(async () => {
|
||||
mockGetDocument = jest.fn().mockReturnValue({ promise: Promise.resolve() });
|
||||
jest.mock('pdfjs-dist-v4/legacy/build/pdf.mjs', () => ({
|
||||
...jest.requireActual('pdfjs-dist-v4/legacy/build/pdf.mjs'),
|
||||
getDocument: mockGetDocument,
|
||||
}));
|
||||
wrapper = mountComponent({ pdf: `${FIXTURES_PATH}/blob/pdf/test.pdf` });
|
||||
await wrapper.vm.load();
|
||||
});
|
||||
|
||||
it('renders with pdfjs-dist v4', () => {
|
||||
expect(mockGetDocument).toHaveBeenCalledWith({
|
||||
url: '/fixtures/blob/pdf/test.pdf',
|
||||
cMapUrl: process.env.PDF_JS_CMAPS_V4_PUBLIC_PATH,
|
||||
cMapPacked: true,
|
||||
isEvalSupported: true,
|
||||
});
|
||||
expect(wrapper.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
it('gets worker file path from environment var', () => {
|
||||
expect(GlobalWorkerOptionsV4.workerSrc).toBe('mock/path/v4/pdf.worker.js');
|
||||
});
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.isVisible()).toBe(true);
|
||||
});
|
||||
describe('when upgradePdfjs flag is off', () => {
|
||||
let mockGetDocument;
|
||||
|
||||
it('gets worker file path from environment var', () => {
|
||||
expect(GlobalWorkerOptions).toEqual({
|
||||
workerPort: null,
|
||||
workerSrc: 'mock/path/pdf.worker.js',
|
||||
beforeEach(async () => {
|
||||
mockGetDocument = jest.fn().mockReturnValue({ promise: Promise.resolve() });
|
||||
jest.mock('pdfjs-dist-v3/legacy/build/pdf', () => ({
|
||||
...jest.requireActual('pdfjs-dist-v3/legacy/build/pdf'),
|
||||
getDocument: mockGetDocument,
|
||||
}));
|
||||
wrapper = mountComponent({ pdf: `${FIXTURES_PATH}/blob/pdf/test.pdf`, flagValue: false });
|
||||
await wrapper.vm.load();
|
||||
});
|
||||
|
||||
it('renders with pdfjs-dist v3', () => {
|
||||
expect(mockGetDocument).toHaveBeenCalledWith({
|
||||
url: '/fixtures/blob/pdf/test.pdf',
|
||||
cMapUrl: process.env.PDF_JS_CMAPS_V3_PUBLIC_PATH,
|
||||
cMapPacked: true,
|
||||
isEvalSupported: false,
|
||||
});
|
||||
expect(wrapper.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
it('gets worker file path from environment var', () => {
|
||||
expect(GlobalWorkerOptionsV3.workerSrc).toBe('mock/path/v3/pdf.worker.js');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ import { nextTick } from 'vue';
|
|||
import { mount } from '@vue/test-utils';
|
||||
import PageComponent from '~/pdf/page/index.vue';
|
||||
|
||||
jest.mock('pdfjs-dist/webpack', () => {
|
||||
return { default: jest.requireActual('pdfjs-dist/legacy/build/pdf') };
|
||||
});
|
||||
|
||||
describe('Page component', () => {
|
||||
let wrapper;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlModal, GlFormTextarea, GlToggle } from '@gitlab/ui';
|
||||
import { GlModal, GlFormTextarea, GlFormCheckbox } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
|
@ -53,7 +53,7 @@ describe('NewDirectoryModal', () => {
|
|||
const findDirName = () => wrapper.find('[name="dir_name"]');
|
||||
const findBranchName = () => wrapper.find('[name="branch_name"]');
|
||||
const findCommitMessage = () => wrapper.findComponent(GlFormTextarea);
|
||||
const findMrToggle = () => wrapper.findComponent(GlToggle);
|
||||
const findMrCheckbox = () => wrapper.findComponent(GlFormCheckbox);
|
||||
|
||||
const fillForm = async (inputValue = {}) => {
|
||||
const {
|
||||
|
|
@ -66,7 +66,7 @@ describe('NewDirectoryModal', () => {
|
|||
await findDirName().vm.$emit('input', dirName);
|
||||
await findBranchName().vm.$emit('input', branchName);
|
||||
await findCommitMessage().vm.$emit('input', commitMessage);
|
||||
await findMrToggle().vm.$emit('change', createNewMr);
|
||||
await findMrCheckbox().vm.$emit('input', createNewMr);
|
||||
await nextTick();
|
||||
};
|
||||
|
||||
|
|
@ -95,16 +95,24 @@ describe('NewDirectoryModal', () => {
|
|||
|
||||
describe('form', () => {
|
||||
it.each`
|
||||
component | defaultValue | canPushCode | targetBranch | originalBranch | exist
|
||||
${findDirName} | ${undefined} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
|
||||
${findBranchName} | ${initialProps.targetBranch} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
|
||||
${findBranchName} | ${undefined} | ${false} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${false}
|
||||
${findCommitMessage} | ${initialProps.commitMessage} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
|
||||
${findMrToggle} | ${'true'} | ${true} | ${'new-target-branch'} | ${'master'} | ${true}
|
||||
${findMrToggle} | ${'true'} | ${true} | ${'master'} | ${'master'} | ${true}
|
||||
component | defaultValue | canPushCode | targetBranch | originalBranch | exist | attributes
|
||||
${findDirName} | ${undefined} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true} | ${'value'}
|
||||
${findBranchName} | ${initialProps.targetBranch} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true} | ${'value'}
|
||||
${findBranchName} | ${undefined} | ${false} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${false} | ${'value'}
|
||||
${findCommitMessage} | ${initialProps.commitMessage} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true} | ${'value'}
|
||||
${findMrCheckbox} | ${'true'} | ${true} | ${'new-target-branch'} | ${'master'} | ${true} | ${'checked'}
|
||||
${findMrCheckbox} | ${'true'} | ${true} | ${'master'} | ${'master'} | ${true} | ${'checked'}
|
||||
`(
|
||||
'has the correct form fields',
|
||||
({ component, defaultValue, canPushCode, targetBranch, originalBranch, exist }) => {
|
||||
({
|
||||
component,
|
||||
defaultValue,
|
||||
canPushCode,
|
||||
targetBranch,
|
||||
originalBranch,
|
||||
exist,
|
||||
attributes,
|
||||
}) => {
|
||||
createComponent({
|
||||
canPushCode,
|
||||
targetBranch,
|
||||
|
|
@ -118,7 +126,7 @@ describe('NewDirectoryModal', () => {
|
|||
}
|
||||
|
||||
expect(formField.exists()).toBe(true);
|
||||
expect(formField.attributes('value')).toBe(defaultValue);
|
||||
expect(formField.attributes(attributes)).toBe(defaultValue);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlModal, GlFormInput, GlFormTextarea, GlToggle, GlAlert } from '@gitlab/ui';
|
||||
import { GlModal, GlFormInput, GlFormTextarea, GlFormCheckbox, GlAlert } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
|
@ -52,7 +52,7 @@ describe('UploadBlobModal', () => {
|
|||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findCommitMessage = () => wrapper.findComponent(GlFormTextarea);
|
||||
const findBranchName = () => wrapper.findComponent(GlFormInput);
|
||||
const findMrToggle = () => wrapper.findComponent(GlToggle);
|
||||
const findMrCheckbox = () => wrapper.findComponent(GlFormCheckbox);
|
||||
const findUploadDropzone = () => wrapper.findComponent(UploadDropzone);
|
||||
const actionButtonDisabledState = () => findModal().props('actionPrimary').attributes.disabled;
|
||||
const cancelButtonDisabledState = () => findModal().props('actionCancel').attributes.disabled;
|
||||
|
|
@ -90,8 +90,8 @@ describe('UploadBlobModal', () => {
|
|||
expect(cancelButtonDisabledState()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not display the MR toggle', () => {
|
||||
expect(findMrToggle().exists()).toBe(false);
|
||||
it('does not display the MR checkbox', () => {
|
||||
expect(findMrCheckbox().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it(`${
|
||||
|
|
@ -106,12 +106,12 @@ describe('UploadBlobModal', () => {
|
|||
|
||||
if (canPushCode) {
|
||||
describe('when changing the branch name', () => {
|
||||
it('displays the MR toggle', async () => {
|
||||
it('displays the MR checkbox', async () => {
|
||||
createComponent({ targetBranch: 'Not main' });
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(findMrToggle().exists()).toBe(true);
|
||||
expect(findMrCheckbox().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,9 +459,6 @@ export const TEST_RAW_BUCKETS = [
|
|||
];
|
||||
|
||||
export const TEST_FILTER_DATA = {
|
||||
header: 'Language',
|
||||
scopes: { BLOBS: 'blobs' },
|
||||
filterParam: 'language',
|
||||
filters: {
|
||||
GO: { label: 'Go', value: 'Go', count: 350 },
|
||||
C: { label: 'C', value: 'C', count: 298 },
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import Vue from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import {
|
||||
SEARCH_TYPE_ZOEKT,
|
||||
SEARCH_TYPE_ADVANCED,
|
||||
SEARCH_TYPE_BASIC,
|
||||
} from '~/search/sidebar/constants';
|
||||
import { MOCK_QUERY } from 'jest/search/mock_data';
|
||||
import { toggleSuperSidebarCollapsed } from '~/super_sidebar/super_sidebar_collapsed_state_manager';
|
||||
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
|
||||
|
|
@ -89,26 +84,23 @@ describe('GlobalSearchSidebar', () => {
|
|||
${'wiki_blobs'} | ${findWikiBlobsFilters}
|
||||
${'wiki_blobs'} | ${findAllScopesStartFilters}
|
||||
`('with sidebar scope: $scope', ({ scope, filter }) => {
|
||||
describe.each([SEARCH_TYPE_BASIC, SEARCH_TYPE_ADVANCED])(
|
||||
'with search_type %s',
|
||||
(searchType) => {
|
||||
beforeEach(() => {
|
||||
getterSpies.currentScope = jest.fn(() => scope);
|
||||
createComponent({ urlQuery: { scope }, searchType });
|
||||
});
|
||||
describe.each(['basic', 'advanced'])('with search_type %s', (searchType) => {
|
||||
beforeEach(() => {
|
||||
getterSpies.currentScope = jest.fn(() => scope);
|
||||
createComponent({ urlQuery: { scope }, searchType });
|
||||
});
|
||||
|
||||
it(`renders correctly ${filter.name.replace('find', '')}`, () => {
|
||||
expect(filter().exists()).toBe(true);
|
||||
});
|
||||
},
|
||||
);
|
||||
it(`renders correctly ${filter.name.replace('find', '')}`, () => {
|
||||
expect(filter().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
scope | searchType | isShown
|
||||
${'blobs'} | ${SEARCH_TYPE_BASIC} | ${false}
|
||||
${'blobs'} | ${SEARCH_TYPE_ADVANCED} | ${true}
|
||||
${'blobs'} | ${SEARCH_TYPE_ZOEKT} | ${true}
|
||||
scope | searchType | isShown
|
||||
${'blobs'} | ${'basic'} | ${false}
|
||||
${'blobs'} | ${'advanced'} | ${true}
|
||||
${'blobs'} | ${'zoekt'} | ${true}
|
||||
`('sidebar blobs scope:', ({ scope, searchType, isShown }) => {
|
||||
beforeEach(() => {
|
||||
getterSpies.currentScope = jest.fn(() => scope);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
|
||||
|
||||
import { archivedFilterData } from '~/search/sidebar/components/archived_filter/data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('ArchivedFilter', () => {
|
||||
|
|
@ -46,7 +44,7 @@ describe('ArchivedFilter', () => {
|
|||
|
||||
it('renders the divider', () => {
|
||||
expect(findTitle().exists()).toBe(true);
|
||||
expect(findTitle().text()).toBe(archivedFilterData.headerLabel);
|
||||
expect(findTitle().text()).toBe('Archived');
|
||||
});
|
||||
|
||||
it('wraps the label element with a tooltip', () => {
|
||||
|
|
@ -67,7 +65,7 @@ describe('ArchivedFilter', () => {
|
|||
|
||||
it("doesn't render the divider", () => {
|
||||
expect(findTitle().exists()).toBe(true);
|
||||
expect(findTitle().text()).toBe(archivedFilterData.headerLabel);
|
||||
expect(findTitle().text()).toBe('Archived');
|
||||
});
|
||||
|
||||
it('wraps the label element with a tooltip', () => {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@ import BlobsFilters from '~/search/sidebar/components/blobs_filters.vue';
|
|||
import LanguageFilter from '~/search/sidebar/components/language_filter/index.vue';
|
||||
import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
|
||||
import ForksFilter from '~/search/sidebar/components/forks_filter/index.vue';
|
||||
import {
|
||||
SEARCH_TYPE_ZOEKT,
|
||||
SEARCH_TYPE_ADVANCED,
|
||||
SEARCH_TYPE_BASIC,
|
||||
} from '~/search/sidebar/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -23,7 +18,7 @@ describe('GlobalSearch BlobsFilters', () => {
|
|||
hasMissingProjectContext: () => true,
|
||||
};
|
||||
|
||||
const createComponent = (initialState = { searchType: SEARCH_TYPE_ADVANCED }) => {
|
||||
const createComponent = (initialState = { searchType: 'advanced' }) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
urlQuery: MOCK_QUERY,
|
||||
|
|
@ -46,10 +41,10 @@ describe('GlobalSearch BlobsFilters', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
searchType | isShown
|
||||
${SEARCH_TYPE_BASIC} | ${false}
|
||||
${SEARCH_TYPE_ADVANCED} | ${true}
|
||||
${SEARCH_TYPE_ZOEKT} | ${false}
|
||||
searchType | isShown
|
||||
${'basic'} | ${false}
|
||||
${'advanced'} | ${true}
|
||||
${'zoekt'} | ${false}
|
||||
`('sidebar blobs language filter:', ({ searchType, isShown }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ searchType });
|
||||
|
|
@ -61,13 +56,13 @@ describe('GlobalSearch BlobsFilters', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
searchType | hasProjectContent | isShown
|
||||
${SEARCH_TYPE_BASIC} | ${true} | ${false}
|
||||
${SEARCH_TYPE_BASIC} | ${false} | ${false}
|
||||
${SEARCH_TYPE_ADVANCED} | ${true} | ${false}
|
||||
${SEARCH_TYPE_ADVANCED} | ${false} | ${false}
|
||||
${SEARCH_TYPE_ZOEKT} | ${true} | ${true}
|
||||
${SEARCH_TYPE_ZOEKT} | ${false} | ${false}
|
||||
searchType | hasProjectContent | isShown
|
||||
${'basic'} | ${true} | ${false}
|
||||
${'basic'} | ${false} | ${false}
|
||||
${'advanced'} | ${true} | ${false}
|
||||
${'advanced'} | ${false} | ${false}
|
||||
${'zoekt'} | ${true} | ${true}
|
||||
${'zoekt'} | ${false} | ${false}
|
||||
`('sidebar blobs fork filter:', ({ searchType, hasProjectContent, isShown }) => {
|
||||
beforeEach(() => {
|
||||
defaultGetters.hasMissingProjectContext = () => hasProjectContent;
|
||||
|
|
@ -80,13 +75,13 @@ describe('GlobalSearch BlobsFilters', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
searchType | hasProjectContent | isShown
|
||||
${SEARCH_TYPE_BASIC} | ${true} | ${true}
|
||||
${SEARCH_TYPE_BASIC} | ${false} | ${false}
|
||||
${SEARCH_TYPE_ADVANCED} | ${true} | ${true}
|
||||
${SEARCH_TYPE_ADVANCED} | ${false} | ${false}
|
||||
${SEARCH_TYPE_ZOEKT} | ${true} | ${true}
|
||||
${SEARCH_TYPE_ZOEKT} | ${false} | ${false}
|
||||
searchType | hasProjectContent | isShown
|
||||
${'basic'} | ${true} | ${true}
|
||||
${'basic'} | ${false} | ${false}
|
||||
${'advanced'} | ${true} | ${true}
|
||||
${'advanced'} | ${false} | ${false}
|
||||
${'zoekt'} | ${true} | ${true}
|
||||
${'zoekt'} | ${false} | ${false}
|
||||
`('sidebar blobs archived filter:', ({ searchType, hasProjectContent, isShown }) => {
|
||||
beforeEach(() => {
|
||||
defaultGetters.hasMissingProjectContext = () => hasProjectContent;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,8 @@ import Vuex from 'vuex';
|
|||
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { MOCK_QUERY, MOCK_LANGUAGE_AGGREGATIONS_BUCKETS } from 'jest/search/mock_data';
|
||||
import CheckboxFilter, {
|
||||
TRACKING_LABEL_CHECKBOX,
|
||||
TRACKING_LABEL_SET,
|
||||
} from '~/search/sidebar/components/language_filter/checkbox_filter.vue';
|
||||
import CheckboxFilter from '~/search/sidebar/components/language_filter/checkbox_filter.vue';
|
||||
|
||||
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
|
||||
import { convertFiltersData } from '~/search/sidebar/utils';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
|
@ -30,6 +26,7 @@ describe('CheckboxFilter', () => {
|
|||
const defaultProps = {
|
||||
filtersData: convertFiltersData(MOCK_LANGUAGE_AGGREGATIONS_BUCKETS),
|
||||
trackingNamespace: 'testNameSpace',
|
||||
queryParam: 'language',
|
||||
};
|
||||
|
||||
const createComponent = (Props = defaultProps) => {
|
||||
|
|
@ -101,21 +98,17 @@ describe('CheckboxFilter', () => {
|
|||
|
||||
it('triggers setQuery', () => {
|
||||
expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
|
||||
key: languageFilterData.filterParam,
|
||||
key: 'language',
|
||||
value: checkedLanguageName,
|
||||
});
|
||||
});
|
||||
|
||||
it('sends tracking information when setQuery', () => {
|
||||
findFormCheckboxGroup().vm.$emit('input', checkedLanguageName);
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
defaultProps.trackingNamespace,
|
||||
TRACKING_LABEL_CHECKBOX,
|
||||
{
|
||||
label: TRACKING_LABEL_SET,
|
||||
property: checkedLanguageName,
|
||||
},
|
||||
);
|
||||
expect(trackingSpy).toHaveBeenCalledWith(defaultProps.trackingNamespace, 'checkbox', {
|
||||
label: 'set',
|
||||
property: checkedLanguageName,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ import { GlFormCheckboxGroup } from '@gitlab/ui';
|
|||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
|
||||
import { EVENT_CLICK_ZOEKT_INCLUDE_FORKS_ON_SEARCH_RESULTS_PAGE } from '~/search/sidebar/constants';
|
||||
import ForksFilter, {
|
||||
INCLUDE_FORKED_FILTER_PARAM,
|
||||
} from '~/search/sidebar/components/forks_filter/index.vue';
|
||||
import ForksFilter from '~/search/sidebar/components/forks_filter/index.vue';
|
||||
|
||||
Vue.use(Vuex);
|
||||
const { bindInternalEventDocument } = useMockInternalEventsTracking();
|
||||
|
|
@ -104,7 +101,7 @@ describe('ForksFilter', () => {
|
|||
findCheckboxFilter().vm.$emit('input', selectedFilter);
|
||||
|
||||
expect(defaultActions.setQuery).toHaveBeenCalledWith(expect.any(Object), {
|
||||
key: INCLUDE_FORKED_FILTER_PARAM,
|
||||
key: 'include_forked',
|
||||
value: 'false',
|
||||
});
|
||||
expect(selectedFilter).toEqual([false]);
|
||||
|
|
@ -120,12 +117,12 @@ describe('ForksFilter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(`dispatches internal ${EVENT_CLICK_ZOEKT_INCLUDE_FORKS_ON_SEARCH_RESULTS_PAGE}`, () => {
|
||||
it(`dispatches internal click_zoekt_include_forks_on_search_results_page`, () => {
|
||||
findCheckboxFilter().vm.$emit('change');
|
||||
const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
|
||||
|
||||
expect(trackEventSpy).toHaveBeenCalledWith(
|
||||
EVENT_CLICK_ZOEKT_INCLUDE_FORKS_ON_SEARCH_RESULTS_PAGE,
|
||||
'click_zoekt_include_forks_on_search_results_page',
|
||||
{},
|
||||
undefined,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
|
|||
import { GROUPS_LOCAL_STORAGE_KEY } from '~/search/store/constants';
|
||||
import GroupFilter from '~/search/sidebar/components/group_filter.vue';
|
||||
import SearchableDropdown from '~/search/sidebar/components/shared/searchable_dropdown.vue';
|
||||
import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/sidebar/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -72,13 +71,17 @@ describe('GroupFilter', () => {
|
|||
|
||||
describe('when @change is emitted with Any', () => {
|
||||
beforeEach(() => {
|
||||
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
|
||||
findSearchableDropdown().vm.$emit('change', {
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls setUrlParams with group null, project id null, nav_source null, and then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
[GROUP_DATA.queryParam]: null,
|
||||
[PROJECT_DATA.queryParam]: null,
|
||||
group_id: null,
|
||||
project_id: null,
|
||||
nav_source: null,
|
||||
scope: CURRENT_SCOPE,
|
||||
});
|
||||
|
|
@ -98,8 +101,8 @@ describe('GroupFilter', () => {
|
|||
|
||||
it('calls setUrlParams with group id, project id null, nav_source null, and then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
[GROUP_DATA.queryParam]: MOCK_GROUP.id,
|
||||
[PROJECT_DATA.queryParam]: null,
|
||||
group_id: MOCK_GROUP.id,
|
||||
project_id: null,
|
||||
nav_source: null,
|
||||
scope: CURRENT_SCOPE,
|
||||
});
|
||||
|
|
@ -131,7 +134,11 @@ describe('GroupFilter', () => {
|
|||
});
|
||||
|
||||
it('sets selectedGroup to ANY_OPTION', () => {
|
||||
expect(wrapper.vm.selectedGroup).toBe(ANY_OPTION);
|
||||
expect(wrapper.vm.selectedGroup).toStrictEqual({
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_f
|
|||
import StatusFilter from '~/search/sidebar/components/status_filter/index.vue';
|
||||
import LabelFilter from '~/search/sidebar/components/label_filter/index.vue';
|
||||
import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
|
||||
import { SEARCH_TYPE_ADVANCED, SEARCH_TYPE_BASIC } from '~/search/sidebar/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ describe('GlobalSearch IssuesFilters', () => {
|
|||
const store = new Vuex.Store({
|
||||
state: {
|
||||
urlQuery: MOCK_QUERY,
|
||||
searchType: SEARCH_TYPE_ADVANCED,
|
||||
searchType: 'advanced',
|
||||
...initialState,
|
||||
},
|
||||
getters: defaultGetters,
|
||||
|
|
@ -64,7 +63,7 @@ describe('GlobalSearch IssuesFilters', () => {
|
|||
|
||||
describe('Renders correctly with basic search', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ initialState: { searchType: SEARCH_TYPE_BASIC } });
|
||||
createComponent({ initialState: { searchType: 'basic' } });
|
||||
});
|
||||
it('renders StatusFilter', () => {
|
||||
expect(findStatusFilter().exists()).toBe(true);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ import {
|
|||
MOCK_LABEL_AGGREGATIONS,
|
||||
MOCK_FILTERED_UNSELECTED_LABELS,
|
||||
} from 'jest/search/mock_data';
|
||||
|
||||
import LabelFilter from '~/search/sidebar/components/label_filter/index.vue';
|
||||
|
||||
import LabelDropdownItems from '~/search/sidebar/components/label_filter/label_dropdown_items.vue';
|
||||
|
||||
import * as actions from '~/search/store/actions';
|
||||
|
|
@ -26,16 +28,6 @@ import * as getters from '~/search/store/getters';
|
|||
import mutations from '~/search/store/mutations';
|
||||
import createState from '~/search/store/state';
|
||||
|
||||
import {
|
||||
TRACKING_LABEL_FILTER,
|
||||
TRACKING_LABEL_DROPDOWN,
|
||||
TRACKING_LABEL_CHECKBOX,
|
||||
TRACKING_ACTION_SELECT,
|
||||
TRACKING_ACTION_SHOW,
|
||||
} from '~/search/sidebar/components/label_filter/tracking';
|
||||
|
||||
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
|
||||
|
||||
import {
|
||||
RECEIVE_AGGREGATIONS_SUCCESS,
|
||||
REQUEST_AGGREGATIONS,
|
||||
|
|
@ -216,8 +208,8 @@ describe('GlobalSearchSidebarLabelFilter', () => {
|
|||
});
|
||||
|
||||
it('sends tracking information when dropdown is opened', () => {
|
||||
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SHOW, TRACKING_LABEL_DROPDOWN, {
|
||||
label: TRACKING_LABEL_DROPDOWN,
|
||||
expect(trackingSpy).toHaveBeenCalledWith('search:agreggations:label:show', 'Dropdown', {
|
||||
label: 'Dropdown',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -352,15 +344,19 @@ describe('GlobalSearchSidebarLabelFilter', () => {
|
|||
it('trigger event', () => {
|
||||
expect(actionSpies.setQuery).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({ key: LABEL_FILTER_PARAM, value: ['Cosche'] }),
|
||||
expect.objectContaining({ key: 'label_name', value: ['Cosche'] }),
|
||||
);
|
||||
});
|
||||
|
||||
it('sends tracking information when checkbox is selected', () => {
|
||||
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SELECT, TRACKING_LABEL_CHECKBOX, {
|
||||
label: TRACKING_LABEL_FILTER,
|
||||
property: [6],
|
||||
});
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
'search:agreggations:label:select',
|
||||
'Label Checkbox',
|
||||
{
|
||||
label: 'Label Key',
|
||||
property: [6],
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,18 +12,6 @@ import {
|
|||
import LanguageFilter from '~/search/sidebar/components/language_filter/index.vue';
|
||||
import CheckboxFilter from '~/search/sidebar/components/language_filter/checkbox_filter.vue';
|
||||
|
||||
import {
|
||||
TRACKING_LABEL_SHOW_MORE,
|
||||
TRACKING_PROPERTY_MAX,
|
||||
TRACKING_LABEL_MAX,
|
||||
TRACKING_LABEL_FILTERS,
|
||||
TRACKING_ACTION_SHOW,
|
||||
TRACKING_ACTION_CLICK,
|
||||
TRACKING_LABEL_ALL,
|
||||
} from '~/search/sidebar/components/language_filter/tracking';
|
||||
|
||||
import { MAX_ITEM_LENGTH } from '~/search/sidebar/components/language_filter/data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('GlobalSearchSidebarLanguageFilter', () => {
|
||||
|
|
@ -104,23 +92,23 @@ describe('GlobalSearchSidebarLanguageFilter', () => {
|
|||
unmockTracking();
|
||||
});
|
||||
|
||||
it(`renders ${MAX_ITEM_LENGTH} amount of items`, async () => {
|
||||
it(`renders 100 items`, async () => {
|
||||
findShowMoreButton().vm.$emit('click');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(findAllCheckboxes()).toHaveLength(MAX_ITEM_LENGTH);
|
||||
expect(findAllCheckboxes()).toHaveLength(100);
|
||||
});
|
||||
|
||||
it('sends tracking information when show more clicked', () => {
|
||||
findShowMoreButton().vm.$emit('click');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_CLICK, TRACKING_LABEL_SHOW_MORE, {
|
||||
label: TRACKING_LABEL_ALL,
|
||||
expect(trackingSpy).toHaveBeenCalledWith('search:agreggations:language:click', 'Show More', {
|
||||
label: 'All Filters',
|
||||
});
|
||||
});
|
||||
|
||||
it(`renders more then ${MAX_ITEM_LENGTH} text`, async () => {
|
||||
it(`renders more then 10 text`, async () => {
|
||||
findShowMoreButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
expect(findHasOverMax().exists()).toBe(true);
|
||||
|
|
@ -129,9 +117,9 @@ describe('GlobalSearchSidebarLanguageFilter', () => {
|
|||
it('sends tracking information when show more clicked and max item reached', () => {
|
||||
findShowMoreButton().vm.$emit('click');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SHOW, TRACKING_LABEL_FILTERS, {
|
||||
label: TRACKING_LABEL_MAX,
|
||||
property: TRACKING_PROPERTY_MAX,
|
||||
expect(trackingSpy).toHaveBeenCalledWith('search:agreggations:language:show', 'Filters', {
|
||||
label: 'Max Shown',
|
||||
property: `More than 10 filters to show`,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import StatusFilter from '~/search/sidebar/components/status_filter/index.vue';
|
|||
import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
|
||||
import SourceBranchFilter from '~/search/sidebar/components/source_branch_filter/index.vue';
|
||||
import LabelFilter from '~/search/sidebar/components/label_filter/index.vue';
|
||||
import { SEARCH_TYPE_ADVANCED, SEARCH_TYPE_BASIC } from '~/search/sidebar/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -29,7 +28,7 @@ describe('GlobalSearch MergeRequestsFilters', () => {
|
|||
const store = new Vuex.Store({
|
||||
state: {
|
||||
urlQuery: MOCK_QUERY,
|
||||
searchType: SEARCH_TYPE_ADVANCED,
|
||||
searchType: 'advanced',
|
||||
groupInitialJson: {
|
||||
id: 1,
|
||||
},
|
||||
|
|
@ -73,7 +72,7 @@ describe('GlobalSearch MergeRequestsFilters', () => {
|
|||
|
||||
describe('Renders correctly with basic search', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ searchType: SEARCH_TYPE_BASIC });
|
||||
createComponent({ searchType: 'basic' });
|
||||
});
|
||||
|
||||
it('renders StatusFilter', () => {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ import Vue from 'vue';
|
|||
import Vuex from 'vuex';
|
||||
import { MOCK_PROJECT, MOCK_QUERY, CURRENT_SCOPE } from 'jest/search/mock_data';
|
||||
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
|
||||
import { PROJECTS_LOCAL_STORAGE_KEY } from '~/search/store/constants';
|
||||
import ProjectFilter from '~/search/sidebar/components/project_filter.vue';
|
||||
import SearchableDropdown from '~/search/sidebar/components/shared/searchable_dropdown.vue';
|
||||
import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/sidebar/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -75,13 +73,17 @@ describe('ProjectFilter', () => {
|
|||
describe('when @change is emitted', () => {
|
||||
describe('with Any', () => {
|
||||
beforeEach(() => {
|
||||
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
|
||||
findSearchableDropdown().vm.$emit('change', {
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls setUrlParams with null, no group id, nav_source null, then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
include_archived: null,
|
||||
[PROJECT_DATA.queryParam]: null,
|
||||
project_id: null,
|
||||
nav_source: null,
|
||||
scope: CURRENT_SCOPE,
|
||||
});
|
||||
|
|
@ -100,16 +102,16 @@ describe('ProjectFilter', () => {
|
|||
|
||||
it('calls setUrlParams with project id, group id, nav_source null, then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
[GROUP_DATA.queryParam]: MOCK_PROJECT.namespace.id,
|
||||
group_id: MOCK_PROJECT.namespace.id,
|
||||
include_archived: null,
|
||||
[PROJECT_DATA.queryParam]: MOCK_PROJECT.id,
|
||||
project_id: MOCK_PROJECT.id,
|
||||
nav_source: null,
|
||||
scope: CURRENT_SCOPE,
|
||||
});
|
||||
expect(visitUrl).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`calls setFrequentProject with the group and ${PROJECTS_LOCAL_STORAGE_KEY}`, () => {
|
||||
it(`calls setFrequentProject with the group and global-search-frequent-projects`, () => {
|
||||
expect(actionSpies.setFrequentProject).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
MOCK_PROJECT,
|
||||
|
|
@ -133,11 +135,24 @@ describe('ProjectFilter', () => {
|
|||
describe('selectedProject', () => {
|
||||
describe('when initialData is null', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ projectInitialJson: ANY_OPTION }, {});
|
||||
createComponent(
|
||||
{
|
||||
projectInitialJson: {
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
},
|
||||
},
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it('sets selectedProject to ANY_OPTION', () => {
|
||||
expect(cloneDeep(wrapper.vm.selectedProject)).toStrictEqual(ANY_OPTION);
|
||||
expect(cloneDeep(wrapper.vm.selectedProject)).toStrictEqual({
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import Vue from 'vue';
|
|||
import Vuex from 'vuex';
|
||||
import { MOCK_QUERY } from 'jest/search/mock_data';
|
||||
import RadioFilter from '~/search/sidebar/components/shared/radio_filter.vue';
|
||||
import { confidentialFilterData } from '~/search/sidebar/components/confidentiality_filter/data';
|
||||
import { statusFilterData } from '~/search/sidebar/components/status_filter/data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -22,10 +20,66 @@ describe('RadioFilter', () => {
|
|||
};
|
||||
|
||||
const defaultProps = {
|
||||
filterData: statusFilterData,
|
||||
filtersArray: {
|
||||
issues: [
|
||||
{
|
||||
label: 'Any',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
label: 'Confidential',
|
||||
value: 'yes',
|
||||
},
|
||||
{
|
||||
label: 'Not confidential',
|
||||
value: 'no',
|
||||
},
|
||||
],
|
||||
},
|
||||
header: 'Confidentiality',
|
||||
filterParam: 'confidential',
|
||||
};
|
||||
|
||||
const createComponent = (initialState, props = {}) => {
|
||||
const statusDefaultProps = {
|
||||
filtersArray: {
|
||||
issues: [
|
||||
{
|
||||
label: 'Any',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
label: 'Open',
|
||||
value: 'opened',
|
||||
},
|
||||
{
|
||||
label: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
merge_requests: [
|
||||
{
|
||||
label: 'Any',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
label: 'Open',
|
||||
value: 'opened',
|
||||
},
|
||||
{
|
||||
label: 'Merged',
|
||||
value: 'merged',
|
||||
},
|
||||
{
|
||||
label: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
},
|
||||
header: 'Status',
|
||||
filterParam: 'state',
|
||||
};
|
||||
|
||||
const createComponent = (initialState = {}, props = {}) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
query: MOCK_QUERY,
|
||||
|
|
@ -59,50 +113,46 @@ describe('RadioFilter', () => {
|
|||
|
||||
describe('Radio Buttons', () => {
|
||||
describe('Status Filter', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, statusDefaultProps);
|
||||
});
|
||||
it('renders a radio button for each filterOption', () => {
|
||||
expect(findGlRadioButtonsText()).toStrictEqual(
|
||||
statusFilterData.filterByScope[statusFilterData.scopes.ISSUES].map((f) => {
|
||||
return f.value === statusFilterData.filters.ANY.value
|
||||
? `Any ${statusFilterData.header.toLowerCase()}`
|
||||
: f.label;
|
||||
statusDefaultProps.filtersArray.issues.map((f) => {
|
||||
return f.value === null ? `Any ${'Status'.toLowerCase()}` : f.label;
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('clicking a radio button item calls setQuery', () => {
|
||||
const filter = statusFilterData.filters[Object.keys(statusFilterData.filters)[0]].value;
|
||||
findGlRadioButtonGroup().vm.$emit('input', filter);
|
||||
findGlRadioButtonGroup().vm.$emit('input', 'opened');
|
||||
|
||||
expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
|
||||
key: statusFilterData.filterParam,
|
||||
value: filter,
|
||||
key: 'state',
|
||||
value: 'opened',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Confidentiality Filter', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, { filterData: confidentialFilterData });
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders a radio button for each filterOption', () => {
|
||||
expect(findGlRadioButtonsText()).toStrictEqual(
|
||||
confidentialFilterData.filterByScope[confidentialFilterData.scopes.ISSUES].map((f) => {
|
||||
return f.value === confidentialFilterData.filters.ANY.value
|
||||
? `Any ${confidentialFilterData.header.toLowerCase()}`
|
||||
: f.label;
|
||||
defaultProps.filtersArray.issues.map((f) => {
|
||||
return f.value === null ? `Any ${'Confidentiality'.toLowerCase()}` : f.label;
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('clicking a radio button item calls setQuery', () => {
|
||||
const filter =
|
||||
confidentialFilterData.filters[Object.keys(confidentialFilterData.filters)[0]].value;
|
||||
findGlRadioButtonGroup().vm.$emit('input', filter);
|
||||
findGlRadioButtonGroup().vm.$emit('input', 'closed');
|
||||
|
||||
expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
|
||||
key: confidentialFilterData.filterParam,
|
||||
value: filter,
|
||||
key: 'confidential',
|
||||
value: 'closed',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import Vuex from 'vuex';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { MOCK_GROUPS, MOCK_QUERY } from 'jest/search/mock_data';
|
||||
import SearchableDropdown from '~/search/sidebar/components/shared/searchable_dropdown.vue';
|
||||
import { ANY_OPTION, GROUP_DATA } from '~/search/sidebar/constants';
|
||||
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
|
@ -16,11 +15,15 @@ describe('Global Search Searchable Dropdown', () => {
|
|||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
headerText: GROUP_DATA.headerText,
|
||||
name: GROUP_DATA.name,
|
||||
fullName: GROUP_DATA.fullName,
|
||||
headerText: 'Filter results by group',
|
||||
name: 'name',
|
||||
fullName: 'full_name',
|
||||
loading: false,
|
||||
selectedItem: ANY_OPTION,
|
||||
selectedItem: {
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
},
|
||||
items: [],
|
||||
frequentItems: [{ ...MOCK_GROUPS[0] }],
|
||||
searchHandler: jest.fn(),
|
||||
|
|
@ -55,7 +58,18 @@ describe('Global Search Searchable Dropdown', () => {
|
|||
});
|
||||
|
||||
const propItems = [
|
||||
{ text: '', options: [{ value: ANY_OPTION.name, text: ANY_OPTION.name, ...ANY_OPTION }] },
|
||||
{
|
||||
text: '',
|
||||
options: [
|
||||
{
|
||||
value: 'Any',
|
||||
text: 'Any',
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Frequently searched',
|
||||
options: [{ value: MOCK_GROUPS[0].id, text: MOCK_GROUPS[0].full_name, ...MOCK_GROUPS[0] }],
|
||||
|
|
@ -86,7 +100,11 @@ describe('Global Search Searchable Dropdown', () => {
|
|||
|
||||
it('emits reset', () => {
|
||||
findGlDropdown().vm.$emit('reset');
|
||||
expect(cloneDeep(wrapper.emitted('change')[0][0])).toStrictEqual(ANY_OPTION);
|
||||
expect(cloneDeep(wrapper.emitted('change')[0][0])).toStrictEqual({
|
||||
id: null,
|
||||
name: 'Any',
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
});
|
||||
|
||||
it('emits first-open', () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ const indexedDB = new IDBFactory();
|
|||
Dexie.dependencies.indexedDB = indexedDB;
|
||||
Dexie.dependencies.IDBKeyRange = IDBKeyRange;
|
||||
|
||||
process.env.PDF_JS_WORKER_PUBLIC_PATH = 'mock/path/pdf.worker.js';
|
||||
process.env.PDF_JS_WORKER_V4_PUBLIC_PATH = 'mock/path/v4/pdf.worker.js';
|
||||
process.env.PDF_JS_WORKER_V3_PUBLIC_PATH = 'mock/path/v3/pdf.worker.js';
|
||||
|
||||
afterEach(() =>
|
||||
// give Promises a bit more time so they fail the right test
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Size, feature_category: :pipeline_composition do
|
||||
let_it_be(:namespace) { create(:namespace) }
|
||||
let_it_be(:project, reload: true) { create(:project, :repository, namespace: namespace) }
|
||||
let_it_be(:default_plan) { create(:default_plan) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:pipeline) { build(:ci_pipeline, project: project) }
|
||||
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command,
|
||||
project: project,
|
||||
current_user: user,
|
||||
pipeline_seed: instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 1))
|
||||
end
|
||||
|
||||
let(:step) { described_class.new(pipeline, command) }
|
||||
|
||||
subject(:perform_step) { step.perform! }
|
||||
|
||||
context 'when pipeline size limit is exceeded' do
|
||||
before do
|
||||
create(:plan_limits, plan: default_plan, ci_pipeline_size: 1)
|
||||
end
|
||||
|
||||
context 'when saving incomplete pipelines' do
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command,
|
||||
project: project,
|
||||
current_user: user,
|
||||
save_incompleted: true,
|
||||
pipeline_seed: instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2))
|
||||
end
|
||||
|
||||
it 'drops the pipeline' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline.reload).to be_failed
|
||||
end
|
||||
|
||||
it 'persists the pipeline' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline).to be_persisted
|
||||
end
|
||||
|
||||
it 'breaks the chain' do
|
||||
perform_step
|
||||
|
||||
expect(step.break?).to be true
|
||||
end
|
||||
|
||||
it 'sets a valid failure reason' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline.size_limit_exceeded?).to be true
|
||||
end
|
||||
|
||||
it 'appends validation error' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline.errors.to_a)
|
||||
.to include "The number of jobs has exceeded the limit of 1. " \
|
||||
"Try splitting the configuration with parent-child-pipelines " \
|
||||
"http://localhost/help/ci/debugging.md#pipeline-with-many-jobs-fails-to-start"
|
||||
end
|
||||
|
||||
it 'logs the error' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
instance_of(Gitlab::Ci::Limit::LimitExceededError),
|
||||
{
|
||||
jobs_count: pipeline.statuses.count,
|
||||
project_id: project.id, plan: namespace.actual_plan_name,
|
||||
project_full_path: project.full_path, pipeline_source: pipeline.source
|
||||
}
|
||||
)
|
||||
|
||||
perform_step
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not saving incomplete pipelines' do
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command,
|
||||
project: project,
|
||||
current_user: user,
|
||||
save_incompleted: false,
|
||||
pipeline_seed: instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2),
|
||||
increment_pipeline_failure_reason_counter: true)
|
||||
end
|
||||
|
||||
it 'fails but does not persist the pipeline' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline).not_to be_persisted
|
||||
expect(pipeline).to be_size_limit_exceeded
|
||||
expect(pipeline).to be_failed
|
||||
end
|
||||
|
||||
it 'breaks the chain' do
|
||||
perform_step
|
||||
|
||||
expect(step.break?).to be true
|
||||
end
|
||||
|
||||
it 'increments the error metric' do
|
||||
expect(command).to receive(:increment_pipeline_failure_reason_counter).with(:size_limit_exceeded)
|
||||
|
||||
perform_step
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline size limit is not exceeded' do
|
||||
before do
|
||||
create(:plan_limits, plan: default_plan, ci_pipeline_size: 100)
|
||||
end
|
||||
|
||||
it 'does not break the chain' do
|
||||
perform_step
|
||||
|
||||
expect(step.break?).to be false
|
||||
end
|
||||
|
||||
it 'does not persist the pipeline' do
|
||||
perform_step
|
||||
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
|
||||
it 'does not log any error' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
|
||||
|
||||
perform_step
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline size limit is disabled' do
|
||||
before do
|
||||
create(:plan_limits, plan: default_plan, ci_pipeline_size: 0)
|
||||
end
|
||||
|
||||
context 'when global pipeline size limit is exceeded' do
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command,
|
||||
project: project,
|
||||
current_user: user,
|
||||
pipeline_seed: instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2001))
|
||||
end
|
||||
|
||||
it 'logs the pipeline' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
instance_of(Gitlab::Ci::Limit::LimitExceededError),
|
||||
{
|
||||
jobs_count: pipeline.statuses.count,
|
||||
project_id: project.id, plan: namespace.actual_plan_name,
|
||||
project_full_path: project.full_path, pipeline_source: pipeline.source
|
||||
}
|
||||
)
|
||||
|
||||
perform_step
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Pipeline::Quota::Size, feature_category: :pipeline_composition do
|
||||
let_it_be(:namespace) { create(:namespace) }
|
||||
let_it_be(:project, reload: true) { create(:project, :repository, namespace: namespace) }
|
||||
let_it_be(:plan_limits, reload: true) { create(:plan_limits, :default_plan) }
|
||||
|
||||
let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
|
||||
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command, pipeline_seed: pipeline_seed_double)
|
||||
end
|
||||
|
||||
let(:pipeline_seed_double) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2)
|
||||
end
|
||||
|
||||
subject { described_class.new(namespace, pipeline, command) }
|
||||
|
||||
shared_context 'when pipeline size limit exceeded' do
|
||||
before do
|
||||
plan_limits.update!(ci_pipeline_size: 1)
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when pipeline size limit not exceeded' do
|
||||
before do
|
||||
plan_limits.update!(ci_pipeline_size: 2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#enabled?' do
|
||||
context 'when limit is enabled in plan' do
|
||||
before do
|
||||
plan_limits.update!(ci_pipeline_size: 10)
|
||||
end
|
||||
|
||||
it 'is enabled' do
|
||||
is_expected.to be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limit is not enabled' do
|
||||
before do
|
||||
plan_limits.update!(ci_pipeline_size: 0)
|
||||
end
|
||||
|
||||
it 'is not enabled' do
|
||||
is_expected.not_to be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limit does not exist' do
|
||||
before do
|
||||
allow(namespace).to receive(:actual_plan) { create(:default_plan) }
|
||||
end
|
||||
|
||||
it 'is not enabled' do
|
||||
is_expected.not_to be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#exceeded?' do
|
||||
context 'when limit is exceeded' do
|
||||
include_context 'when pipeline size limit exceeded'
|
||||
|
||||
it 'is exceeded' do
|
||||
is_expected.to be_exceeded
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limit is not exceeded' do
|
||||
include_context 'when pipeline size limit not exceeded'
|
||||
|
||||
it 'is not exceeded' do
|
||||
is_expected.not_to be_exceeded
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#message' do
|
||||
context 'when limit is exceeded' do
|
||||
include_context 'when pipeline size limit exceeded'
|
||||
|
||||
it 'returns info about pipeline size limit exceeded' do
|
||||
is_expected.to have_attributes(message: "The number of jobs has exceeded the limit of 1. " \
|
||||
"Try splitting the configuration with parent-child-pipelines " \
|
||||
"http://localhost/help/ci/debugging.md#pipeline-with-many-jobs-fails-to-start")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#log_exceeded_limit?' do
|
||||
context 'when there are more than 2000 jobs in the pipeline' do
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command, pipeline_seed: pipeline_seed_double)
|
||||
end
|
||||
|
||||
let(:pipeline_seed_double) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2001)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
is_expected.to be_log_exceeded_limit
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are 2000 or less jobs in the pipeline' do
|
||||
let(:command) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Chain::Command, pipeline_seed: pipeline_seed_double)
|
||||
end
|
||||
|
||||
let(:pipeline_seed_double) do
|
||||
instance_double(::Gitlab::Ci::Pipeline::Seed::Pipeline, size: 2000)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
is_expected.not_to be_log_exceeded_limit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -9,14 +9,8 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
|
|||
let(:rewriter) { described_class.new(+text, nil, old_project, user) }
|
||||
|
||||
context 'text contains links to uploads' do
|
||||
let(:image_uploader) do
|
||||
build(:file_uploader, project: old_project)
|
||||
end
|
||||
|
||||
let(:zip_uploader) do
|
||||
build(:file_uploader, project: old_project,
|
||||
fixture: 'ci_build_artifacts.zip')
|
||||
end
|
||||
let(:image_uploader) { build(:file_uploader, container: old_project) }
|
||||
let(:zip_uploader) { build(:file_uploader, container: old_project, fixture: 'ci_build_artifacts.zip') }
|
||||
|
||||
let(:text) do
|
||||
"Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}".freeze
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ RSpec.describe 'InternalEventsCli::Flows::EventDefiner', :aggregate_failures, fe
|
|||
include_context 'when running the Internal Events Cli'
|
||||
|
||||
describe 'end-to-end behavior' do
|
||||
YAML.safe_load(File.read('spec/fixtures/scripts/internal_events/new_events.yml')).each do |test_case|
|
||||
YAML.safe_load(File.read('spec/fixtures/scripts/internal_events/event_definer_examples.yml')).each do |test_case|
|
||||
it_behaves_like 'creates the right definition files', test_case['description'], test_case
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ RSpec.describe 'InternalEventsCli::Flows::MetricDefiner', :aggregate_failures, f
|
|||
let_it_be(:event3_content) { internal_event_fixture('events/secondary_event_with_identifiers.yml') }
|
||||
|
||||
describe 'end-to-end behavior' do
|
||||
YAML.safe_load(File.read('spec/fixtures/scripts/internal_events/new_metrics.yml')).each do |test_case|
|
||||
YAML.safe_load(File.read('spec/fixtures/scripts/internal_events/metric_definer_examples.yml')).each do |test_case|
|
||||
it_behaves_like 'creates the right definition files', test_case['description'], test_case
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ RSpec.describe MarkdownContentRewriterService, feature_category: :markdown do
|
|||
end
|
||||
|
||||
context 'when content contains an upload' do
|
||||
let(:image_uploader) { build(:file_uploader, project: source_parent) }
|
||||
let(:image_uploader) { build(:file_uploader, container: source_parent) }
|
||||
let(:content) { "Text and #{image_uploader.markdown_link}" }
|
||||
|
||||
it 'rewrites content' do
|
||||
|
|
@ -96,7 +96,7 @@ RSpec.describe MarkdownContentRewriterService, feature_category: :markdown do
|
|||
end
|
||||
|
||||
context 'when content has uploaded file references' do
|
||||
let(:image_uploader) { build(:file_uploader, project: source_parent) }
|
||||
let(:image_uploader) { build(:file_uploader, container: source_parent) }
|
||||
let(:content) { "Text and #{image_uploader.markdown_link}" }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ RSpec.describe Notes::CopyService, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
context 'notes with upload' do
|
||||
let(:uploader) { build(:file_uploader, project: from_noteable.project) }
|
||||
let(:uploader) { build(:file_uploader, container: from_noteable.project) }
|
||||
let(:text) { "Simple text with image: #{uploader.markdown_link} " }
|
||||
let!(:note) { create(:note, noteable: from_noteable, note: text, project: from_noteable.project) }
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ RSpec.describe Projects::AfterRenameService, feature_category: :groups_and_proje
|
|||
|
||||
context 'attachments' do
|
||||
let(:uploader) { create(:upload, :issuable_upload, :with_file, model: project) }
|
||||
let(:file_uploader) { build(:file_uploader, project: project) }
|
||||
let(:file_uploader) { build(:file_uploader, container: project) }
|
||||
let(:legacy_storage_path) { File.join(file_uploader.root, legacy_storage.disk_path) }
|
||||
let(:hashed_storage_path) { File.join(file_uploader.root, hashed_storage.disk_path) }
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ RSpec.describe Projects::HashedStorage::MigrateAttachmentsService, feature_categ
|
|||
let(:hashed_storage) { Storage::Hashed.new(project) }
|
||||
|
||||
let!(:upload) { Upload.find_by(path: file_uploader.upload_path) }
|
||||
let(:file_uploader) { build(:file_uploader, project: project) }
|
||||
let(:file_uploader) { build(:file_uploader, container: project) }
|
||||
let(:old_disk_path) { File.join(base_path(legacy_storage), upload.path) }
|
||||
let(:new_disk_path) { File.join(base_path(hashed_storage), upload.path) }
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue