Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
fbf183eebe
commit
87f8fdb93c
|
|
@ -18,13 +18,13 @@ export const HTTP_STATUS_BAD_REQUEST = 400;
|
|||
export const HTTP_STATUS_UNAUTHORIZED = 401;
|
||||
export const HTTP_STATUS_FORBIDDEN = 403;
|
||||
export const HTTP_STATUS_NOT_FOUND = 404;
|
||||
export const HTTP_STATUS_INTERNAL_SERVER_ERROR = 500;
|
||||
export const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;
|
||||
|
||||
// TODO move the rest of the status codes to primitive constants
|
||||
// https://docs.gitlab.com/ee/development/fe_guide/style/javascript.html#export-constants-as-primitives
|
||||
const httpStatusCodes = {
|
||||
OK: 200,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
SERVICE_UNAVAILABLE: 503,
|
||||
};
|
||||
|
||||
export const successCodes = [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<script>
|
||||
import { GlTable, GlLink, GlPagination } from '@gitlab/ui';
|
||||
import { GlTable, GlLink, GlPagination, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { getParameterValues, setUrlParams } from '~/lib/utils/url_utility';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import IncubationAlert from './incubation_alert.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -9,9 +10,13 @@ export default {
|
|||
components: {
|
||||
GlTable,
|
||||
GlLink,
|
||||
TimeAgo,
|
||||
IncubationAlert,
|
||||
GlPagination,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
inject: ['candidates', 'metricNames', 'paramNames', 'pagination'],
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -21,6 +26,9 @@ export default {
|
|||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
{ key: 'name', label: this.$options.i18n.nameLabel },
|
||||
{ key: 'created_at', label: this.$options.i18n.createdAtLabel },
|
||||
{ key: 'user', label: this.$options.i18n.userLabel },
|
||||
...this.paramNames,
|
||||
...this.metricNames,
|
||||
{ key: 'details', label: '' },
|
||||
|
|
@ -47,6 +55,10 @@ export default {
|
|||
emptyStateLabel: __('This experiment has no logged candidates'),
|
||||
artifactsLabel: __('Artifacts'),
|
||||
detailsLabel: __('Details'),
|
||||
userLabel: __('User'),
|
||||
createdAtLabel: __('Created at'),
|
||||
nameLabel: __('Name'),
|
||||
noDataContent: __('-'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -64,17 +76,46 @@ export default {
|
|||
:items="candidates"
|
||||
:empty-text="$options.i18n.emptyStateLabel"
|
||||
show-empty
|
||||
class="gl-mt-0!"
|
||||
small
|
||||
class="gl-mt-0! ml-candidate-table"
|
||||
>
|
||||
<template #cell()="data">
|
||||
<div v-gl-tooltip.hover :title="data.value">{{ data.value }}</div>
|
||||
</template>
|
||||
|
||||
<template #cell(artifact)="data">
|
||||
<gl-link v-if="data.value" :href="data.value" target="_blank">{{
|
||||
$options.i18n.artifactsLabel
|
||||
}}</gl-link>
|
||||
<gl-link
|
||||
v-if="data.value"
|
||||
v-gl-tooltip.hover
|
||||
:href="data.value"
|
||||
target="_blank"
|
||||
:title="$options.i18n.artifactsLabel"
|
||||
>{{ $options.i18n.artifactsLabel }}</gl-link
|
||||
>
|
||||
<div v-else v-gl-tooltip.hover :title="$options.i18n.artifactsLabel">
|
||||
{{ $options.i18n.noDataContent }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #cell(details)="data">
|
||||
<gl-link :href="data.value">{{ $options.i18n.detailsLabel }}</gl-link>
|
||||
<gl-link v-gl-tooltip.hover :href="data.value" :title="$options.i18n.detailsLabel">{{
|
||||
$options.i18n.detailsLabel
|
||||
}}</gl-link>
|
||||
</template>
|
||||
|
||||
<template #cell(created_at)="data">
|
||||
<time-ago v-gl-tooltip.hover :time="data.value" :title="data.value" />
|
||||
</template>
|
||||
|
||||
<template #cell(user)="data">
|
||||
<gl-link
|
||||
v-if="data.value"
|
||||
v-gl-tooltip.hover
|
||||
:href="data.value.path"
|
||||
:title="data.value.username"
|
||||
>@{{ data.value.username }}</gl-link
|
||||
>
|
||||
<div v-else>{{ $options.i18n.noDataContent }}</div>
|
||||
</template>
|
||||
</gl-table>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { backOff } from '~/lib/utils/common_utils';
|
||||
import statusCodes, {
|
||||
import {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { PROMETHEUS_TIMEOUT } from '../constants';
|
||||
|
|
@ -39,7 +40,7 @@ export const getPrometheusQueryData = (prometheusEndpoint, params, opts) =>
|
|||
if (
|
||||
response.status === HTTP_STATUS_BAD_REQUEST ||
|
||||
response.status === HTTP_STATUS_UNPROCESSABLE_ENTITY ||
|
||||
response.status === statusCodes.SERVICE_UNAVAILABLE
|
||||
response.status === HTTP_STATUS_SERVICE_UNAVAILABLE
|
||||
) {
|
||||
const { data } = response;
|
||||
if (data?.status === 'error' && data?.error) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { pick } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { BACKOFF_TIMEOUT } from '~/lib/utils/common_utils';
|
||||
import httpStatusCodes, { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_SERVICE_UNAVAILABLE } from '~/lib/utils/http_status';
|
||||
import { dashboardEmptyStates, endpointKeys, initialStateKeys, metricStates } from '../constants';
|
||||
import * as types from './mutation_types';
|
||||
import { mapToDashboardViewModel, mapPanelToViewModel, normalizeQueryResponseData } from './utils';
|
||||
|
|
@ -43,7 +43,7 @@ const emptyStateFromError = (error) => {
|
|||
|
||||
// Axios error responses
|
||||
const { response } = error;
|
||||
if (response && response.status === httpStatusCodes.SERVICE_UNAVAILABLE) {
|
||||
if (response && response.status === HTTP_STATUS_SERVICE_UNAVAILABLE) {
|
||||
return metricStates.CONNECTION_FAILED;
|
||||
} else if (response && response.status === HTTP_STATUS_BAD_REQUEST) {
|
||||
// Note: "error.response.data.error" may contain Prometheus error information
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export default {
|
|||
:action-cancel="$options.modal.cancelAction"
|
||||
:title="$options.i18n.DELETE_PACKAGES_MODAL_TITLE"
|
||||
@primary="$emit('confirm')"
|
||||
@cancel="$emit('cancel')"
|
||||
>
|
||||
<span>{{ description }}</span>
|
||||
</gl-modal>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@ import DeletePackageModal from '~/packages_and_registries/shared/components/dele
|
|||
import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue';
|
||||
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
|
||||
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import {
|
||||
DELETE_PACKAGE_TRACKING_ACTION,
|
||||
DELETE_PACKAGES_TRACKING_ACTION,
|
||||
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
REQUEST_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
CANCEL_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
PACKAGE_ERROR_STATUS,
|
||||
} from '~/packages_and_registries/package_registry/constants';
|
||||
import { packageTypeToTrackCategory } from '~/packages_and_registries/package_registry/utils';
|
||||
|
|
@ -18,6 +22,7 @@ export default {
|
|||
name: 'PackagesList',
|
||||
components: {
|
||||
GlAlert,
|
||||
DeleteModal,
|
||||
DeletePackageModal,
|
||||
PackagesListLoader,
|
||||
PackagesListRow,
|
||||
|
|
@ -44,6 +49,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
itemToBeDeleted: null,
|
||||
itemsToBeDeleted: [],
|
||||
errorPackages: [],
|
||||
};
|
||||
},
|
||||
|
|
@ -92,7 +98,18 @@ export default {
|
|||
this.setItemToBeDeleted(item);
|
||||
return;
|
||||
}
|
||||
this.$emit('delete', items);
|
||||
this.itemsToBeDeleted = items;
|
||||
this.track(REQUEST_DELETE_PACKAGES_TRACKING_ACTION);
|
||||
this.$refs.deletePackagesModal.show();
|
||||
},
|
||||
deleteItemsConfirmation() {
|
||||
this.$emit('delete', this.itemsToBeDeleted);
|
||||
this.track(DELETE_PACKAGES_TRACKING_ACTION);
|
||||
this.itemsToBeDeleted = [];
|
||||
},
|
||||
deleteItemsCanceled() {
|
||||
this.track(CANCEL_DELETE_PACKAGES_TRACKING_ACTION);
|
||||
this.itemsToBeDeleted = [];
|
||||
},
|
||||
deleteItemConfirmation() {
|
||||
this.$emit('package:delete', this.itemToBeDeleted);
|
||||
|
|
@ -159,6 +176,13 @@ export default {
|
|||
@ok="deleteItemConfirmation"
|
||||
@cancel="deleteItemCanceled"
|
||||
/>
|
||||
|
||||
<delete-modal
|
||||
ref="deletePackagesModal"
|
||||
:items-to-be-deleted="itemsToBeDeleted"
|
||||
@confirm="deleteItemsConfirmation"
|
||||
@cancel="deleteItemsCanceled"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ export const FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE = s__(
|
|||
export const FETCH_PACKAGE_METADATA_ERROR_MESSAGE = s__(
|
||||
'PackageRegistry|Something went wrong while fetching the package metadata.',
|
||||
);
|
||||
|
||||
export const DELETE_PACKAGES_TRACKING_ACTION = 'delete_packages';
|
||||
export const REQUEST_DELETE_PACKAGES_TRACKING_ACTION = 'request_delete_packages';
|
||||
export const CANCEL_DELETE_PACKAGES_TRACKING_ACTION = 'cancel_delete_packages';
|
||||
|
||||
export const DELETE_PACKAGES_ERROR_MESSAGE = s__(
|
||||
'PackageRegistry|Something went wrong while deleting packages.',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlAlert, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { createAlert, VARIANT_INFO } from '~/flash';
|
||||
import { createAlert, VARIANT_INFO, VARIANT_SUCCESS, VARIANT_DANGER } from '~/flash';
|
||||
import { historyReplaceState } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
|
||||
|
|
@ -20,7 +20,6 @@ import DeletePackage from '~/packages_and_registries/package_registry/components
|
|||
import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue';
|
||||
import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
|
||||
import PackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -31,14 +30,12 @@ export default {
|
|||
PackageList,
|
||||
PackageTitle,
|
||||
PackageSearch,
|
||||
DeleteModal,
|
||||
DeletePackage,
|
||||
},
|
||||
inject: ['emptyListIllustration', 'isGroupPage', 'fullPath'],
|
||||
data() {
|
||||
return {
|
||||
alertVariables: null,
|
||||
itemsToBeDeleted: [],
|
||||
packages: {},
|
||||
sort: '',
|
||||
filters: {},
|
||||
|
|
@ -117,15 +114,13 @@ export default {
|
|||
historyReplaceState(cleanUrl);
|
||||
}
|
||||
},
|
||||
async confirmDelete() {
|
||||
const { itemsToBeDeleted } = this;
|
||||
this.itemsToBeDeleted = [];
|
||||
async deletePackages(packageEntities) {
|
||||
this.mutationLoading = true;
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: destroyPackagesMutation,
|
||||
variables: {
|
||||
ids: itemsToBeDeleted.map((i) => i.id),
|
||||
ids: packageEntities.map((i) => i.id),
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [
|
||||
|
|
@ -140,22 +135,18 @@ export default {
|
|||
throw new Error(data.destroyPackages.errors[0]);
|
||||
}
|
||||
this.showAlert({
|
||||
variant: 'success',
|
||||
variant: VARIANT_SUCCESS,
|
||||
message: DELETE_PACKAGES_SUCCESS_MESSAGE,
|
||||
});
|
||||
} catch {
|
||||
this.showAlert({
|
||||
variant: 'danger',
|
||||
variant: VARIANT_DANGER,
|
||||
message: DELETE_PACKAGES_ERROR_MESSAGE,
|
||||
});
|
||||
} finally {
|
||||
this.mutationLoading = false;
|
||||
}
|
||||
},
|
||||
showDeletePackagesModal(toBeDeleted) {
|
||||
this.itemsToBeDeleted = toBeDeleted;
|
||||
this.$refs.deletePackagesModal.show();
|
||||
},
|
||||
handleSearchUpdate({ sort, filters }) {
|
||||
this.sort = sort;
|
||||
this.filters = { ...filters };
|
||||
|
|
@ -236,7 +227,7 @@ export default {
|
|||
@prev-page="fetchPreviousPage"
|
||||
@next-page="fetchNextPage"
|
||||
@package:delete="deletePackage"
|
||||
@delete="showDeletePackagesModal"
|
||||
@delete="deletePackages"
|
||||
>
|
||||
<template #empty-state>
|
||||
<gl-empty-state :title="emptyStateTitle" :svg-path="emptyListIllustration">
|
||||
|
|
@ -255,11 +246,5 @@ export default {
|
|||
</package-list>
|
||||
</template>
|
||||
</delete-package>
|
||||
|
||||
<delete-modal
|
||||
ref="deletePackagesModal"
|
||||
:items-to-be-deleted="itemsToBeDeleted"
|
||||
@confirm="confirmDelete"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -79,8 +79,11 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="gl-w-full gl-display-flex gl-align-items-baseline"
|
||||
:class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
|
||||
class="gl-w-full gl-display-flex"
|
||||
:class="{
|
||||
'gl-border-t gl-py-3 gl-pl-7 gl-align-items-baseline': level === 2,
|
||||
'gl-align-items-center': level === 3,
|
||||
}"
|
||||
>
|
||||
<status-icon
|
||||
v-if="statusIconName && !header"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
table.ml-candidate-table {
|
||||
table-layout: fixed;
|
||||
|
||||
tr td,
|
||||
tr th {
|
||||
padding: $gl-padding-8;
|
||||
|
||||
> * {
|
||||
@include gl-display-block;
|
||||
@include gl-text-truncate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.candidate-details {
|
||||
td {
|
||||
padding: $gl-spacing-scale-3;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module Projects
|
|||
page = 1 if page == 0
|
||||
|
||||
@candidates = @experiment.candidates
|
||||
.including_metrics_and_params
|
||||
.including_relationships
|
||||
.page(page)
|
||||
.per(MAX_CANDIDATES_PER_PAGE)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@ module Mutations
|
|||
def resolve(ids:)
|
||||
raise_resource_not_available_error!(TOO_MANY_IDS_ERROR) if ids.size > MAX_PACKAGES
|
||||
|
||||
ids = GitlabSchema.parse_gids(ids, expected_type: ::Packages::Package)
|
||||
.map(&:model_id)
|
||||
model_ids = ids.map(&:model_id)
|
||||
|
||||
service = ::Packages::MarkPackagesForDestructionService.new(
|
||||
packages: packages_from(ids),
|
||||
packages: packages_from(model_ids),
|
||||
current_user: current_user
|
||||
)
|
||||
result = service.execute
|
||||
|
|
|
|||
|
|
@ -65,17 +65,6 @@ module MarkupHelper
|
|||
|
||||
tags = %w(a gl-emoji b strong i em pre code p span)
|
||||
|
||||
if Feature.disabled?(:two_line_mention_enabled, current_user)
|
||||
includes_code = false
|
||||
|
||||
if is_todo
|
||||
fragment = Nokogiri::HTML.fragment(md)
|
||||
includes_code = fragment.css('code').any?
|
||||
|
||||
md = fragment
|
||||
end
|
||||
end
|
||||
|
||||
context = markdown_field_render_context(object, attribute, options)
|
||||
context.reverse_merge!(truncate_visible_max_chars: max_chars || md.length)
|
||||
|
||||
|
|
@ -91,9 +80,6 @@ module MarkupHelper
|
|||
)
|
||||
)
|
||||
|
||||
if is_todo && !includes_code && Feature.disabled?(:two_line_mention_enabled, current_user)
|
||||
text = "<span class=\"gl-relative\">\"</span>#{text}<span class=\"gl-relative\">\"</span>"
|
||||
end
|
||||
# since <img> tags are stripped, this can leave empty <a> tags hanging around
|
||||
# (as our markdown wraps images in links)
|
||||
strip_empty_link_tags(text).html_safe
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ module Projects
|
|||
**candidate.params.to_h { |p| [p.name, p.value] },
|
||||
**candidate.latest_metrics.to_h { |m| [m.name, number_with_precision(m.value, precision: 4)] },
|
||||
artifact: link_to_artifact(candidate),
|
||||
details: link_to_details(candidate)
|
||||
details: link_to_details(candidate),
|
||||
name: candidate.name,
|
||||
created_at: candidate.created_at,
|
||||
user: user_info(candidate)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -58,6 +61,17 @@ module Projects
|
|||
|
||||
project_ml_experiment_path(experiment.project, experiment.iid)
|
||||
end
|
||||
|
||||
def user_info(candidate)
|
||||
user = candidate.user
|
||||
|
||||
return unless user.present?
|
||||
|
||||
{
|
||||
username: user.username,
|
||||
path: user_path(user)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module Ml
|
|||
|
||||
attribute :iid, default: -> { SecureRandom.uuid }
|
||||
|
||||
scope :including_metrics_and_params, -> { includes(:latest_metrics, :params) }
|
||||
scope :including_relationships, -> { includes(:latest_metrics, :params, :user) }
|
||||
|
||||
delegate :project_id, :project, to: :experiment
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
- two_line_mention = Feature.enabled?(:two_line_mention_enabled, current_user)
|
||||
|
||||
%li.todo.gl-hover-border-blue-200.gl-hover-bg-blue-50.gl-hover-cursor-pointer.gl-relative{ class: "todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) }
|
||||
.gl-display-flex.gl-flex-direction-column.gl-sm-flex-direction-row.gl-sm-align-items-center
|
||||
.todo-item.gl-overflow-hidden.gl-overflow-x-auto.gl-align-self-center.gl-w-full{ data: { qa_selector: "todo_item_container" } }
|
||||
|
|
@ -32,20 +30,13 @@
|
|||
= link_to_author(todo, self_added: todo.self_added?)
|
||||
- else
|
||||
= _('(removed)')
|
||||
- if todo.note.present? && two_line_mention
|
||||
- if todo.note.present?
|
||||
\:
|
||||
|
||||
%span.action-name{ data: { qa_selector: "todo_action_name_content" } }<
|
||||
- if two_line_mention
|
||||
- if !todo.note.present?
|
||||
= todo_action_name(todo)
|
||||
- unless todo.self_assigned?
|
||||
\.
|
||||
- else
|
||||
- if !todo.note.present?
|
||||
= todo_action_name(todo)
|
||||
- if todo.note.present?
|
||||
\:
|
||||
- unless todo.note.present? || todo.self_assigned?
|
||||
- unless todo.self_assigned?
|
||||
\.
|
||||
|
||||
- if todo.self_assigned?
|
||||
|
|
@ -53,9 +44,8 @@
|
|||
= todo_self_addressing(todo)
|
||||
\.
|
||||
- if todo.note.present?
|
||||
%span.action-description{ :class => ("gl-font-style-italic" if !two_line_mention) }<
|
||||
- max_chars = two_line_mention ? 125 : 100
|
||||
= first_line_in_markdown(todo, :body, max_chars, is_todo: true, project: todo.project, group: todo.group)
|
||||
%span.action-description<
|
||||
= first_line_in_markdown(todo, :body, 125, is_todo: true, project: todo.project, group: todo.group)
|
||||
|
||||
.todo-timestamp.gl-white-space-nowrap.gl-sm-ml-3.gl-mt-2.gl-mb-2.gl-sm-my-0.gl-px-2.gl-sm-px-0
|
||||
%span.todo-timestamp.gl-font-sm.gl-text-secondary
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: two_line_mention_enabled
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106689
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385889
|
||||
milestone: '15.7'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: false
|
||||
|
|
@ -14,24 +14,22 @@ features of GitLab.
|
|||
|
||||
To reduce memory use, Puma forks worker processes. Each time a worker is created,
|
||||
it shares memory with the primary process. The worker uses additional memory only
|
||||
when it changes or adds to its memory pages.
|
||||
when it changes or adds to its memory pages. This can lead to Puma workers using
|
||||
more physical memory over time as workers handle additional web requests. The amount of memory
|
||||
used over time depends on the use of GitLab. The more features used by GitLab users,
|
||||
the higher the expected memory use over time.
|
||||
|
||||
Memory use increases over time, but you can use Puma Worker Killer to recover memory.
|
||||
To stop uncontrolled memory growth, the GitLab Rails application runs a supervision thread
|
||||
that automatically restarts workers if they exceed a given resident set size (RSS) threshold
|
||||
for a certain amount of time.
|
||||
|
||||
By default:
|
||||
|
||||
- The [Puma Worker Killer](https://github.com/schneems/puma_worker_killer) restarts a worker if it
|
||||
exceeds a [memory limit](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/cluster/puma_worker_killer_initializer.rb).
|
||||
- Rolling restarts of Puma workers are performed every 12 hours.
|
||||
|
||||
### Change the memory limit setting
|
||||
|
||||
To change the memory limit setting:
|
||||
GitLab sets a default of `1200Mb` for the memory limit. To override the default value,
|
||||
set `per_worker_max_memory_mb` to the new RSS limit in megabytes:
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
puma['per_worker_max_memory_mb'] = 1024
|
||||
puma['per_worker_max_memory_mb'] = 1024 # 1GB
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
|
@ -40,48 +38,40 @@ To change the memory limit setting:
|
|||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
When workers are killed and replaced, capacity to run GitLab is reduced,
|
||||
and CPU is consumed. Set `per_worker_max_memory_mb` to a higher value if the worker killer
|
||||
is replacing workers too often.
|
||||
When workers are restarted, capacity to run GitLab is reduced for a short
|
||||
period of time. Set `per_worker_max_memory_mb` to a higher value if workers are replaced too often.
|
||||
|
||||
Worker count is calculated based on CPU cores. A small GitLab deployment
|
||||
with 4-8 workers may experience performance issues if workers are being restarted
|
||||
too often (once or more per minute).
|
||||
|
||||
A higher value of `1200` or more would be beneficial if the server has free memory.
|
||||
A higher value of `1200` or more could be beneficial if the server has free memory.
|
||||
|
||||
### Monitor worker memory
|
||||
### Monitor worker restarts
|
||||
|
||||
The worker killer checks memory every 20 seconds.
|
||||
GitLab emits log events if workers are restarted due to high memory use.
|
||||
|
||||
To monitor the worker killer, use [the Puma log](../logs/index.md#puma_stdoutlog) `/var/log/gitlab/puma/puma_stdout.log`.
|
||||
For example:
|
||||
The following is an example of one of these log events in `/var/log/gitlab/gitlab-rails/application_json.log`:
|
||||
|
||||
```plaintext
|
||||
PumaWorkerKiller: Out of memory. 4 workers consuming total: 4871.23828125 MB
|
||||
out of max: 4798.08 MB. Sending TERM to pid 26668 consuming 1001.00390625 MB.
|
||||
```json
|
||||
{
|
||||
"severity": "WARN",
|
||||
"time": "2023-01-04T09:45:16.173Z",
|
||||
"correlation_id": null,
|
||||
"pid": 2725,
|
||||
"worker_id": "puma_0",
|
||||
"memwd_handler_class": "Gitlab::Memory::Watchdog::PumaHandler",
|
||||
"memwd_sleep_time_s": 5,
|
||||
"memwd_rss_bytes": 1077682176,
|
||||
"memwd_max_rss_bytes": 629145600,
|
||||
"memwd_max_strikes": 5,
|
||||
"memwd_cur_strikes": 6,
|
||||
"message": "rss memory limit exceeded"
|
||||
}
|
||||
```
|
||||
|
||||
From this output:
|
||||
|
||||
- The formula that calculates the maximum memory value results in workers
|
||||
being killed before they reach the `per_worker_max_memory_mb` value.
|
||||
- In GitLab 13.4 and earlier, the default values for the formula were 550 MB for the primary
|
||||
and 850 MB for each worker.
|
||||
- In GitLab 13.5 and later, the values are primary: 800 MB, worker: 1024 MB.
|
||||
- The threshold for workers to be killed is set at 98% of the limit:
|
||||
|
||||
```plaintext
|
||||
0.98 * ( 800 + ( worker_processes * 1024MB ) )
|
||||
```
|
||||
|
||||
- In the log output above, `0.98 * ( 800 + ( 4 * 1024 ) )` returns the
|
||||
`max: 4798.08 MB` value.
|
||||
|
||||
Increasing the maximum to `1200`, for example, would set a `max: 5488 MB` value.
|
||||
|
||||
Workers use additional memory on top of the shared memory. The amount of memory
|
||||
depends on a site's use of GitLab.
|
||||
`memwd_rss_bytes` is the actual amount of memory consumed, and `memwd_max_rss_bytes` is the
|
||||
RSS limit set through `per_worker_max_memory_mb`.
|
||||
|
||||
## Change the worker timeout
|
||||
|
||||
|
|
@ -146,7 +136,7 @@ for details.
|
|||
When running Puma in single mode, some features are not supported:
|
||||
|
||||
- [Phased restart](https://gitlab.com/gitlab-org/gitlab/-/issues/300665)
|
||||
- [Puma Worker Killer](https://gitlab.com/gitlab-org/gitlab/-/issues/300664)
|
||||
- [Memory killers](#reducing-memory-use)
|
||||
|
||||
To learn more, visit [epic 5303](https://gitlab.com/groups/gitlab-org/-/epics/5303).
|
||||
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ To access private container registries, the GitLab Runner process can use:
|
|||
|
||||
To define which option should be used, the runner process reads the configuration in this order:
|
||||
|
||||
- A `DOCKER_AUTH_CONFIG` [CI/CD variable](../variables/index.md).
|
||||
- A `DOCKER_AUTH_CONFIG` [CI/CD variable](../variables/index.md) of the [type `Variable`](../variables/index.md#cicd-variable-types).
|
||||
- A `DOCKER_AUTH_CONFIG` environment variable set in the runner's `config.toml` file.
|
||||
- A `config.json` file in `$HOME/.docker` directory of the user running the process.
|
||||
If the `--user` flag is provided to run the child processes as unprivileged user,
|
||||
|
|
|
|||
|
|
@ -247,8 +247,8 @@ works.
|
|||
|
||||
### Puma per worker maximum memory
|
||||
|
||||
By default, each Puma worker is limited to 1024 MB of memory.
|
||||
This setting [can be adjusted](../administration/operations/puma.md#change-the-memory-limit-setting) and should be considered
|
||||
By default, each Puma worker is limited to 1.2 GB of memory.
|
||||
You can [adjust this memory setting](../administration/operations/puma.md#reducing-memory-use) and should do so
|
||||
if you must increase the number of Puma workers.
|
||||
|
||||
## Redis and Sidekiq
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ To add a new application for your user:
|
|||
- The OAuth 2 Client ID in the **Application ID** field.
|
||||
- The OAuth 2 Client Secret, accessible:
|
||||
- In the **Secret** field in GitLab 14.1 and earlier.
|
||||
- Using the **Copy** button on the **Secret** field
|
||||
- By selecting **Copy** in the **Secret** field
|
||||
[in GitLab 14.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/332844).
|
||||
|
||||
## Group owned applications
|
||||
|
|
@ -63,7 +63,7 @@ To add a new application for your user:
|
|||
|
||||
To add a new application for a group:
|
||||
|
||||
1. Navigate to the desired group.
|
||||
1. Go to the desired group.
|
||||
1. On the left sidebar, select **Settings > Applications**.
|
||||
1. Enter a **Name**, **Redirect URI** and OAuth 2 scopes as defined in [Authorized Applications](#authorized-applications).
|
||||
The **Redirect URI** is the URL where users are sent after they authorize with GitLab.
|
||||
|
|
@ -72,7 +72,7 @@ To add a new application for a group:
|
|||
- The OAuth 2 Client ID in the **Application ID** field.
|
||||
- The OAuth 2 Client Secret, accessible:
|
||||
- In the **Secret** field in GitLab 14.1 and earlier.
|
||||
- Using the **Copy** button on the **Secret** field
|
||||
- By selecting **Copy** in the **Secret** field
|
||||
[in GitLab 14.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/332844).
|
||||
|
||||
## Instance-wide applications
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.42.1'
|
||||
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.45.0'
|
||||
|
||||
.dast-auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.42.1'
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.45.0'
|
||||
|
||||
.auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.42.1'
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.45.0'
|
||||
|
||||
.auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -44,28 +44,28 @@ module Gitlab
|
|||
TRANSLATION_LEVELS = {
|
||||
'bg' => 0,
|
||||
'cs_CZ' => 0,
|
||||
'da_DK' => 36,
|
||||
'de' => 17,
|
||||
'da_DK' => 35,
|
||||
'de' => 16,
|
||||
'en' => 100,
|
||||
'eo' => 0,
|
||||
'es' => 35,
|
||||
'es' => 34,
|
||||
'fil_PH' => 0,
|
||||
'fr' => 94,
|
||||
'fr' => 98,
|
||||
'gl_ES' => 0,
|
||||
'id_ID' => 0,
|
||||
'it' => 1,
|
||||
'ja' => 30,
|
||||
'ja' => 29,
|
||||
'ko' => 20,
|
||||
'nb_NO' => 24,
|
||||
'nl_NL' => 0,
|
||||
'pl_PL' => 3,
|
||||
'pt_BR' => 57,
|
||||
'ro_RO' => 96,
|
||||
'ro_RO' => 94,
|
||||
'ru' => 26,
|
||||
'si_LK' => 11,
|
||||
'tr_TR' => 11,
|
||||
'uk' => 52,
|
||||
'zh_CN' => 97,
|
||||
'tr_TR' => 10,
|
||||
'uk' => 54,
|
||||
'zh_CN' => 98,
|
||||
'zh_HK' => 1,
|
||||
'zh_TW' => 99
|
||||
}.freeze
|
||||
|
|
|
|||
|
|
@ -1375,6 +1375,9 @@ msgstr ""
|
|||
msgid ", or "
|
||||
msgstr ""
|
||||
|
||||
msgid "-"
|
||||
msgstr ""
|
||||
|
||||
msgid "- %{policy_name} (notifying after %{elapsed_time} minutes unless %{status})"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11821,6 +11824,9 @@ msgstr ""
|
|||
msgid "Created a branch and a merge request to resolve this issue."
|
||||
msgstr ""
|
||||
|
||||
msgid "Created at"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created branch '%{branch_name}' and a merge request to resolve this issue."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@
|
|||
"dateformat": "^5.0.1",
|
||||
"deckar01-task_list": "^2.3.1",
|
||||
"diff": "^3.4.0",
|
||||
"dompurify": "^2.4.2",
|
||||
"dompurify": "^2.4.3",
|
||||
"dropzone": "^4.2.0",
|
||||
"editorconfig": "^0.15.3",
|
||||
"emoji-regex": "^10.0.0",
|
||||
|
|
|
|||
|
|
@ -158,11 +158,9 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
|||
context 'when todo has a note' do
|
||||
let(:note) { create(:note, project: project, note: "Check out stuff", noteable: create(:issue, project: project)) }
|
||||
let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note, target: note.noteable) }
|
||||
let(:two_line_mention_enabled_enabled) { true }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
stub_feature_flags(two_line_mention_enabled: two_line_mention_enabled_enabled)
|
||||
visit dashboard_todos_path
|
||||
end
|
||||
|
||||
|
|
@ -171,14 +169,6 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
|||
expect(page).to have_no_content('"Check out stuff"')
|
||||
expect(page).to have_content('Check out stuff')
|
||||
end
|
||||
|
||||
context 'when two_line_mention_enabled_enabled is disabled' do
|
||||
let(:two_line_mention_enabled_enabled) { false }
|
||||
|
||||
it 'shows mention previews on one line' do
|
||||
expect(page).to have_content("#{author.name} mentioned you: \"Check out stuff\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
|
|||
import httpStatus, {
|
||||
HTTP_STATUS_ACCEPTED,
|
||||
HTTP_STATUS_CREATED,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_NOT_FOUND,
|
||||
} from '~/lib/utils/http_status';
|
||||
|
|
@ -699,7 +700,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while fetching an issue template', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onGet(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onGet(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
Api.issueTemplate(namespace, project, templateKey, templateType, () => {
|
||||
|
|
@ -737,7 +738,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while fetching issue templates', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onGet(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onGet(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
Api.issueTemplates(namespace, project, templateType, () => {
|
||||
expect(mock.history.get).toHaveLength(1);
|
||||
|
|
@ -1032,7 +1033,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while fetching releases', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onGet(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onGet(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.releases(dummyProjectPath).catch(() => {
|
||||
expect(mock.history.get).toHaveLength(1);
|
||||
|
|
@ -1056,7 +1057,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while fetching the release', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onGet(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onGet(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.release(dummyProjectPath, dummyTagName).catch(() => {
|
||||
expect(mock.history.get).toHaveLength(1);
|
||||
|
|
@ -1084,7 +1085,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while creating the release', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onPost(expectedUrl, release).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost(expectedUrl, release).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.createRelease(dummyProjectPath, release).catch(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
@ -1112,7 +1113,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while updating the release', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onPut(expectedUrl, release).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPut(expectedUrl, release).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.updateRelease(dummyProjectPath, dummyTagName, release).catch(() => {
|
||||
expect(mock.history.put).toHaveLength(1);
|
||||
|
|
@ -1140,7 +1141,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while creating the Release', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onPost(expectedUrl, expectedLink).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost(expectedUrl, expectedLink).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.createReleaseLink(dummyProjectPath, dummyTagName, expectedLink).catch(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
@ -1165,7 +1166,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while deleting the Release', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onDelete(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onDelete(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.deleteReleaseLink(dummyProjectPath, dummyTagName, dummyLinkId).catch(() => {
|
||||
expect(mock.history.delete).toHaveLength(1);
|
||||
|
|
@ -1207,7 +1208,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while getting a raw file', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onPost(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.getRawFile(dummyProjectPath, dummyFilePath).catch(() => {
|
||||
expect(mock.history.get).toHaveLength(1);
|
||||
|
|
@ -1239,7 +1240,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when an error occurs while getting a raw file', () => {
|
||||
it('rejects the Promise', () => {
|
||||
mock.onPost(expectedUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost(expectedUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return Api.createProjectMergeRequest(dummyProjectPath).catch(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import Api from '~/api';
|
||||
import PipelinesTable from '~/commit/pipelines/pipelines_table.vue';
|
||||
import httpStatusCodes, {
|
||||
import {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_UNAUTHORIZED,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { createAlert } from '~/flash';
|
||||
|
|
@ -246,10 +247,10 @@ describe('Pipelines table in Commits and Merge requests', () => {
|
|||
'An error occurred while trying to run a new pipeline for this merge request.';
|
||||
|
||||
it.each`
|
||||
status | message
|
||||
${HTTP_STATUS_BAD_REQUEST} | ${defaultMsg}
|
||||
${HTTP_STATUS_UNAUTHORIZED} | ${permissionsMsg}
|
||||
${httpStatusCodes.INTERNAL_SERVER_ERROR} | ${defaultMsg}
|
||||
status | message
|
||||
${HTTP_STATUS_BAD_REQUEST} | ${defaultMsg}
|
||||
${HTTP_STATUS_UNAUTHORIZED} | ${permissionsMsg}
|
||||
${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${defaultMsg}
|
||||
`('displays permissions error message', async ({ status, message }) => {
|
||||
const response = { response: { status } };
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Video from '~/content_editor/extensions/video';
|
|||
import Link from '~/content_editor/extensions/link';
|
||||
import Loading from '~/content_editor/extensions/loading';
|
||||
import { VARIANT_DANGER } from '~/flash';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
import { createTestEditor, createDocBuilder } from '../test_utils';
|
||||
import {
|
||||
|
|
@ -167,7 +167,7 @@ describe('content_editor/extensions/attachment', () => {
|
|||
|
||||
describe('when uploading request fails', () => {
|
||||
beforeEach(() => {
|
||||
mock.onPost().reply(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost().reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
|
||||
it('resets the doc to original state', async () => {
|
||||
|
|
@ -246,7 +246,7 @@ describe('content_editor/extensions/attachment', () => {
|
|||
|
||||
describe('when uploading request fails', () => {
|
||||
beforeEach(() => {
|
||||
mock.onPost().reply(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mock.onPost().reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
|
||||
it('resets the doc to orginal state', async () => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { dismiss } from '~/feature_highlight/feature_highlight_helper';
|
||||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes, { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_CREATED, HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
|
@ -11,7 +11,6 @@ describe('feature highlight helper', () => {
|
|||
let mockAxios;
|
||||
const endpoint = '/-/callouts/dismiss';
|
||||
const highlightId = '123';
|
||||
const { INTERNAL_SERVER_ERROR } = httpStatusCodes;
|
||||
|
||||
beforeEach(() => {
|
||||
mockAxios = new MockAdapter(axios);
|
||||
|
|
@ -28,7 +27,9 @@ describe('feature highlight helper', () => {
|
|||
});
|
||||
|
||||
it('triggers flash when dismiss request fails', async () => {
|
||||
mockAxios.onPost(endpoint, { feature_name: highlightId }).replyOnce(INTERNAL_SERVER_ERROR);
|
||||
mockAxios
|
||||
.onPost(endpoint, { feature_name: highlightId })
|
||||
.replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
await dismiss(endpoint, highlightId);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
billingPlanNames,
|
||||
} from '~/integrations/constants';
|
||||
import { createStore } from '~/integrations/edit/store';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { refreshCurrentPage } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
mockIntegrationProps,
|
||||
|
|
@ -456,11 +456,11 @@ describe('IntegrationForm', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
scenario | replyStatus | errorMessage | serviceResponse | expectToast | expectSentry
|
||||
${'when "test settings" request fails'} | ${httpStatus.INTERNAL_SERVER_ERROR} | ${undefined} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true}
|
||||
${'when "test settings" returns an error'} | ${httpStatus.OK} | ${'an error'} | ${undefined} | ${'an error'} | ${false}
|
||||
${'when "test settings" returns an error with details'} | ${httpStatus.OK} | ${'an error.'} | ${'extra info'} | ${'an error. extra info'} | ${false}
|
||||
${'when "test settings" succeeds'} | ${httpStatus.OK} | ${undefined} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false}
|
||||
scenario | replyStatus | errorMessage | serviceResponse | expectToast | expectSentry
|
||||
${'when "test settings" request fails'} | ${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${undefined} | ${undefined} | ${I18N_DEFAULT_ERROR_MESSAGE} | ${true}
|
||||
${'when "test settings" returns an error'} | ${httpStatus.OK} | ${'an error'} | ${undefined} | ${'an error'} | ${false}
|
||||
${'when "test settings" returns an error with details'} | ${httpStatus.OK} | ${'an error.'} | ${'extra info'} | ${'an error. extra info'} | ${false}
|
||||
${'when "test settings" succeeds'} | ${httpStatus.OK} | ${undefined} | ${undefined} | ${I18N_SUCCESSFUL_CONNECTION_MESSAGE} | ${false}
|
||||
`(
|
||||
'$scenario',
|
||||
({ replyStatus, errorMessage, serviceResponse, expectToast, expectSentry }) => {
|
||||
|
|
@ -491,7 +491,7 @@ describe('IntegrationForm', () => {
|
|||
const mockResetPath = '/reset';
|
||||
|
||||
beforeEach(async () => {
|
||||
mockAxios.onPost(mockResetPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mockAxios.onPost(mockResetPath).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
createComponent({
|
||||
customStateProps: {
|
||||
resetPath: mockResetPath,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import IntegrationOverrides from '~/integrations/overrides/components/integratio
|
|||
import IntegrationTabs from '~/integrations/overrides/components/integration_tabs.vue';
|
||||
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
|
||||
import UrlSync from '~/vue_shared/components/url_sync.vue';
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ describe('IntegrationOverrides', () => {
|
|||
describe('when request fails', () => {
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(Sentry, 'captureException');
|
||||
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
mockAxios.onGet(defaultProps.overridesPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ import {
|
|||
import eventHub from '~/invite_members/event_hub';
|
||||
import ContentTransition from '~/vue_shared/components/content_transition.vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus, { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
|
||||
import {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_CREATED,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
displaySuccessfulInvitationAlert,
|
||||
|
|
@ -549,7 +553,7 @@ describe('InviteMembersModal', () => {
|
|||
|
||||
it('displays the generic error for http server error', async () => {
|
||||
mockInvitationsApi(
|
||||
httpStatus.INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
'Request failed with status code 500',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
|
||||
<table
|
||||
aria-busy="false"
|
||||
aria-colcount="6"
|
||||
class="table b-table gl-table gl-mt-0! table-sm"
|
||||
aria-colcount="9"
|
||||
class="table b-table gl-table gl-mt-0! ml-candidate-table table-sm"
|
||||
role="table"
|
||||
>
|
||||
<!---->
|
||||
|
|
@ -117,7 +117,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
scope="col"
|
||||
>
|
||||
<div>
|
||||
L1 Ratio
|
||||
Name
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
|
|
@ -127,7 +127,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Rmse
|
||||
Created at
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
|
|
@ -137,7 +137,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Auc
|
||||
User
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
|
|
@ -147,11 +147,41 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Mae
|
||||
L1 Ratio
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Rmse
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Auc
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
>
|
||||
<div>
|
||||
Mae
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="8"
|
||||
aria-label="Details"
|
||||
class=""
|
||||
role="columnheader"
|
||||
|
|
@ -160,7 +190,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
<div />
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
aria-label="Artifact"
|
||||
class=""
|
||||
role="columnheader"
|
||||
|
|
@ -183,39 +213,97 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.4
|
||||
<div
|
||||
title="aCandidate"
|
||||
>
|
||||
aCandidate
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="2"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
1
|
||||
<time
|
||||
class=""
|
||||
datetime="2023-01-05T14:07:02.975Z"
|
||||
title="2023-01-05T14:07:02.975Z"
|
||||
>
|
||||
in 2 years
|
||||
</time>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="/root"
|
||||
title="root"
|
||||
>
|
||||
@root
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="4"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="0.4"
|
||||
>
|
||||
0.4
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title="1"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="8"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="link_to_candidate1"
|
||||
title="Details"
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
|
|
@ -224,6 +312,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
href="link_to_artifact"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
title="Artifacts"
|
||||
>
|
||||
Artifacts
|
||||
</a>
|
||||
|
|
@ -238,42 +327,104 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.5
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="2"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<time
|
||||
class=""
|
||||
datetime="2023-01-05T14:07:02.975Z"
|
||||
title="2023-01-05T14:07:02.975Z"
|
||||
>
|
||||
in 2 years
|
||||
</time>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.3
|
||||
<div>
|
||||
-
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="4"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="0.5"
|
||||
>
|
||||
0.5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title="0.3"
|
||||
>
|
||||
0.3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="8"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="link_to_candidate2"
|
||||
title="Details"
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="Artifacts"
|
||||
>
|
||||
|
||||
-
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
|
|
@ -284,42 +435,104 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.5
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="2"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<time
|
||||
class=""
|
||||
datetime="2023-01-05T14:07:02.975Z"
|
||||
title="2023-01-05T14:07:02.975Z"
|
||||
>
|
||||
in 2 years
|
||||
</time>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.3
|
||||
<div>
|
||||
-
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="4"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="0.5"
|
||||
>
|
||||
0.5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title="0.3"
|
||||
>
|
||||
0.3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="8"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="link_to_candidate3"
|
||||
title="Details"
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="Artifacts"
|
||||
>
|
||||
|
||||
-
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
|
|
@ -330,42 +543,104 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.5
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="2"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<time
|
||||
class=""
|
||||
datetime="2023-01-05T14:07:02.975Z"
|
||||
title="2023-01-05T14:07:02.975Z"
|
||||
>
|
||||
in 2 years
|
||||
</time>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.3
|
||||
<div>
|
||||
-
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="4"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="0.5"
|
||||
>
|
||||
0.5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title="0.3"
|
||||
>
|
||||
0.3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="8"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="link_to_candidate4"
|
||||
title="Details"
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="Artifacts"
|
||||
>
|
||||
|
||||
-
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
|
|
@ -376,42 +651,104 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
|
|||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.5
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="2"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<time
|
||||
class=""
|
||||
datetime="2023-01-05T14:07:02.975Z"
|
||||
title="2023-01-05T14:07:02.975Z"
|
||||
>
|
||||
in 2 years
|
||||
</time>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
0.3
|
||||
<div>
|
||||
-
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="4"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="0.5"
|
||||
>
|
||||
0.5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="5"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title="0.3"
|
||||
>
|
||||
0.3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="7"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<div
|
||||
title=""
|
||||
>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="8"
|
||||
class=""
|
||||
role="cell"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
href="link_to_candidate5"
|
||||
title="Details"
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
<td
|
||||
aria-colindex="6"
|
||||
aria-colindex="9"
|
||||
class=""
|
||||
role="cell"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
title="Artifacts"
|
||||
>
|
||||
|
||||
-
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!---->
|
||||
<!---->
|
||||
|
|
|
|||
|
|
@ -46,11 +46,47 @@ describe('MlExperiment', () => {
|
|||
const createWrapperWithCandidates = (pagination = defaultPagination) => {
|
||||
return createWrapper(
|
||||
[
|
||||
{ rmse: 1, l1_ratio: 0.4, details: 'link_to_candidate1', artifact: 'link_to_artifact' },
|
||||
{ auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate2' },
|
||||
{ auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate3' },
|
||||
{ auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate4' },
|
||||
{ auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate5' },
|
||||
{
|
||||
rmse: 1,
|
||||
l1_ratio: 0.4,
|
||||
details: 'link_to_candidate1',
|
||||
artifact: 'link_to_artifact',
|
||||
name: 'aCandidate',
|
||||
created_at: '2023-01-05T14:07:02.975Z',
|
||||
user: { username: 'root', path: '/root' },
|
||||
},
|
||||
{
|
||||
auc: 0.3,
|
||||
l1_ratio: 0.5,
|
||||
details: 'link_to_candidate2',
|
||||
created_at: '2023-01-05T14:07:02.975Z',
|
||||
name: null,
|
||||
user: null,
|
||||
},
|
||||
{
|
||||
auc: 0.3,
|
||||
l1_ratio: 0.5,
|
||||
details: 'link_to_candidate3',
|
||||
created_at: '2023-01-05T14:07:02.975Z',
|
||||
name: null,
|
||||
user: null,
|
||||
},
|
||||
{
|
||||
auc: 0.3,
|
||||
l1_ratio: 0.5,
|
||||
details: 'link_to_candidate4',
|
||||
created_at: '2023-01-05T14:07:02.975Z',
|
||||
name: null,
|
||||
user: null,
|
||||
},
|
||||
{
|
||||
auc: 0.3,
|
||||
l1_ratio: 0.5,
|
||||
details: 'link_to_candidate5',
|
||||
created_at: '2023-01-05T14:07:02.975Z',
|
||||
name: null,
|
||||
user: null,
|
||||
},
|
||||
],
|
||||
['rmse', 'auc', 'mae'],
|
||||
['l1_ratio'],
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import * as commonUtils from '~/lib/utils/common_utils';
|
|||
import statusCodes, {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
HTTP_STATUS_UNAUTHORIZED,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
|
|
@ -138,7 +139,7 @@ describe('monitoring metrics_requests', () => {
|
|||
code | reason
|
||||
${HTTP_STATUS_BAD_REQUEST} | ${'Parameters are missing or incorrect'}
|
||||
${HTTP_STATUS_UNPROCESSABLE_ENTITY} | ${"Expression can't be executed"}
|
||||
${statusCodes.SERVICE_UNAVAILABLE} | ${'Query timed out or aborted'}
|
||||
${HTTP_STATUS_SERVICE_UNAVAILABLE} | ${'Query timed out or aborted'}
|
||||
`('rejects with details: "$reason" after getting an HTTP $code error', ({ code, reason }) => {
|
||||
mock.onGet(prometheusEndpoint).reply(code, {
|
||||
status: 'error',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import httpStatusCodes, { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_SERVICE_UNAVAILABLE } from '~/lib/utils/http_status';
|
||||
import { dashboardEmptyStates, metricStates } from '~/monitoring/constants';
|
||||
import * as types from '~/monitoring/stores/mutation_types';
|
||||
import mutations from '~/monitoring/stores/mutations';
|
||||
|
|
@ -317,7 +317,7 @@ describe('Monitoring mutations', () => {
|
|||
metricId,
|
||||
error: {
|
||||
response: {
|
||||
status: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
status: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -63,6 +63,14 @@ describe('DeleteModal', () => {
|
|||
expect(wrapper.emitted('confirm')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('emits cancel when cancel event is emitted', () => {
|
||||
expect(wrapper.emitted('cancel')).toBeUndefined();
|
||||
|
||||
findModal().vm.$emit('cancel');
|
||||
|
||||
expect(wrapper.emitted('cancel')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('show calls gl-modal show', () => {
|
||||
findModal().vm.show();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
import { GlAlert, GlSprintf } from '@gitlab/ui';
|
||||
import { nextTick } from 'vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue';
|
||||
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
|
||||
import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
|
||||
import {
|
||||
DELETE_PACKAGE_TRACKING_ACTION,
|
||||
DELETE_PACKAGES_TRACKING_ACTION,
|
||||
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
REQUEST_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
CANCEL_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
} from '~/packages_and_registries/package_registry/constants';
|
||||
import PackagesList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
|
||||
import Tracking from '~/tracking';
|
||||
|
|
@ -44,6 +49,7 @@ describe('packages_list', () => {
|
|||
const findRegistryList = () => wrapper.findComponent(RegistryList);
|
||||
const findPackagesListRow = () => wrapper.findComponent(PackagesListRow);
|
||||
const findErrorPackageAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findDeletePackagesModal = () => wrapper.findComponent(DeleteModal);
|
||||
|
||||
const mountComponent = (props) => {
|
||||
wrapper = shallowMountExtended(PackagesList, {
|
||||
|
|
@ -53,6 +59,11 @@ describe('packages_list', () => {
|
|||
},
|
||||
stubs: {
|
||||
DeletePackageModal,
|
||||
DeleteModal: stubComponent(DeleteModal, {
|
||||
methods: {
|
||||
show: jest.fn(),
|
||||
},
|
||||
}),
|
||||
GlSprintf,
|
||||
RegistryList,
|
||||
},
|
||||
|
|
@ -125,20 +136,48 @@ describe('packages_list', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when the user can destroy the package', () => {
|
||||
beforeEach(async () => {
|
||||
describe.each`
|
||||
description | finderFunction | deletePayload
|
||||
${'when the user can destroy the package'} | ${findPackagesListRow} | ${firstPackage}
|
||||
${'when the user can bulk destroy packages and deletes only one package'} | ${findRegistryList} | ${[firstPackage]}
|
||||
`('$description', ({ finderFunction, deletePayload }) => {
|
||||
let eventSpy;
|
||||
const category = 'UI::NpmPackages';
|
||||
|
||||
beforeEach(() => {
|
||||
eventSpy = jest.spyOn(Tracking, 'event');
|
||||
mountComponent();
|
||||
await findPackagesListRow().vm.$emit('delete', firstPackage);
|
||||
finderFunction().vm.$emit('delete', deletePayload);
|
||||
});
|
||||
|
||||
it('passes itemToBeDeleted to the modal', () => {
|
||||
expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual(firstPackage);
|
||||
});
|
||||
|
||||
it('emits package:delete when modal confirms', async () => {
|
||||
await findPackageListDeleteModal().vm.$emit('ok');
|
||||
it('requesting delete tracks the right action', () => {
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
expect(wrapper.emitted('package:delete')[0]).toEqual([firstPackage]);
|
||||
describe('when modal confirms', () => {
|
||||
beforeEach(() => {
|
||||
findPackageListDeleteModal().vm.$emit('ok');
|
||||
});
|
||||
|
||||
it('emits package:delete when modal confirms', () => {
|
||||
expect(wrapper.emitted('package:delete')[0]).toEqual([firstPackage]);
|
||||
});
|
||||
|
||||
it('tracks the right action', () => {
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['ok', 'cancel'])('resets itemToBeDeleted when modal emits %s', async (event) => {
|
||||
|
|
@ -146,26 +185,73 @@ describe('packages_list', () => {
|
|||
|
||||
expect(findPackageListDeleteModal().props('itemToBeDeleted')).toBeNull();
|
||||
});
|
||||
|
||||
it('canceling delete tracks the right action', () => {
|
||||
findPackageListDeleteModal().vm.$emit('cancel');
|
||||
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user can bulk destroy packages', () => {
|
||||
let eventSpy;
|
||||
const items = [firstPackage, secondPackage];
|
||||
|
||||
beforeEach(() => {
|
||||
eventSpy = jest.spyOn(Tracking, 'event');
|
||||
mountComponent();
|
||||
findRegistryList().vm.$emit('delete', items);
|
||||
});
|
||||
|
||||
it('passes itemToBeDeleted to the modal when there is only one package', async () => {
|
||||
await findRegistryList().vm.$emit('delete', [firstPackage]);
|
||||
|
||||
expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual(firstPackage);
|
||||
it('passes itemsToBeDeleted to the modal', () => {
|
||||
expect(findDeletePackagesModal().props('itemsToBeDeleted')).toStrictEqual(items);
|
||||
expect(wrapper.emitted('delete')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('emits delete when there is more than one package', () => {
|
||||
const items = [firstPackage, secondPackage];
|
||||
findRegistryList().vm.$emit('delete', items);
|
||||
it('requesting delete tracks the right action', () => {
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
REQUEST_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
expect(wrapper.emitted('delete')).toHaveLength(1);
|
||||
expect(wrapper.emitted('delete')[0]).toEqual([items]);
|
||||
describe('when modal confirms', () => {
|
||||
beforeEach(() => {
|
||||
findDeletePackagesModal().vm.$emit('confirm');
|
||||
});
|
||||
|
||||
it('emits delete event', () => {
|
||||
expect(wrapper.emitted('delete')[0]).toEqual([items]);
|
||||
});
|
||||
|
||||
it('tracks the right action', () => {
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
DELETE_PACKAGES_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['confirm', 'cancel'])('resets itemsToBeDeleted when modal emits %s', async (event) => {
|
||||
await findDeletePackagesModal().vm.$emit(event);
|
||||
|
||||
expect(findDeletePackagesModal().props('itemsToBeDeleted')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('canceling delete tracks the right action', () => {
|
||||
findDeletePackagesModal().vm.$emit('cancel');
|
||||
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
CANCEL_DELETE_PACKAGES_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -223,44 +309,4 @@ describe('packages_list', () => {
|
|||
expect(wrapper.emitted('next-page')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
let eventSpy;
|
||||
const category = 'UI::NpmPackages';
|
||||
|
||||
beforeEach(() => {
|
||||
eventSpy = jest.spyOn(Tracking, 'event');
|
||||
mountComponent();
|
||||
findPackagesListRow().vm.$emit('delete', firstPackage);
|
||||
return nextTick();
|
||||
});
|
||||
|
||||
it('requesting the delete tracks the right action', () => {
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('confirming delete tracks the right action', () => {
|
||||
findPackageListDeleteModal().vm.$emit('ok');
|
||||
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('canceling delete tracks the right action', () => {
|
||||
findPackageListDeleteModal().vm.$emit('cancel');
|
||||
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
import { GlAlert, GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import ListPage from '~/packages_and_registries/package_registry/pages/list.vue';
|
||||
import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue';
|
||||
import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
|
||||
import OriginalPackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
|
||||
import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import {
|
||||
PROJECT_RESOURCE_TYPE,
|
||||
GROUP_RESOURCE_TYPE,
|
||||
|
|
@ -62,7 +59,6 @@ describe('PackagesListApp', () => {
|
|||
const findListComponent = () => wrapper.findComponent(PackageList);
|
||||
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
|
||||
const findDeletePackage = () => wrapper.findComponent(DeletePackage);
|
||||
const findDeletePackagesModal = () => wrapper.findComponent(DeleteModal);
|
||||
|
||||
const mountComponent = ({
|
||||
resolver = jest.fn().mockResolvedValue(packagesListQuery()),
|
||||
|
|
@ -87,11 +83,6 @@ describe('PackagesListApp', () => {
|
|||
GlLink,
|
||||
PackageList,
|
||||
DeletePackage,
|
||||
DeleteModal: stubComponent(DeleteModal, {
|
||||
methods: {
|
||||
show: jest.fn(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -296,18 +287,6 @@ describe('PackagesListApp', () => {
|
|||
describe('bulk delete package', () => {
|
||||
const items = [{ id: '1' }, { id: '2' }];
|
||||
|
||||
it('deletePackage is bound to package-list package:delete event', async () => {
|
||||
mountComponent();
|
||||
|
||||
await waitForFirstRequest();
|
||||
|
||||
findListComponent().vm.$emit('delete', [{ id: '1' }, { id: '2' }]);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDeletePackagesModal().props('itemsToBeDeleted')).toEqual(items);
|
||||
});
|
||||
|
||||
it('calls mutation with the right values and shows success alert', async () => {
|
||||
const mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutation());
|
||||
mountComponent({
|
||||
|
|
@ -318,8 +297,6 @@ describe('PackagesListApp', () => {
|
|||
|
||||
findListComponent().vm.$emit('delete', items);
|
||||
|
||||
findDeletePackagesModal().vm.$emit('confirm');
|
||||
|
||||
expect(mutationResolver).toHaveBeenCalledWith({
|
||||
ids: items.map((item) => item.id),
|
||||
});
|
||||
|
|
@ -341,8 +318,6 @@ describe('PackagesListApp', () => {
|
|||
|
||||
findListComponent().vm.$emit('delete', items);
|
||||
|
||||
findDeletePackagesModal().vm.$emit('confirm');
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findAlert().exists()).toBe(true);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import WikiContent from '~/pages/shared/wikis/components/wiki_content.vue';
|
||||
import { renderGFM } from '~/behaviors/markdown/render_gfm';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { handleLocationHash } from '~/lib/utils/common_utils';
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ describe('pages/shared/wikis/components/wiki_content', () => {
|
|||
|
||||
describe('when loading content fails', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(PATH).replyOnce(httpStatus.INTERNAL_SERVER_ERROR, '');
|
||||
mock.onGet(PATH).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR, '');
|
||||
buildWrapper();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_help
|
|||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes, { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, {
|
||||
HTTP_STATUS_BAD_REQUEST,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import PipelineNewForm from '~/pipeline_new/components/pipeline_new_form.vue';
|
||||
import ciConfigVariablesQuery from '~/pipeline_new/graphql/queries/ci_config_variables.graphql';
|
||||
|
|
@ -365,7 +368,7 @@ describe('Pipeline New Form', () => {
|
|||
beforeEach(() => {
|
||||
mock
|
||||
.onGet(projectRefsEndpoint, { params: { search: '' } })
|
||||
.reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
.reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
findRefsDropdown().vm.$emit('loadingError');
|
||||
});
|
||||
|
|
@ -449,9 +452,7 @@ describe('Pipeline New Form', () => {
|
|||
|
||||
describe('when the error response cannot be handled', () => {
|
||||
beforeEach(async () => {
|
||||
mock
|
||||
.onPost(pipelinesPath)
|
||||
.reply(httpStatusCodes.INTERNAL_SERVER_ERROR, 'something went wrong');
|
||||
mock.onPost(pipelinesPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, 'something went wrong');
|
||||
|
||||
findForm().vm.$emit('submit', dummySubmitEvent);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
|
||||
import RefsDropdown from '~/pipeline_new/components/refs_dropdown.vue';
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ describe('Pipeline New Form', () => {
|
|||
beforeEach(async () => {
|
||||
mock
|
||||
.onGet(projectRefsEndpoint, { params: { search: '' } })
|
||||
.reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
.reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
findDropdown().vm.$emit('shown');
|
||||
await waitForPromises();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/commits_service';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { createAlert } from '~/flash';
|
||||
import { I18N_COMMIT_DATA_FETCH_ERROR } from '~/repository/constants';
|
||||
import { refWithSpecialCharMock } from './mock_data';
|
||||
|
|
@ -71,7 +71,7 @@ describe('commits service', () => {
|
|||
it('calls `createAlert` when the request fails', async () => {
|
||||
const invalidPath = '/#@ some/path';
|
||||
const invalidUrl = `${url}${invalidPath}`;
|
||||
mock.onGet(invalidUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR, [], {});
|
||||
mock.onGet(invalidUrl).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR, [], {});
|
||||
|
||||
await requestCommits(1, 'my-project', invalidPath);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import CodeIntelligence from '~/code_navigation/components/app.vue';
|
|||
import * as urlUtility from '~/lib/utils/url_utility';
|
||||
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
import { LEGACY_FILE_TYPES } from '~/repository/constants';
|
||||
import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants';
|
||||
|
|
@ -368,7 +368,7 @@ describe('Blob content viewer component', () => {
|
|||
|
||||
it('does not load a CodeIntelligence component when no viewers are loaded', async () => {
|
||||
const url = 'some_file.js?format=json&viewer=rich';
|
||||
mockAxios.onGet(url).replyOnce(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
mockAxios.onGet(url).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
await createComponent({ blob: { ...richViewerMock, fileType: 'unknown' } });
|
||||
|
||||
expect(findCodeIntelligence().exists()).toBe(false);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, {
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
} from '~/lib/utils/http_status';
|
||||
import TestCaseDetails from '~/pipelines/components/test_reports/test_case_details.vue';
|
||||
|
||||
import { failedReport } from 'jest/ci/reports/mock_data/mock_data';
|
||||
|
|
@ -91,7 +94,7 @@ describe('Test report extension', () => {
|
|||
});
|
||||
|
||||
it('with an error response, displays failed to load text', async () => {
|
||||
mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
mockApi(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import axios from '~/lib/utils/axios_utils';
|
|||
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
import accessibilityExtension from '~/vue_merge_request_widget/extensions/accessibility';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { accessibilityReportResponseErrors, accessibilityReportResponseSuccess } from './mock_data';
|
||||
|
||||
describe('Accessibility extension', () => {
|
||||
|
|
@ -53,7 +53,7 @@ describe('Accessibility extension', () => {
|
|||
});
|
||||
|
||||
it('displays failed loading text', async () => {
|
||||
mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
mockApi(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import axios from '~/lib/utils/axios_utils';
|
|||
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
import codeQualityExtension from '~/vue_merge_request_widget/extensions/code_quality';
|
||||
import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, {
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
} from '~/lib/utils/http_status';
|
||||
import {
|
||||
i18n,
|
||||
codeQualityPrefixes,
|
||||
|
|
@ -76,7 +79,7 @@ describe('Code Quality extension', () => {
|
|||
});
|
||||
|
||||
it('displays failed loading text', async () => {
|
||||
mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR);
|
||||
mockApi(HTTP_STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import testAction from 'helpers/vuex_action_helper';
|
|||
import { mockBranches } from 'jest/vue_shared/components/filtered_search_bar/mock_data';
|
||||
import Api from '~/api';
|
||||
import { createAlert } from '~/flash';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_SERVICE_UNAVAILABLE } from '~/lib/utils/http_status';
|
||||
import * as actions from '~/vue_shared/components/filtered_search_bar/store/modules/filters/actions';
|
||||
import * as types from '~/vue_shared/components/filtered_search_bar/store/modules/filters/mutation_types';
|
||||
import initialState from '~/vue_shared/components/filtered_search_bar/store/modules/filters/state';
|
||||
|
|
@ -143,7 +143,7 @@ describe('Filters actions', () => {
|
|||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onAny().replyOnce(httpStatusCodes.SERVICE_UNAVAILABLE);
|
||||
mock.onAny().replyOnce(HTTP_STATUS_SERVICE_UNAVAILABLE);
|
||||
});
|
||||
|
||||
it('dispatches RECEIVE_BRANCHES_ERROR', () => {
|
||||
|
|
@ -155,7 +155,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_BRANCHES },
|
||||
{
|
||||
type: types.RECEIVE_BRANCHES_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -215,7 +215,7 @@ describe('Filters actions', () => {
|
|||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onAny().replyOnce(httpStatusCodes.SERVICE_UNAVAILABLE);
|
||||
mock.onAny().replyOnce(HTTP_STATUS_SERVICE_UNAVAILABLE);
|
||||
});
|
||||
|
||||
it('dispatches RECEIVE_AUTHORS_ERROR and groupEndpoint set', () => {
|
||||
|
|
@ -227,7 +227,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_AUTHORS },
|
||||
{
|
||||
type: types.RECEIVE_AUTHORS_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -246,7 +246,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_AUTHORS },
|
||||
{
|
||||
type: types.RECEIVE_AUTHORS_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -282,7 +282,7 @@ describe('Filters actions', () => {
|
|||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onAny().replyOnce(httpStatusCodes.SERVICE_UNAVAILABLE);
|
||||
mock.onAny().replyOnce(HTTP_STATUS_SERVICE_UNAVAILABLE);
|
||||
});
|
||||
|
||||
it('dispatches RECEIVE_MILESTONES_ERROR', () => {
|
||||
|
|
@ -294,7 +294,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_MILESTONES },
|
||||
{
|
||||
type: types.RECEIVE_MILESTONES_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -352,7 +352,7 @@ describe('Filters actions', () => {
|
|||
describe('error', () => {
|
||||
let restoreVersion;
|
||||
beforeEach(() => {
|
||||
mock.onAny().replyOnce(httpStatusCodes.SERVICE_UNAVAILABLE);
|
||||
mock.onAny().replyOnce(HTTP_STATUS_SERVICE_UNAVAILABLE);
|
||||
restoreVersion = gon.api_version;
|
||||
gon.api_version = 'v1';
|
||||
});
|
||||
|
|
@ -370,7 +370,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_ASSIGNEES },
|
||||
{
|
||||
type: types.RECEIVE_ASSIGNEES_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -389,7 +389,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_ASSIGNEES },
|
||||
{
|
||||
type: types.RECEIVE_ASSIGNEES_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
@ -425,7 +425,7 @@ describe('Filters actions', () => {
|
|||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onAny().replyOnce(httpStatusCodes.SERVICE_UNAVAILABLE);
|
||||
mock.onAny().replyOnce(HTTP_STATUS_SERVICE_UNAVAILABLE);
|
||||
});
|
||||
|
||||
it('dispatches RECEIVE_LABELS_ERROR', () => {
|
||||
|
|
@ -437,7 +437,7 @@ describe('Filters actions', () => {
|
|||
{ type: types.REQUEST_LABELS },
|
||||
{
|
||||
type: types.RECEIVE_LABELS_ERROR,
|
||||
payload: httpStatusCodes.SERVICE_UNAVAILABLE,
|
||||
payload: HTTP_STATUS_SERVICE_UNAVAILABLE,
|
||||
},
|
||||
],
|
||||
[],
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
|
|||
end
|
||||
|
||||
let_it_be(:candidate1) do
|
||||
create(:ml_candidates, experiment: experiment, user: project.creator).tap do |c|
|
||||
create(:ml_candidates, experiment: experiment, user: project.creator, name: 'candidate1').tap do |c|
|
||||
c.params.build([{ name: 'param2', value: 'p3' }, { name: 'param3', value: 'p4' }])
|
||||
c.metrics.create!(name: 'metric3', value: 0.4)
|
||||
end
|
||||
|
|
@ -27,18 +27,39 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
|
|||
let_it_be(:candidates) { [candidate0, candidate1] }
|
||||
|
||||
describe '#candidates_table_items' do
|
||||
subject { helper.candidates_table_items(candidates) }
|
||||
subject { Gitlab::Json.parse(helper.candidates_table_items(candidates)) }
|
||||
|
||||
it 'creates the correct model for the table' do
|
||||
expected_value = [
|
||||
it 'creates the correct model for the table', :aggregate_failures do
|
||||
expected_values = [
|
||||
{ 'param1' => 'p1', 'param2' => 'p2', 'metric1' => '0.1000', 'metric2' => '0.2000', 'metric3' => '0.3000',
|
||||
'artifact' => "/#{project.full_path}/-/packages/#{candidate0.artifact.id}",
|
||||
'details' => "/#{project.full_path}/-/ml/candidates/#{candidate0.iid}" },
|
||||
'details' => "/#{project.full_path}/-/ml/candidates/#{candidate0.iid}",
|
||||
'name' => candidate0.name,
|
||||
'created_at' => candidate0.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
'user' => { 'username' => candidate0.user.username, 'path' => "/#{candidate0.user.username}" } },
|
||||
{ 'param2' => 'p3', 'param3' => 'p4', 'metric3' => '0.4000',
|
||||
'artifact' => nil, 'details' => "/#{project.full_path}/-/ml/candidates/#{candidate1.iid}" }
|
||||
'artifact' => nil, 'details' => "/#{project.full_path}/-/ml/candidates/#{candidate1.iid}",
|
||||
'name' => candidate1.name,
|
||||
'created_at' => candidate1.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
'user' => { 'username' => candidate1.user.username, 'path' => "/#{candidate1.user.username}" } }
|
||||
]
|
||||
|
||||
expect(Gitlab::Json.parse(subject)).to match_array(expected_value)
|
||||
subject.sort_by! { |s| s[:name] }
|
||||
|
||||
expect(subject[0]).to eq(expected_values[0])
|
||||
expect(subject[1]).to eq(expected_values[1])
|
||||
end
|
||||
|
||||
context 'when candidate does not have user' do
|
||||
let(:candidates) { [candidate0] }
|
||||
|
||||
before do
|
||||
allow(candidate0).to receive(:user).and_return(nil)
|
||||
end
|
||||
|
||||
it 'has the user property, but is nil' do
|
||||
expect(subject[0]['user']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -121,12 +121,13 @@ RSpec.describe Ml::Candidate, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#including_metrics_and_params" do
|
||||
subject { described_class.including_metrics_and_params.find_by(id: candidate.id) }
|
||||
describe "#including_relationships" do
|
||||
subject { described_class.including_relationships.find_by(id: candidate.id) }
|
||||
|
||||
it 'loads latest metrics and params', :aggregate_failures do
|
||||
expect(subject.association_cached?(:latest_metrics)).to be(true)
|
||||
expect(subject.association_cached?(:params)).to be(true)
|
||||
expect(subject.association_cached?(:user)).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5097,10 +5097,10 @@ dompurify@2.3.8:
|
|||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
|
||||
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
|
||||
|
||||
dompurify@^2.4.1, dompurify@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.2.tgz#c3409b49357804c9b00e1fbebea81f26514c5bc3"
|
||||
integrity sha512-ckbbxcGpfTJ7SNHC2yT2pHSCYxo2oQgSfdoDHQANzMzQyGzVmalF9W/B+X97Cdik5xFwWtwJP232gIP2+1kNEA==
|
||||
dompurify@^2.4.1, dompurify@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03"
|
||||
integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==
|
||||
|
||||
domutils@^2.5.2, domutils@^2.6.0:
|
||||
version "2.6.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue