Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
24fca38040
commit
962fbcfb94
|
@ -57,7 +57,11 @@ tasks:
|
|||
# Waiting for GitLab ...
|
||||
gp await-port 3000
|
||||
printf "Waiting for GitLab at $(gp url 3000) ..."
|
||||
until $(curl -sNL $(gp url 3000) | grep -q "GitLab"); do printf '.'; sleep 5; done && echo ""
|
||||
# Check /-/readiness which returns JSON, but we're only interested in the exit code
|
||||
#
|
||||
# We use http://localhost:3000 instead of the public hostname because
|
||||
# it's no longer possible to access as specific cookies are required
|
||||
until curl --silent --no-buffer --fail http://localhost:3000/-/readiness > /dev/null 2>&1; do printf '.'; sleep 5; done && echo ""
|
||||
# Give Gitpod a few more seconds to set up everything ...
|
||||
sleep 5
|
||||
printf "$(date) – GitLab is up (took ~%.1f minutes)\n" "$((10*$SECONDS/60))e-1" | tee -a /workspace/startup.log
|
||||
|
|
|
@ -103,6 +103,7 @@ export default {
|
|||
<tr
|
||||
class="gl-h-11 gl-border-0 gl-border-solid gl-border-t-1 gl-border-gray-100 gl-h-11"
|
||||
data-qa-selector="project_import_row"
|
||||
:data-qa-source-project="repo.importSource.fullName"
|
||||
>
|
||||
<td class="gl-p-4">
|
||||
<gl-link :href="repo.importSource.providerLink" target="_blank" data-testid="providerLink"
|
||||
|
@ -155,7 +156,7 @@ export default {
|
|||
</template>
|
||||
<template v-else-if="repo.importedProject">{{ displayFullPath }}</template>
|
||||
</td>
|
||||
<td class="gl-p-4">
|
||||
<td class="gl-p-4" data-qa-selector="import_status_indicator">
|
||||
<import-status :status="importStatus" />
|
||||
</td>
|
||||
<td data-testid="actions">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlIcon, GlSprintf, GlTooltipDirective, GlBadge } from '@gitlab/ui';
|
||||
import { GlIcon, GlSprintf, GlBadge } from '@gitlab/ui';
|
||||
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
|
||||
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -8,7 +8,7 @@ import { PACKAGE_TYPE_NUGET } from '~/packages_and_registries/package_registry/c
|
|||
import { getPackageTypeLabel } from '~/packages_and_registries/package_registry/utils';
|
||||
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
|
||||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
export default {
|
||||
name: 'PackageTitle',
|
||||
|
@ -19,11 +19,8 @@ export default {
|
|||
PackageTags,
|
||||
MetadataItem,
|
||||
GlBadge,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [timeagoMixin],
|
||||
i18n: {
|
||||
packageInfo: __('v%{version} published %{timeAgo}'),
|
||||
},
|
||||
|
@ -77,17 +74,21 @@ export default {
|
|||
<title-area :title="packageEntity.name" :avatar="packageIcon" data-qa-selector="package_title">
|
||||
<template #sub-header>
|
||||
<gl-icon name="eye" class="gl-mr-3" />
|
||||
<gl-sprintf :message="$options.i18n.packageInfo">
|
||||
<template #version>
|
||||
{{ packageEntity.version }}
|
||||
</template>
|
||||
<span data-testid="sub-header">
|
||||
<gl-sprintf :message="$options.i18n.packageInfo">
|
||||
<template #version>
|
||||
{{ packageEntity.version }}
|
||||
</template>
|
||||
|
||||
<template #timeAgo>
|
||||
<span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)">
|
||||
{{ timeFormatted(packageEntity.created_at) }}
|
||||
</span>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<template #timeAgo>
|
||||
<time-ago-tooltip
|
||||
v-if="packageEntity.createdAt"
|
||||
class="gl-ml-2"
|
||||
:time="packageEntity.createdAt"
|
||||
/>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="packageTypeDisplay" #metadata-type>
|
||||
|
|
|
@ -46,38 +46,44 @@ export const fetchProjects = ({ commit, state }, search) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const loadFrequentGroups = async ({ commit }) => {
|
||||
const data = loadDataFromLS(GROUPS_LOCAL_STORAGE_KEY);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data });
|
||||
export const preloadStoredFrequentItems = ({ commit }) => {
|
||||
const storedGroups = loadDataFromLS(GROUPS_LOCAL_STORAGE_KEY);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: storedGroups });
|
||||
|
||||
const promises = data.map((d) => Api.group(d.id));
|
||||
const storedProjects = loadDataFromLS(PROJECTS_LOCAL_STORAGE_KEY);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: storedProjects });
|
||||
};
|
||||
|
||||
export const loadFrequentGroups = async ({ commit, state }) => {
|
||||
const storedData = state.frequentItems[GROUPS_LOCAL_STORAGE_KEY];
|
||||
const promises = storedData.map((d) => Api.group(d.id));
|
||||
try {
|
||||
const inflatedData = mergeById(await Promise.all(promises), data);
|
||||
const inflatedData = mergeById(await Promise.all(promises), storedData);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: inflatedData });
|
||||
} catch {
|
||||
createFlash({ message: __('There was a problem fetching recent groups.') });
|
||||
}
|
||||
};
|
||||
|
||||
export const loadFrequentProjects = async ({ commit }) => {
|
||||
const data = loadDataFromLS(PROJECTS_LOCAL_STORAGE_KEY);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data });
|
||||
|
||||
const promises = data.map((d) => Api.project(d.id).then((res) => res.data));
|
||||
export const loadFrequentProjects = async ({ commit, state }) => {
|
||||
const storedData = state.frequentItems[PROJECTS_LOCAL_STORAGE_KEY];
|
||||
const promises = storedData.map((d) => Api.project(d.id).then((res) => res.data));
|
||||
try {
|
||||
const inflatedData = mergeById(await Promise.all(promises), data);
|
||||
const inflatedData = mergeById(await Promise.all(promises), storedData);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: inflatedData });
|
||||
} catch {
|
||||
createFlash({ message: __('There was a problem fetching recent projects.') });
|
||||
}
|
||||
};
|
||||
|
||||
export const setFrequentGroup = ({ state }, item) => {
|
||||
setFrequentItemToLS(GROUPS_LOCAL_STORAGE_KEY, state.frequentItems, item);
|
||||
export const setFrequentGroup = ({ state, commit }, item) => {
|
||||
const frequentItems = setFrequentItemToLS(GROUPS_LOCAL_STORAGE_KEY, state.frequentItems, item);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: frequentItems });
|
||||
};
|
||||
|
||||
export const setFrequentProject = ({ state }, item) => {
|
||||
setFrequentItemToLS(PROJECTS_LOCAL_STORAGE_KEY, state.frequentItems, item);
|
||||
export const setFrequentProject = ({ state, commit }, item) => {
|
||||
const frequentItems = setFrequentItemToLS(PROJECTS_LOCAL_STORAGE_KEY, state.frequentItems, item);
|
||||
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: frequentItems });
|
||||
};
|
||||
|
||||
export const setQuery = ({ commit }, { key, value }) => {
|
||||
|
|
|
@ -21,7 +21,7 @@ export const loadDataFromLS = (key) => {
|
|||
|
||||
export const setFrequentItemToLS = (key, data, itemData) => {
|
||||
if (!AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const keyList = [
|
||||
|
@ -66,9 +66,11 @@ export const setFrequentItemToLS = (key, data, itemData) => {
|
|||
// Note we do not need to commit a mutation here as immediately after this we refresh the page to
|
||||
// update the search results.
|
||||
localStorage.setItem(key, JSON.stringify(frequentItems));
|
||||
return frequentItems;
|
||||
} catch {
|
||||
// The LS got in a bad state, let's wipe it
|
||||
localStorage.removeItem(key);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -39,8 +39,11 @@ export default {
|
|||
return !this.query.snippets || this.query.snippets === 'false';
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.preloadStoredFrequentItems();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['applyQuery', 'setQuery']),
|
||||
...mapActions(['applyQuery', 'setQuery', 'preloadStoredFrequentItems']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -18,12 +18,18 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['groups', 'fetchingGroups']),
|
||||
...mapState(['query', 'groups', 'fetchingGroups']),
|
||||
...mapGetters(['frequentGroups']),
|
||||
selectedGroup() {
|
||||
return isEmpty(this.initialData) ? ANY_OPTION : this.initialData;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// This tracks groups searched via the top nav search bar
|
||||
if (this.query.nav_source === 'navbar' && this.initialData?.id) {
|
||||
this.setFrequentGroup(this.initialData);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchGroups', 'setFrequentGroup', 'loadFrequentGroups']),
|
||||
handleGroupChange(group) {
|
||||
|
@ -33,7 +39,11 @@ export default {
|
|||
}
|
||||
|
||||
visitUrl(
|
||||
setUrlParams({ [GROUP_DATA.queryParam]: group.id, [PROJECT_DATA.queryParam]: null }),
|
||||
setUrlParams({
|
||||
[GROUP_DATA.queryParam]: group.id,
|
||||
[PROJECT_DATA.queryParam]: null,
|
||||
nav_source: null,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -17,12 +17,18 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['projects', 'fetchingProjects']),
|
||||
...mapState(['query', 'projects', 'fetchingProjects']),
|
||||
...mapGetters(['frequentProjects']),
|
||||
selectedProject() {
|
||||
return this.initialData ? this.initialData : ANY_OPTION;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// This tracks projects searched via the top nav search bar
|
||||
if (this.query.nav_source === 'navbar' && this.initialData?.id) {
|
||||
this.setFrequentProject(this.initialData);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchProjects', 'setFrequentProject', 'loadFrequentProjects']),
|
||||
handleProjectChange(project) {
|
||||
|
@ -35,6 +41,7 @@ export default {
|
|||
const queryParams = {
|
||||
...(project.namespace?.id && { [GROUP_DATA.queryParam]: project.namespace.id }),
|
||||
[PROJECT_DATA.queryParam]: project.id,
|
||||
nav_source: null,
|
||||
};
|
||||
|
||||
visitUrl(setUrlParams(queryParams));
|
||||
|
|
|
@ -284,8 +284,8 @@ export class SearchAutocomplete {
|
|||
if (projectId) {
|
||||
const projectOptions = gl.projectOptions[getProjectSlug()];
|
||||
const url = groupId
|
||||
? `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&group_id=${groupId}`
|
||||
: `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}`;
|
||||
? `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&group_id=${groupId}&nav_source=navbar`
|
||||
: `${gon.relative_url_root}/search?search=${term}&project_id=${projectId}&nav_source=navbar`;
|
||||
|
||||
options.push({
|
||||
icon,
|
||||
|
@ -313,7 +313,7 @@ export class SearchAutocomplete {
|
|||
},
|
||||
false,
|
||||
),
|
||||
url: `${gon.relative_url_root}/search?search=${term}&group_id=${groupId}`,
|
||||
url: `${gon.relative_url_root}/search?search=${term}&group_id=${groupId}&nav_source=navbar`,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -321,7 +321,7 @@ export class SearchAutocomplete {
|
|||
icon,
|
||||
text: term,
|
||||
template: s__('SearchAutocomplete|in all GitLab'),
|
||||
url: `${gon.relative_url_root}/search?search=${term}`,
|
||||
url: `${gon.relative_url_root}/search?search=${term}&nav_source=navbar`,
|
||||
});
|
||||
|
||||
return options;
|
||||
|
|
|
@ -143,7 +143,7 @@ module GroupsHelper
|
|||
end
|
||||
|
||||
def remove_group_message(group)
|
||||
_("You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") %
|
||||
_("You are going to remove %{group_name}. This will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") %
|
||||
{ group_name: group.name }
|
||||
end
|
||||
|
||||
|
|
|
@ -101,6 +101,8 @@ module Projects
|
|||
@project.track_project_repository
|
||||
@project.create_project_setting unless @project.project_setting
|
||||
|
||||
yield if block_given?
|
||||
|
||||
event_service.create_project(@project, current_user)
|
||||
system_hook_service.execute_hooks_for(@project, :create)
|
||||
|
||||
|
|
|
@ -28,3 +28,4 @@
|
|||
= render 'groups/settings/transfer', group: @group
|
||||
= render 'groups/settings/remove', group: @group
|
||||
= render_if_exists 'groups/settings/restore', group: @group
|
||||
= render_if_exists 'groups/settings/immediately_remove', group: @group
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
= hidden_field_tag :group_id, params[:group_id]
|
||||
- if params[:project_id].present?
|
||||
= hidden_field_tag :project_id, params[:project_id]
|
||||
- group_attributes = @group&.attributes&.slice('id', 'name')&.merge(full_name: @group&.full_name)
|
||||
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
|
||||
|
||||
- if @search_results
|
||||
|
@ -16,7 +17,7 @@
|
|||
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
|
||||
|
||||
.gl-mt-3
|
||||
#js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
|
||||
#js-search-topbar{ data: { "group-initial-data": group_attributes.to_json, "project-initial-data": project_attributes.to_json } }
|
||||
- if @search_term
|
||||
= render 'search/category'
|
||||
= render 'search/results'
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: allow_archive_as_web_access_format
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64471
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334944
|
||||
milestone: '14.1'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: false
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: counts_monthly.successful_deployments
|
||||
description: Total successful deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: counts_monthly.failed_deployments
|
||||
description: Total failed deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,16 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage_monthly.release.deployments
|
||||
description: Unique users triggering deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage_monthly.release.failed_deployments
|
||||
description: Total failed deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage_monthly.release.releases
|
||||
description: Unique users creating release tags
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage_monthly.release.successful_deployments
|
||||
description: Total successful deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -4,7 +4,7 @@ key_path: counts.deployments
|
|||
description: Total deployments count
|
||||
product_section: ops
|
||||
product_stage: release
|
||||
product_group: group::ops release
|
||||
product_group: group::release
|
||||
value_type: number
|
||||
status: data_available
|
||||
milestone: "8.12"
|
||||
|
|
|
@ -4,14 +4,16 @@ key_path: counts.feature_flags
|
|||
description: Number of feature flag toggles
|
||||
product_section: ops
|
||||
product_stage: release
|
||||
product_group: group::progressive delivery
|
||||
product_category:
|
||||
product_group: group::release
|
||||
product_category: feature_flags
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
---
|
||||
data_category: Optional
|
||||
key_path: counts.deploy_keys
|
||||
description:
|
||||
description: Count of deploy keys
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: counts.successful_deployments
|
||||
description: Total successful deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: counts.failed_deployments
|
||||
description: Total failed deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: counts.environments
|
||||
description: Total available and stopped environments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
---
|
||||
data_category: Optional
|
||||
key_path: counts.in_review_folder
|
||||
description:
|
||||
description: A number of environments with name review/*
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
---
|
||||
data_category: Optional
|
||||
key_path: counts.releases
|
||||
description: Unique release tags
|
||||
description: Count of releases
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: releases
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: release_orchestration
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage.release.deployments
|
||||
description: Unique users triggering deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage.release.failed_deployments
|
||||
description: Total failed deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage.release.releases
|
||||
description: Unique users creating release tags
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: release_orchestration
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -3,15 +3,17 @@ data_category: Optional
|
|||
key_path: usage_activity_by_stage.release.successful_deployments
|
||||
description: Total successful deployments
|
||||
product_section: ops
|
||||
product_stage:
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category:
|
||||
product_category: continuous_delivery
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source:
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -4,14 +4,16 @@ key_path: counts.pages_domains
|
|||
description: Total GitLab Pages domains
|
||||
product_section: ops
|
||||
product_stage: release
|
||||
product_group: group::release management
|
||||
product_category:
|
||||
product_group: group::release
|
||||
product_category: pages
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -2,16 +2,18 @@
|
|||
data_category: Optional
|
||||
key_path: gitlab_pages.version
|
||||
description: The version number of GitLab Pages
|
||||
product_section: growth
|
||||
product_stage: growth
|
||||
product_group: group::product intelligence
|
||||
product_category: collection
|
||||
product_section: ops
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category: pages
|
||||
value_type: string
|
||||
status: data_available
|
||||
time_frame: none
|
||||
data_source: system
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
|
@ -2,9 +2,9 @@
|
|||
data_category: Optional
|
||||
key_path: gitlab_pages.enabled
|
||||
description: Whether GitLab Pages is enabled
|
||||
product_section: growth
|
||||
product_stage: growth
|
||||
product_group: group::product intelligence
|
||||
product_section: ops
|
||||
product_stage: release
|
||||
product_group: group::release
|
||||
product_category: collection
|
||||
value_type: boolean
|
||||
status: data_available
|
||||
|
@ -12,6 +12,8 @@ time_frame: none
|
|||
data_source: system
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
|
@ -425,6 +425,10 @@ it('does something', () => {
|
|||
|
||||
### Mocking the current location in Jest
|
||||
|
||||
NOTE:
|
||||
The value of `window.location.href` is reset before every test to avoid earlier
|
||||
tests affecting later ones.
|
||||
|
||||
If your tests require `window.location.href` to take a particular value, use
|
||||
the `setWindowLocation` helper:
|
||||
|
||||
|
@ -442,7 +446,6 @@ it('passes', () => {
|
|||
});
|
||||
```
|
||||
|
||||
NOTE:
|
||||
To modify only the hash, use either the `setWindowLocation` helper, or assign
|
||||
directly to `window.location.hash`, e.g.:
|
||||
|
||||
|
|
|
@ -1156,7 +1156,7 @@ Tiers: `ultimate`
|
|||
|
||||
### `counts.deploy_keys`
|
||||
|
||||
Missing description
|
||||
Count of deploy keys
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181908_deploy_keys.yml)
|
||||
|
||||
|
@ -1166,7 +1166,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.deployments`
|
||||
|
||||
|
@ -1174,7 +1174,7 @@ Total deployments count
|
|||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210201124934_deployments.yml)
|
||||
|
||||
Group: `group::ops release`
|
||||
Group: `group::release`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
|
@ -1224,6 +1224,20 @@ Status: `data_available`
|
|||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.diff_searches`
|
||||
|
||||
Total count of merge request diff searches
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210723075525_diff_searches.yml)
|
||||
|
||||
Group: `group::code review`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `implemented`
|
||||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.environments`
|
||||
|
||||
Total available and stopped environments
|
||||
|
@ -1236,7 +1250,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.epic_issues`
|
||||
|
||||
|
@ -1292,7 +1306,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.feature_flags`
|
||||
|
||||
|
@ -1300,13 +1314,13 @@ Number of feature flag toggles
|
|||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181249_feature_flags.yml)
|
||||
|
||||
Group: `group::progressive delivery`
|
||||
Group: `group::release`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.geo_event_log_max_id`
|
||||
|
||||
|
@ -2864,7 +2878,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.in_review_folder`
|
||||
|
||||
Missing description
|
||||
A number of environments with name review/*
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181916_in_review_folder.yml)
|
||||
|
||||
|
@ -2874,7 +2888,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.incident_issues`
|
||||
|
||||
|
@ -4988,7 +5002,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.personal_snippets`
|
||||
|
||||
|
@ -6728,7 +6742,7 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.releases`
|
||||
|
||||
Unique release tags
|
||||
Count of releases
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181918_releases.yml)
|
||||
|
||||
|
@ -6738,7 +6752,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.remote_mirrors`
|
||||
|
||||
|
@ -7046,7 +7060,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.suggestions`
|
||||
|
||||
|
@ -7706,20 +7720,6 @@ Status: `data_available`
|
|||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.user_searches_diffs`
|
||||
|
||||
Count of users who search merge request diffs
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210723075525_user_searches_diffs.yml)
|
||||
|
||||
Group: `group::code review`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `implemented`
|
||||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.web_hooks`
|
||||
|
||||
Missing description
|
||||
|
@ -8026,7 +8026,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts_monthly.packages`
|
||||
|
||||
|
@ -8124,7 +8124,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts_weekly.aggregated_metrics.code_review_category_monthly_active_users`
|
||||
|
||||
|
@ -8424,29 +8424,29 @@ Tiers: `free`
|
|||
|
||||
Whether GitLab Pages is enabled
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/settings/20210204124934_enabled.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/settings/20210204124934_pages_enabled.yml)
|
||||
|
||||
Group: `group::product intelligence`
|
||||
Group: `group::release`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `gitlab_pages.version`
|
||||
|
||||
The version number of GitLab Pages
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/license/20210204124936_version.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/license/20210204124936_pages_version.yml)
|
||||
|
||||
Group: `group::product intelligence`
|
||||
Group: `group::release`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `gitlab_shared_runners_enabled`
|
||||
|
||||
|
@ -10716,20 +10716,6 @@ Status: `data_available`
|
|||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.code_review.i_code_review_searches_in_diff_monthly`
|
||||
|
||||
Count of searches in merge request diffs
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210722132444_i_code_review_searches_in_diff_monthly.yml)
|
||||
|
||||
Group: `group::code review`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `implemented`
|
||||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.code_review.i_code_review_user_add_suggestion_monthly`
|
||||
|
||||
Count of unique users per month who added a suggestion
|
||||
|
@ -11612,6 +11598,20 @@ Status: `implemented`
|
|||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.code_review.i_code_review_user_searches_diff_weekly`
|
||||
|
||||
Count of users who search merge request diffs
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210720144005_i_code_review_user_searches_diff_weekly.yml)
|
||||
|
||||
Group: `group::code review`
|
||||
|
||||
Data Category: `Optional`
|
||||
|
||||
Status: `implemented`
|
||||
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.code_review.i_code_review_user_single_file_diffs_monthly`
|
||||
|
||||
Count of unique users per month with diffs viewed file by file
|
||||
|
@ -20422,7 +20422,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.release.failed_deployments`
|
||||
|
||||
|
@ -20436,7 +20436,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.release.projects_mirrored_with_pipelines_enabled`
|
||||
|
||||
|
@ -20464,7 +20464,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.release.successful_deployments`
|
||||
|
||||
|
@ -20478,7 +20478,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.secure.api_fuzzing_scans`
|
||||
|
||||
|
@ -22830,7 +22830,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage_monthly.release.failed_deployments`
|
||||
|
||||
|
@ -22844,7 +22844,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage_monthly.release.projects_mirrored_with_pipelines_enabled`
|
||||
|
||||
|
@ -22872,7 +22872,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage_monthly.release.successful_deployments`
|
||||
|
||||
|
@ -22886,7 +22886,7 @@ Data Category: `Optional`
|
|||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage_monthly.secure.api_fuzzing_pipeline`
|
||||
|
||||
|
|
|
@ -430,6 +430,28 @@ Specifically:
|
|||
- In [GitLab 13.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/39504), if the user who sets up the deletion is removed from the group before the
|
||||
deletion happens, the job is cancelled, and the group is no longer scheduled for deletion.
|
||||
|
||||
## Remove a group immediately **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336985) in GitLab 14.2.
|
||||
|
||||
If you don't want to wait, you can remove a group immediately.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Owner role for a group.
|
||||
- You have [marked the group for deletion](#remove-a-group).
|
||||
|
||||
To immediately remove a group marked for deletion:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand **Advanced**.
|
||||
1. In the "Permanently remove group" section, select **Remove group**.
|
||||
1. Confirm the action when asked to.
|
||||
|
||||
Your group, its subgroups, projects, and all related resources, including issues and merge requests,
|
||||
are deleted.
|
||||
|
||||
## Restore a group **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33257) in GitLab 12.8.
|
||||
|
|
|
@ -298,7 +298,7 @@ module Gitlab
|
|||
when :api
|
||||
api_request?
|
||||
when :archive
|
||||
archive_request? if Feature.enabled?(:allow_archive_as_web_access_format, default_enabled: :yaml)
|
||||
archive_request?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def tunnel_url
|
||||
URI.join(external_url, K8S_PROXY_PATH).to_s
|
||||
uri = URI.join(external_url, K8S_PROXY_PATH)
|
||||
uri.scheme = uri.scheme.in?(%w(grpcs wss)) ? 'https' : 'http'
|
||||
uri.to_s
|
||||
end
|
||||
|
||||
# Return GitLab KAS internal_url
|
||||
|
|
|
@ -4317,6 +4317,9 @@ msgstr ""
|
|||
msgid "Are you ABSOLUTELY SURE you wish to delete this project?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you ABSOLUTELY SURE you wish to remove this group?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure that you want to archive this project?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24072,6 +24075,9 @@ msgstr ""
|
|||
msgid "Permanently delete project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Permanently remove group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33688,6 +33694,9 @@ msgstr ""
|
|||
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues and merge requests."
|
||||
msgstr ""
|
||||
|
||||
msgid "This action will %{strongOpen}permanently remove%{strongClose} %{codeOpen}%{group}%{codeClose} %{strongOpen}immediately%{strongClose}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This also resolves all related threads"
|
||||
msgstr ""
|
||||
|
||||
|
@ -37633,7 +37642,7 @@ msgstr ""
|
|||
msgid "You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
|
||||
msgid "You are going to remove %{group_name}. This will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?"
|
||||
|
|
|
@ -21,7 +21,7 @@ module QA
|
|||
end
|
||||
|
||||
def to_s
|
||||
<<~MSG.strip % { page: @page_class }
|
||||
format(<<~MSG.strip, page: @page_class)
|
||||
%{page} has no required elements.
|
||||
See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/dynamic_element_validation.html#required-elements
|
||||
MSG
|
||||
|
@ -232,7 +232,7 @@ module QA
|
|||
visible = kwargs.delete(:visible)
|
||||
visible = visible.nil? && true
|
||||
|
||||
try_find_element = ->(wait) do
|
||||
try_find_element = lambda do |wait|
|
||||
if disabled.nil?
|
||||
has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass, visible: visible)
|
||||
else
|
||||
|
@ -322,13 +322,13 @@ module QA
|
|||
# It would be ideal if we could detect when the animation is complete
|
||||
# but in some cases there's nothing we can easily access via capybara
|
||||
# so instead we wait for the element, and then we wait a little longer
|
||||
raise ElementNotFound, %Q(Couldn't find element named "#{name}") unless has_element?(name)
|
||||
raise ElementNotFound, %(Couldn't find element named "#{name}") unless has_element?(name)
|
||||
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def within_element(name, **kwargs)
|
||||
wait_for_requests
|
||||
wait_for_requests(skip_finished_loading_check: kwargs.delete(:skip_finished_loading_check))
|
||||
text = kwargs.delete(:text)
|
||||
|
||||
page.within(element_selector_css(name, kwargs), text: text) do
|
||||
|
@ -386,9 +386,7 @@ module QA
|
|||
end
|
||||
|
||||
def self.errors
|
||||
if views.empty?
|
||||
return ["Page class does not have views / elements defined!"]
|
||||
end
|
||||
return ["Page class does not have views / elements defined!"] if views.empty?
|
||||
|
||||
views.flat_map(&:errors)
|
||||
end
|
||||
|
|
|
@ -18,12 +18,17 @@ module QA
|
|||
element :import_button
|
||||
element :project_path_content
|
||||
element :go_to_project_button
|
||||
element :import_status_indicator
|
||||
end
|
||||
|
||||
view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
|
||||
element :target_namespace_selector_dropdown
|
||||
end
|
||||
|
||||
# Add personal access token
|
||||
#
|
||||
# @param [String] personal_access_token
|
||||
# @return [void]
|
||||
def add_personal_access_token(personal_access_token)
|
||||
# If for some reasons this process is retried, user cannot re-enter github token in the same group
|
||||
# In this case skip this step and proceed to import project row
|
||||
|
@ -34,71 +39,50 @@ module QA
|
|||
finished_loading?
|
||||
end
|
||||
|
||||
def import!(full_path, name)
|
||||
return if already_imported(full_path)
|
||||
|
||||
choose_test_namespace(full_path)
|
||||
set_path(full_path, name)
|
||||
import_project(full_path)
|
||||
|
||||
wait_for_success
|
||||
end
|
||||
|
||||
# TODO: refactor to use 'go to project' button instead of generic main menu
|
||||
def go_to_project(name)
|
||||
Page::Main::Menu.perform(&:go_to_projects)
|
||||
Page::Dashboard::Projects.perform do |dashboard|
|
||||
dashboard.go_to_project(name)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def within_repo_path(full_path, &block)
|
||||
project_import_row = find_element(:project_import_row, text: full_path)
|
||||
|
||||
within(project_import_row, &block)
|
||||
end
|
||||
|
||||
def choose_test_namespace(full_path)
|
||||
within_repo_path(full_path) do
|
||||
within_element(:target_namespace_selector_dropdown) { click_button(class: 'dropdown-toggle') }
|
||||
click_element(:target_group_dropdown_item, group_name: Runtime::Namespace.path)
|
||||
end
|
||||
end
|
||||
|
||||
def set_path(full_path, name)
|
||||
within_repo_path(full_path) do
|
||||
fill_element(:project_path_field, name)
|
||||
end
|
||||
end
|
||||
|
||||
def import_project(full_path)
|
||||
within_repo_path(full_path) do
|
||||
# Import project
|
||||
#
|
||||
# @param [String] source_project_name
|
||||
# @param [String] target_group_path
|
||||
# @return [void]
|
||||
def import!(gh_project_name, target_group_path, project_name)
|
||||
within_element(:project_import_row, source_project: gh_project_name) do
|
||||
click_element(:target_namespace_selector_dropdown)
|
||||
click_element(:target_group_dropdown_item, group_name: target_group_path)
|
||||
fill_element(:project_path_field, project_name)
|
||||
click_element(:import_button)
|
||||
end
|
||||
end
|
||||
|
||||
def wait_for_success
|
||||
# TODO: set reload:false and remove skip_finished_loading_check_on_refresh when
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/292861 is fixed
|
||||
wait_until(
|
||||
max_duration: 90,
|
||||
sleep_interval: 5.0,
|
||||
reload: true,
|
||||
skip_finished_loading_check_on_refresh: true
|
||||
) do
|
||||
# TODO: Refactor to explicitly wait for specific project import successful status
|
||||
# This check can create false positive if main importing message appears with delay and check exits early
|
||||
page.has_no_content?('Importing 1 repository', wait: 3)
|
||||
# Check Go to project button present
|
||||
#
|
||||
# @param [String] gh_project_name
|
||||
# @return [Boolean]
|
||||
def has_go_to_project_button?(gh_project_name)
|
||||
within_element(:project_import_row, source_project: gh_project_name) do
|
||||
has_element?(:go_to_project_button)
|
||||
end
|
||||
end
|
||||
|
||||
def already_imported(full_path)
|
||||
within_repo_path(full_path) do
|
||||
has_element?(:project_path_content) && has_element?(:go_to_project_button)
|
||||
# Check if import page has a successfully imported project
|
||||
#
|
||||
# @param [String] source_project_name
|
||||
# @param [Integer] wait
|
||||
# @return [Boolean]
|
||||
def has_imported_project?(gh_project_name, wait: QA::Support::WaitForRequests::DEFAULT_MAX_WAIT_TIME)
|
||||
within_element(:project_import_row, source_project: gh_project_name, skip_finished_loading_check: true) do
|
||||
# TODO: remove retrier with reload:true once https://gitlab.com/gitlab-org/gitlab/-/issues/292861 is fixed
|
||||
wait_until(
|
||||
max_duration: wait,
|
||||
sleep_interval: 5,
|
||||
reload: true,
|
||||
skip_finished_loading_check_on_refresh: true
|
||||
) do
|
||||
has_element?(:import_status_indicator, text: "Complete")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :wait_for_success, :has_imported_project?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,9 +18,12 @@ module QA
|
|||
|
||||
Page::Project::Import::Github.perform do |import_page|
|
||||
import_page.add_personal_access_token(github_personal_access_token)
|
||||
import_page.import!(github_repository_path, name)
|
||||
import_page.go_to_project(name)
|
||||
import_page.import!(github_repository_path, group.full_path, name)
|
||||
import_page.wait_for_success(github_repository_path)
|
||||
end
|
||||
|
||||
reload!
|
||||
visit!
|
||||
end
|
||||
|
||||
def go_to_import_page
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
module QA
|
||||
RSpec.describe 'Manage', :github, :requires_admin do
|
||||
describe 'Project import' do
|
||||
let!(:api_client) { Runtime::API::Client.as_admin }
|
||||
let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
|
||||
let!(:user) do
|
||||
let(:github_repo) { 'gitlab-qa-github/test-project' }
|
||||
let(:imported_project_name) { 'imported-project' }
|
||||
let(:api_client) { Runtime::API::Client.as_admin }
|
||||
let(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
|
||||
let(:user) do
|
||||
Resource::User.fabricate_via_api! do |resource|
|
||||
resource.api_client = api_client
|
||||
resource.hard_delete_on_api_removal = true
|
||||
|
@ -13,16 +15,25 @@ module QA
|
|||
end
|
||||
|
||||
let(:imported_project) do
|
||||
Resource::ProjectImportedFromGithub.fabricate_via_browser_ui! do |project|
|
||||
project.name = 'imported-project'
|
||||
Resource::ProjectImportedFromGithub.init do |project|
|
||||
project.import = true
|
||||
project.add_name_uuid = false
|
||||
project.name = imported_project_name
|
||||
project.group = group
|
||||
project.github_personal_access_token = Runtime::Env.github_access_token
|
||||
project.github_repository_path = 'gitlab-qa-github/test-project'
|
||||
project.github_repository_path = github_repo
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||
|
||||
Flow::Login.sign_in(as: user)
|
||||
Page::Main::Menu.perform(&:go_to_create_project)
|
||||
Page::Project::New.perform do |project_page|
|
||||
project_page.click_import_project
|
||||
project_page.click_github_link
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
|
@ -30,13 +41,24 @@ module QA
|
|||
end
|
||||
|
||||
it 'imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1762' do
|
||||
Flow::Login.sign_in(as: user)
|
||||
Page::Project::Import::Github.perform do |import_page|
|
||||
import_page.add_personal_access_token(Runtime::Env.github_access_token)
|
||||
import_page.import!(github_repo, group.full_path, imported_project_name)
|
||||
|
||||
imported_project # import the project
|
||||
aggregate_failures do
|
||||
expect(import_page).to have_imported_project(github_repo)
|
||||
# validate button is present instead of navigating to avoid dealing with multiple tabs
|
||||
# which makes the test more complicated
|
||||
expect(import_page).to have_go_to_project_button(github_repo)
|
||||
end
|
||||
end
|
||||
|
||||
imported_project.reload!.visit!
|
||||
Page::Project::Show.perform do |project|
|
||||
expect(project).to have_content(imported_project.name)
|
||||
expect(project).to have_content('This test project is used for automated GitHub import by GitLab QA.')
|
||||
aggregate_failures do
|
||||
expect(project).to have_content(imported_project_name)
|
||||
expect(project).to have_content('This test project is used for automated GitHub import by GitLab QA.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -254,6 +254,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
href = search_path(search: term)
|
||||
href.concat("&project_id=#{project_id}") if project_id
|
||||
href.concat("&group_id=#{group_id}") if group_id
|
||||
href.concat("&nav_source=navbar")
|
||||
|
||||
".dropdown a[href='#{href}']"
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
const useMockLocation = (fn) => {
|
||||
const origWindowLocation = window.location;
|
||||
let currentWindowLocation;
|
||||
let currentWindowLocation = origWindowLocation;
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
get: () => currentWindowLocation,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import setWindowLocation from './set_window_location_helper';
|
||||
import { TEST_HOST } from './test_constants';
|
||||
|
||||
describe('helpers/set_window_location_helper', () => {
|
||||
const originalLocation = window.location.href;
|
||||
|
@ -103,4 +104,30 @@ describe('helpers/set_window_location_helper', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
// This set of tests relies on Jest executing tests in source order, which is
|
||||
// at the time of writing the only order they will execute, by design.
|
||||
// See https://github.com/facebook/jest/issues/4386 for more details.
|
||||
describe('window.location resetting by global beforeEach', () => {
|
||||
const overridden = 'https://gdk.test:1234/';
|
||||
const initial = `${TEST_HOST}/`;
|
||||
|
||||
it('works before an override', () => {
|
||||
expect(window.location.href).toBe(initial);
|
||||
});
|
||||
|
||||
describe('overriding', () => {
|
||||
beforeEach(() => {
|
||||
setWindowLocation(overridden);
|
||||
});
|
||||
|
||||
it('works', () => {
|
||||
expect(window.location.href).toBe(overridden);
|
||||
});
|
||||
});
|
||||
|
||||
it('works after an override', () => {
|
||||
expect(window.location.href).toBe(initial);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -259,6 +259,8 @@ describe('diffs/components/app', () => {
|
|||
});
|
||||
|
||||
it('marks current diff file based on currently highlighted row', async () => {
|
||||
window.location.hash = 'ABC_123';
|
||||
|
||||
createComponent({
|
||||
shouldShow: true,
|
||||
});
|
||||
|
@ -429,9 +431,8 @@ describe('diffs/components/app', () => {
|
|||
jest.spyOn(wrapper.vm, 'refetchDiffData').mockImplementation(() => {});
|
||||
jest.spyOn(wrapper.vm, 'adjustView').mockImplementation(() => {});
|
||||
};
|
||||
const location = window.location.href;
|
||||
|
||||
beforeAll(() => {
|
||||
beforeEach(() => {
|
||||
setWindowLocation(COMMIT_URL);
|
||||
document.title = 'My Title';
|
||||
});
|
||||
|
@ -440,10 +441,6 @@ describe('diffs/components/app', () => {
|
|||
jest.spyOn(urlUtils, 'updateHistory');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
setWindowLocation(location);
|
||||
});
|
||||
|
||||
it('when the commit changes and the app is not loading it should update the history, refetch the diff data, and update the view', async () => {
|
||||
createComponent({}, ({ state }) => {
|
||||
state.diffs.commit = { ...state.diffs.commit, id: 'OLD' };
|
||||
|
|
|
@ -448,7 +448,7 @@ describe('monitoring/utils', () => {
|
|||
input | urlParams
|
||||
${[]} | ${''}
|
||||
${[{ name: 'env', value: 'prod' }]} | ${'?var-env=prod'}
|
||||
${[{ name: 'env1', value: 'prod' }, { name: 'env2', value: null }]} | ${'?var-env=prod&var-env1=prod'}
|
||||
${[{ name: 'env1', value: 'prod' }, { name: 'env2', value: null }]} | ${'?var-env1=prod'}
|
||||
`(
|
||||
'setCustomVariablesFromUrl updates history with query "$urlParams" with input $input',
|
||||
({ input, urlParams }) => {
|
||||
|
|
|
@ -35,9 +35,19 @@ exports[`PackageTitle renders with tags 1`] = `
|
|||
size="16"
|
||||
/>
|
||||
|
||||
<gl-sprintf-stub
|
||||
message="v%{version} published %{timeAgo}"
|
||||
/>
|
||||
<span
|
||||
data-testid="sub-header"
|
||||
>
|
||||
v
|
||||
1.0.0
|
||||
published
|
||||
<time-ago-tooltip-stub
|
||||
class="gl-ml-2"
|
||||
cssclass=""
|
||||
time="2020-08-17T14:23:32Z"
|
||||
tooltipplacement="top"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -123,9 +133,19 @@ exports[`PackageTitle renders without tags 1`] = `
|
|||
size="16"
|
||||
/>
|
||||
|
||||
<gl-sprintf-stub
|
||||
message="v%{version} published %{timeAgo}"
|
||||
/>
|
||||
<span
|
||||
data-testid="sub-header"
|
||||
>
|
||||
v
|
||||
1.0.0
|
||||
published
|
||||
<time-ago-tooltip-stub
|
||||
class="gl-ml-2"
|
||||
cssclass=""
|
||||
time="2020-08-17T14:23:32Z"
|
||||
tooltipplacement="top"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { GlIcon, GlSprintf } from '@gitlab/ui';
|
||||
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import PackageTags from '~/packages/shared/components/package_tags.vue';
|
||||
|
@ -9,6 +10,7 @@ import {
|
|||
PACKAGE_TYPE_NUGET,
|
||||
} from '~/packages_and_registries/package_registry/constants';
|
||||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
import { packageData, packageFiles, packageTags, packagePipelines } from '../../mock_data';
|
||||
|
||||
|
@ -26,6 +28,7 @@ describe('PackageTitle', () => {
|
|||
propsData: { packageEntity },
|
||||
stubs: {
|
||||
TitleArea,
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
return wrapper.vm.$nextTick();
|
||||
|
@ -38,6 +41,9 @@ describe('PackageTitle', () => {
|
|||
const findPackageRef = () => wrapper.findByTestId('package-ref');
|
||||
const findPackageTags = () => wrapper.findComponent(PackageTags);
|
||||
const findPackageBadges = () => wrapper.findAllByTestId('tag-badge');
|
||||
const findSubHeaderIcon = () => wrapper.findComponent(GlIcon);
|
||||
const findSubHeaderText = () => wrapper.findByTestId('sub-header');
|
||||
const findSubHeaderTimeAgo = () => wrapper.findComponent(TimeAgoTooltip);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -59,6 +65,7 @@ describe('PackageTitle', () => {
|
|||
it('with tags on mobile', async () => {
|
||||
jest.spyOn(GlBreakpointInstance, 'isDesktop').mockReturnValue(false);
|
||||
await createComponent();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findPackageBadges()).toHaveLength(packageTags().length);
|
||||
|
@ -93,6 +100,25 @@ describe('PackageTitle', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('sub-header', () => {
|
||||
it('has the eye icon', async () => {
|
||||
await createComponent();
|
||||
|
||||
expect(findSubHeaderIcon().props('name')).toBe('eye');
|
||||
});
|
||||
|
||||
it('has a text showing version', async () => {
|
||||
await createComponent();
|
||||
|
||||
expect(findSubHeaderText().text()).toMatchInterpolatedText('v 1.0.0 published');
|
||||
});
|
||||
|
||||
it('has a time ago tooltip component', async () => {
|
||||
await createComponent();
|
||||
expect(findSubHeaderTimeAgo().props('time')).toBe(packageWithTags.createdAt);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
packageType | text
|
||||
${PACKAGE_TYPE_CONAN} | ${'Conan'}
|
||||
|
|
|
@ -86,19 +86,22 @@ export const STALE_STORED_DATA = [
|
|||
|
||||
export const MOCK_FRESH_DATA_RES = { name: 'fresh' };
|
||||
|
||||
export const PROMISE_ALL_EXPECTED_MUTATIONS = {
|
||||
initGroups: {
|
||||
export const PRELOAD_EXPECTED_MUTATIONS = [
|
||||
{
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
|
||||
},
|
||||
{
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
|
||||
},
|
||||
];
|
||||
|
||||
export const PROMISE_ALL_EXPECTED_MUTATIONS = {
|
||||
resGroups: {
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] },
|
||||
},
|
||||
initProjects: {
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
|
||||
},
|
||||
resProjects: {
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] },
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
MOCK_GROUP,
|
||||
FRESH_STORED_DATA,
|
||||
MOCK_FRESH_DATA_RES,
|
||||
PRELOAD_EXPECTED_MUTATIONS,
|
||||
PROMISE_ALL_EXPECTED_MUTATIONS,
|
||||
} from '../mock_data';
|
||||
|
||||
|
@ -68,31 +69,31 @@ describe('Global Search Store Actions', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
action | axiosMock | type | expectedMutations | flashCallCount | lsKey
|
||||
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initGroups, PROMISE_ALL_EXPECTED_MUTATIONS.resGroups]} | ${0} | ${GROUPS_LOCAL_STORAGE_KEY}
|
||||
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initGroups]} | ${1} | ${GROUPS_LOCAL_STORAGE_KEY}
|
||||
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initProjects, PROMISE_ALL_EXPECTED_MUTATIONS.resProjects]} | ${0} | ${PROJECTS_LOCAL_STORAGE_KEY}
|
||||
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.initProjects]} | ${1} | ${PROJECTS_LOCAL_STORAGE_KEY}
|
||||
`(
|
||||
'Promise.all calls',
|
||||
({ action, axiosMock, type, expectedMutations, flashCallCount, lsKey }) => {
|
||||
describe(action.name, () => {
|
||||
describe(`on ${type}`, () => {
|
||||
beforeEach(() => {
|
||||
storeUtils.loadDataFromLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
|
||||
mock[axiosMock.method]().reply(axiosMock.code, MOCK_FRESH_DATA_RES);
|
||||
});
|
||||
action | axiosMock | type | expectedMutations | flashCallCount
|
||||
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.resGroups]} | ${0}
|
||||
${actions.loadFrequentGroups} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[]} | ${1}
|
||||
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${[PROMISE_ALL_EXPECTED_MUTATIONS.resProjects]} | ${0}
|
||||
${actions.loadFrequentProjects} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${[]} | ${1}
|
||||
`('Promise.all calls', ({ action, axiosMock, type, expectedMutations, flashCallCount }) => {
|
||||
describe(action.name, () => {
|
||||
describe(`on ${type}`, () => {
|
||||
beforeEach(() => {
|
||||
state.frequentItems = {
|
||||
[GROUPS_LOCAL_STORAGE_KEY]: FRESH_STORED_DATA,
|
||||
[PROJECTS_LOCAL_STORAGE_KEY]: FRESH_STORED_DATA,
|
||||
};
|
||||
|
||||
it(`should dispatch the correct mutations`, () => {
|
||||
return testAction({ action, state, expectedMutations }).then(() => {
|
||||
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(lsKey);
|
||||
flashCallback(flashCallCount);
|
||||
});
|
||||
mock[axiosMock.method]().reply(axiosMock.code, MOCK_FRESH_DATA_RES);
|
||||
});
|
||||
|
||||
it(`should dispatch the correct mutations`, () => {
|
||||
return testAction({ action, state, expectedMutations }).then(() => {
|
||||
flashCallback(flashCallCount);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroupsData', () => {
|
||||
const mockCommit = () => {};
|
||||
|
@ -182,14 +183,38 @@ describe('Global Search Store Actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setFrequentGroup', () => {
|
||||
describe('preloadStoredFrequentItems', () => {
|
||||
beforeEach(() => {
|
||||
storeUtils.setFrequentItemToLS = jest.fn();
|
||||
storeUtils.loadDataFromLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
|
||||
});
|
||||
|
||||
it(`calls setFrequentItemToLS with ${GROUPS_LOCAL_STORAGE_KEY} and item data`, async () => {
|
||||
it('calls preloadStoredFrequentItems for both groups and projects and commits LOAD_FREQUENT_ITEMS', async () => {
|
||||
await testAction({
|
||||
action: actions.preloadStoredFrequentItems,
|
||||
state,
|
||||
expectedMutations: PRELOAD_EXPECTED_MUTATIONS,
|
||||
});
|
||||
|
||||
expect(storeUtils.loadDataFromLS).toHaveBeenCalledTimes(2);
|
||||
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(GROUPS_LOCAL_STORAGE_KEY);
|
||||
expect(storeUtils.loadDataFromLS).toHaveBeenCalledWith(PROJECTS_LOCAL_STORAGE_KEY);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFrequentGroup', () => {
|
||||
beforeEach(() => {
|
||||
storeUtils.setFrequentItemToLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
|
||||
});
|
||||
|
||||
it(`calls setFrequentItemToLS with ${GROUPS_LOCAL_STORAGE_KEY} and item data then commits LOAD_FREQUENT_ITEMS`, async () => {
|
||||
await testAction({
|
||||
action: actions.setFrequentGroup,
|
||||
expectedMutations: [
|
||||
{
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: GROUPS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
|
||||
},
|
||||
],
|
||||
payload: MOCK_GROUP,
|
||||
state,
|
||||
});
|
||||
|
@ -204,12 +229,18 @@ describe('Global Search Store Actions', () => {
|
|||
|
||||
describe('setFrequentProject', () => {
|
||||
beforeEach(() => {
|
||||
storeUtils.setFrequentItemToLS = jest.fn();
|
||||
storeUtils.setFrequentItemToLS = jest.fn().mockReturnValue(FRESH_STORED_DATA);
|
||||
});
|
||||
|
||||
it(`calls setFrequentItemToLS with ${PROJECTS_LOCAL_STORAGE_KEY} and item data`, async () => {
|
||||
await testAction({
|
||||
action: actions.setFrequentProject,
|
||||
expectedMutations: [
|
||||
{
|
||||
type: types.LOAD_FREQUENT_ITEMS,
|
||||
payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: FRESH_STORED_DATA },
|
||||
},
|
||||
],
|
||||
payload: MOCK_PROJECT,
|
||||
state,
|
||||
});
|
||||
|
|
|
@ -51,19 +51,25 @@ describe('Global Search Store Utils', () => {
|
|||
|
||||
describe('setFrequentItemToLS', () => {
|
||||
const frequentItems = {};
|
||||
let res;
|
||||
|
||||
describe('with existing data', () => {
|
||||
describe(`when frequency is less than ${MAX_FREQUENCY}`, () => {
|
||||
beforeEach(() => {
|
||||
frequentItems[MOCK_LS_KEY] = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: PREV_TIME }];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
});
|
||||
|
||||
it('adds 1 to the frequency, tracks lastUsed, and calls localStorage.setItem', () => {
|
||||
it('adds 1 to the frequency, tracks lastUsed, calls localStorage.setItem and returns the array', () => {
|
||||
const updatedFrequentItems = [
|
||||
{ ...MOCK_GROUPS[0], frequency: 2, lastUsed: CURRENT_TIME },
|
||||
];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 2, lastUsed: CURRENT_TIME }]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -72,16 +78,19 @@ describe('Global Search Store Utils', () => {
|
|||
frequentItems[MOCK_LS_KEY] = [
|
||||
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: PREV_TIME },
|
||||
];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
});
|
||||
|
||||
it(`does not further increase frequency past ${MAX_FREQUENCY}, tracks lastUsed, and calls localStorage.setItem`, () => {
|
||||
it(`does not further increase frequency past ${MAX_FREQUENCY}, tracks lastUsed, calls localStorage.setItem, and returns the array`, () => {
|
||||
const updatedFrequentItems = [
|
||||
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: CURRENT_TIME },
|
||||
];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([
|
||||
{ ...MOCK_GROUPS[0], frequency: MAX_FREQUENCY, lastUsed: CURRENT_TIME },
|
||||
]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -89,14 +98,17 @@ describe('Global Search Store Utils', () => {
|
|||
describe('with no existing data', () => {
|
||||
beforeEach(() => {
|
||||
frequentItems[MOCK_LS_KEY] = [];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
});
|
||||
|
||||
it('adds a new entry with frequency 1, tracks lastUsed, and calls localStorage.setItem', () => {
|
||||
it('adds a new entry with frequency 1, tracks lastUsed, calls localStorage.setItem, and returns the array', () => {
|
||||
const updatedFrequentItems = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -107,18 +119,21 @@ describe('Global Search Store Utils', () => {
|
|||
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
|
||||
{ id: 3, frequency: 1, lastUsed: PREV_TIME },
|
||||
];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 3 });
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 3 });
|
||||
});
|
||||
|
||||
it('sorts the array by most frequent and lastUsed', () => {
|
||||
it('sorts the array by most frequent and lastUsed and returns the array', () => {
|
||||
const updatedFrequentItems = [
|
||||
{ id: 3, frequency: 2, lastUsed: CURRENT_TIME },
|
||||
{ id: 1, frequency: 2, lastUsed: PREV_TIME },
|
||||
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
|
||||
];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([
|
||||
{ id: 3, frequency: 2, lastUsed: CURRENT_TIME },
|
||||
{ id: 1, frequency: 2, lastUsed: PREV_TIME },
|
||||
{ id: 2, frequency: 1, lastUsed: PREV_TIME },
|
||||
]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -131,31 +146,35 @@ describe('Global Search Store Utils', () => {
|
|||
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
|
||||
{ id: 5, frequency: 1, lastUsed: PREV_TIME },
|
||||
];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 6 });
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, { id: 6 });
|
||||
});
|
||||
|
||||
it('removes the last item in the array', () => {
|
||||
it('removes the last item in the array and returns the array', () => {
|
||||
const updatedFrequentItems = [
|
||||
{ id: 1, frequency: 5, lastUsed: PREV_TIME },
|
||||
{ id: 2, frequency: 4, lastUsed: PREV_TIME },
|
||||
{ id: 3, frequency: 3, lastUsed: PREV_TIME },
|
||||
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
|
||||
{ id: 6, frequency: 1, lastUsed: CURRENT_TIME },
|
||||
];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([
|
||||
{ id: 1, frequency: 5, lastUsed: PREV_TIME },
|
||||
{ id: 2, frequency: 4, lastUsed: PREV_TIME },
|
||||
{ id: 3, frequency: 3, lastUsed: PREV_TIME },
|
||||
{ id: 4, frequency: 2, lastUsed: PREV_TIME },
|
||||
{ id: 6, frequency: 1, lastUsed: CURRENT_TIME },
|
||||
]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with null data loaded in', () => {
|
||||
beforeEach(() => {
|
||||
frequentItems[MOCK_LS_KEY] = null;
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_GROUPS[0]);
|
||||
});
|
||||
|
||||
it('wipes local storage', () => {
|
||||
it('wipes local storage and returns empty array', () => {
|
||||
expect(localStorage.removeItem).toHaveBeenCalledWith(MOCK_LS_KEY);
|
||||
expect(res).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -163,14 +182,17 @@ describe('Global Search Store Utils', () => {
|
|||
beforeEach(() => {
|
||||
const MOCK_ADDITIONAL_DATA_GROUP = { ...MOCK_GROUPS[0], extraData: 'test' };
|
||||
frequentItems[MOCK_LS_KEY] = [];
|
||||
setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_ADDITIONAL_DATA_GROUP);
|
||||
res = setFrequentItemToLS(MOCK_LS_KEY, frequentItems, MOCK_ADDITIONAL_DATA_GROUP);
|
||||
});
|
||||
|
||||
it('parses out extra data for LS', () => {
|
||||
it('parses out extra data for LS and returns the array', () => {
|
||||
const updatedFrequentItems = [{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }];
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(
|
||||
MOCK_LS_KEY,
|
||||
JSON.stringify([{ ...MOCK_GROUPS[0], frequency: 1, lastUsed: CURRENT_TIME }]),
|
||||
JSON.stringify(updatedFrequentItems),
|
||||
);
|
||||
expect(res).toEqual(updatedFrequentItems);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { MOCK_QUERY } from 'jest/search/mock_data';
|
||||
import GlobalSearchTopbar from '~/search/topbar/components/app.vue';
|
||||
import GroupFilter from '~/search/topbar/components/group_filter.vue';
|
||||
import ProjectFilter from '~/search/topbar/components/project_filter.vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('GlobalSearchTopbar', () => {
|
||||
let wrapper;
|
||||
|
@ -15,6 +15,7 @@ describe('GlobalSearchTopbar', () => {
|
|||
const actionSpies = {
|
||||
applyQuery: jest.fn(),
|
||||
setQuery: jest.fn(),
|
||||
preloadStoredFrequentItems: jest.fn(),
|
||||
};
|
||||
|
||||
const createComponent = (initialState) => {
|
||||
|
@ -27,14 +28,12 @@ describe('GlobalSearchTopbar', () => {
|
|||
});
|
||||
|
||||
wrapper = shallowMount(GlobalSearchTopbar, {
|
||||
localVue,
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findTopbarForm = () => wrapper.find(GlForm);
|
||||
|
@ -110,4 +109,14 @@ describe('GlobalSearchTopbar', () => {
|
|||
expect(actionSpies.applyQuery).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCreate', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('calls preloadStoredFrequentItems', () => {
|
||||
expect(actionSpies.preloadStoredFrequentItems).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,6 @@ describe('GroupFilter', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
|
||||
|
@ -89,10 +88,11 @@ describe('GroupFilter', () => {
|
|||
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
|
||||
});
|
||||
|
||||
it('calls setUrlParams with group null, project id null, and then calls visitUrl', () => {
|
||||
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,
|
||||
nav_source: null,
|
||||
});
|
||||
|
||||
expect(visitUrl).toHaveBeenCalled();
|
||||
|
@ -108,10 +108,11 @@ describe('GroupFilter', () => {
|
|||
findSearchableDropdown().vm.$emit('change', MOCK_GROUP);
|
||||
});
|
||||
|
||||
it('calls setUrlParams with group id, project id null, and then calls visitUrl', () => {
|
||||
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,
|
||||
nav_source: null,
|
||||
});
|
||||
|
||||
expect(visitUrl).toHaveBeenCalled();
|
||||
|
@ -156,4 +157,31 @@ describe('GroupFilter', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
navSource | initialData | callMethod
|
||||
${null} | ${null} | ${false}
|
||||
${null} | ${MOCK_GROUP} | ${false}
|
||||
${'navbar'} | ${null} | ${false}
|
||||
${'navbar'} | ${MOCK_GROUP} | ${true}
|
||||
`('onCreate', ({ navSource, initialData, callMethod }) => {
|
||||
describe(`when nav_source is ${navSource} and ${
|
||||
initialData ? 'has' : 'does not have'
|
||||
} an initial group`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData });
|
||||
});
|
||||
|
||||
it(`${callMethod ? 'does' : 'does not'} call setFrequentGroup`, () => {
|
||||
if (callMethod) {
|
||||
expect(actionSpies.setFrequentGroup).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
initialData,
|
||||
);
|
||||
} else {
|
||||
expect(actionSpies.setFrequentGroup).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,6 @@ describe('ProjectFilter', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
|
||||
|
@ -89,9 +88,10 @@ describe('ProjectFilter', () => {
|
|||
findSearchableDropdown().vm.$emit('change', ANY_OPTION);
|
||||
});
|
||||
|
||||
it('calls setUrlParams with null, no group id, then calls visitUrl', () => {
|
||||
it('calls setUrlParams with null, no group id, nav_source null, then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
[PROJECT_DATA.queryParam]: null,
|
||||
nav_source: null,
|
||||
});
|
||||
expect(visitUrl).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -106,10 +106,11 @@ describe('ProjectFilter', () => {
|
|||
findSearchableDropdown().vm.$emit('change', MOCK_PROJECT);
|
||||
});
|
||||
|
||||
it('calls setUrlParams with project id, group id, then calls visitUrl', () => {
|
||||
it('calls setUrlParams with project id, group id, nav_source null, then calls visitUrl', () => {
|
||||
expect(setUrlParams).toHaveBeenCalledWith({
|
||||
[GROUP_DATA.queryParam]: MOCK_PROJECT.namespace.id,
|
||||
[PROJECT_DATA.queryParam]: MOCK_PROJECT.id,
|
||||
nav_source: null,
|
||||
});
|
||||
expect(visitUrl).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -157,4 +158,31 @@ describe('ProjectFilter', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
navSource | initialData | callMethod
|
||||
${null} | ${null} | ${false}
|
||||
${null} | ${MOCK_PROJECT} | ${false}
|
||||
${'navbar'} | ${null} | ${false}
|
||||
${'navbar'} | ${MOCK_PROJECT} | ${true}
|
||||
`('onCreate', ({ navSource, initialData, callMethod }) => {
|
||||
describe(`when nav_source is ${navSource} and ${
|
||||
initialData ? 'has' : 'does not have'
|
||||
} an initial project`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ query: { ...MOCK_QUERY, nav_source: navSource } }, { initialData });
|
||||
});
|
||||
|
||||
it(`${callMethod ? 'does' : 'does not'} call setFrequentProject`, () => {
|
||||
if (callMethod) {
|
||||
expect(actionSpies.setFrequentProject).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
initialData,
|
||||
);
|
||||
} else {
|
||||
expect(actionSpies.setFrequentProject).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,8 @@ import * as jqueryMatchers from 'custom-jquery-matchers';
|
|||
import Vue from 'vue';
|
||||
import 'jquery';
|
||||
import { setGlobalDateToFakeDate } from 'helpers/fake_date';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import { getJSONFixture, loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures';
|
||||
import { initializeTestTimeout } from './__helpers__/timeout';
|
||||
|
@ -88,8 +90,13 @@ Object.assign(global, {
|
|||
},
|
||||
});
|
||||
|
||||
// make sure that each test actually tests something
|
||||
// see https://jestjs.io/docs/en/expect#expecthasassertions
|
||||
beforeEach(() => {
|
||||
// make sure that each test actually tests something
|
||||
// see https://jestjs.io/docs/en/expect#expecthasassertions
|
||||
expect.hasAssertions();
|
||||
|
||||
// Reset the mocked window.location. This ensures tests don't interfere with
|
||||
// each other, and removes the need to tidy up if it was changed for a given
|
||||
// test.
|
||||
setWindowLocation(TEST_HOST);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { historyPushState } from '~/lib/utils/common_utils';
|
||||
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
import UrlSyncComponent from '~/vue_shared/components/url_sync.vue';
|
||||
|
@ -15,9 +14,6 @@ jest.mock('~/lib/utils/common_utils', () => ({
|
|||
describe('url sync component', () => {
|
||||
let wrapper;
|
||||
const mockQuery = { group_id: '5014437163714', project_ids: ['5014437608314'] };
|
||||
const TEST_HOST = 'http://testhost/';
|
||||
|
||||
setWindowLocation(TEST_HOST);
|
||||
|
||||
const findButton = () => wrapper.find('button');
|
||||
|
||||
|
@ -35,7 +31,9 @@ describe('url sync component', () => {
|
|||
|
||||
const expectUrlSync = (query, times, mergeUrlParamsReturnValue) => {
|
||||
expect(mergeUrlParams).toHaveBeenCalledTimes(times);
|
||||
expect(mergeUrlParams).toHaveBeenCalledWith(query, TEST_HOST, { spreadArrays: true });
|
||||
expect(mergeUrlParams).toHaveBeenCalledWith(query, window.location.href, {
|
||||
spreadArrays: true,
|
||||
});
|
||||
|
||||
expect(historyPushState).toHaveBeenCalledTimes(times);
|
||||
expect(historyPushState).toHaveBeenCalledWith(mergeUrlParamsReturnValue);
|
||||
|
|
|
@ -496,18 +496,6 @@ RSpec.describe Gitlab::Auth::AuthFinders do
|
|||
expect(find_user_from_web_access_token(:archive)).to eq(user)
|
||||
end
|
||||
|
||||
context 'when allow_archive_as_web_access_format feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(allow_archive_as_web_access_format: false)
|
||||
end
|
||||
|
||||
it 'returns nil for ARCHIVE requests' do
|
||||
set_header('SCRIPT_NAME', '/-/archive/main.zip')
|
||||
|
||||
expect(find_user_from_web_access_token(:archive)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'for API requests' do
|
||||
it 'returns the user' do
|
||||
set_header('SCRIPT_NAME', '/api/endpoint')
|
||||
|
|
|
@ -66,8 +66,34 @@ RSpec.describe Gitlab::Kas do
|
|||
end
|
||||
|
||||
describe '.tunnel_url' do
|
||||
it 'returns gitlab_kas external_url with proxy path appended' do
|
||||
expect(described_class.tunnel_url).to eq(Gitlab.config.gitlab_kas.external_url + '/k8s-proxy')
|
||||
before do
|
||||
stub_config(gitlab_kas: { external_url: external_url })
|
||||
end
|
||||
|
||||
subject { described_class.tunnel_url }
|
||||
|
||||
context 'external_url uses wss://' do
|
||||
let(:external_url) { 'wss://kas.gitlab.example.com' }
|
||||
|
||||
it { is_expected.to eq('https://kas.gitlab.example.com/k8s-proxy') }
|
||||
end
|
||||
|
||||
context 'external_url uses ws://' do
|
||||
let(:external_url) { 'ws://kas.gitlab.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://kas.gitlab.example.com/k8s-proxy') }
|
||||
end
|
||||
|
||||
context 'external_url uses grpcs://' do
|
||||
let(:external_url) { 'grpcs://kas.gitlab.example.com' }
|
||||
|
||||
it { is_expected.to eq('https://kas.gitlab.example.com/k8s-proxy') }
|
||||
end
|
||||
|
||||
context 'external_url uses grpc://' do
|
||||
let(:external_url) { 'grpc://kas.gitlab.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://kas.gitlab.example.com/k8s-proxy') }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue