Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c18599314d
commit
d4e22f4ade
|
|
@ -37,11 +37,6 @@ workflow:
|
|||
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^release-tools\/\d+\.\d+\.\d+-rc\d+$/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee)?$/ && $CI_PROJECT_PATH == "gitlab-org/gitlab"'
|
||||
when: never
|
||||
# For merge requests running exclusively in Ruby 3.0
|
||||
- if: '($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
|
||||
variables:
|
||||
RUBY_VERSION: "3.0"
|
||||
PIPELINE_NAME: 'Ruby 3 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
|
||||
# For merge requests running exclusively in Ruby 3.0
|
||||
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
|
||||
variables:
|
||||
RUBY_VERSION: "3.0"
|
||||
|
|
|
|||
|
|
@ -197,8 +197,7 @@
|
|||
- "spec/support/gitlab-git-test.git/**/*"
|
||||
|
||||
.yaml-lint-patterns: &yaml-lint-patterns
|
||||
- "*.yml"
|
||||
- "**/*.yml"
|
||||
- "**/*.{yml,yaml}{,.*}"
|
||||
|
||||
.lint-pipeline-yaml-patterns: &lint-pipeline-yaml-patterns
|
||||
- ".gitlab-ci.yml"
|
||||
|
|
|
|||
|
|
@ -4,21 +4,3 @@ Security/IoMethods:
|
|||
Details: grace period
|
||||
Exclude:
|
||||
- 'db/migrate/20210301200959_init_schema.rb'
|
||||
- 'ee/lib/tasks/gitlab/spdx.rake'
|
||||
- 'ee/spec/factories/spdx_catalogue.rb'
|
||||
- 'ee/spec/lib/ee/gitlab/import_export/group/legacy_tree_saver_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/spdx/catalogue_spec.rb'
|
||||
- 'lib/gitlab/import_export/json/legacy_reader.rb'
|
||||
- 'lib/gitlab/import_export/lfs_restorer.rb'
|
||||
- 'lib/tasks/gitlab/assets.rake'
|
||||
- 'spec/features/projects/import_export/export_file_spec.rb'
|
||||
- 'spec/lib/backup/gitaly_backup_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/group/tree_restorer_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/json/legacy_writer_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/lfs_saver_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/project/relation_saver_spec.rb'
|
||||
- 'spec/support/helpers/gitaly_setup.rb'
|
||||
- 'spec/support/import_export/common_util.rb'
|
||||
|
|
|
|||
19
.yamllint
19
.yamllint
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
extends: default
|
||||
|
||||
yaml-files:
|
||||
# defaults
|
||||
- '*.yaml'
|
||||
- '*.yml'
|
||||
- '.yamllint'
|
||||
# match more extensions
|
||||
- '*.yaml.*'
|
||||
- '*.yml.*'
|
||||
|
||||
# Ideally, we should have nothing in this ignore section.
|
||||
#
|
||||
# Please consider removing entries below by fixing them.
|
||||
|
|
@ -19,6 +28,16 @@ ignore: |
|
|||
# Broken on purpose (for testing)
|
||||
spec/fixtures/lib/gitlab/metrics/dashboard/broken_yml_syntax.yml
|
||||
|
||||
# Dynamic YAML files have syntax errors sometimes.
|
||||
*.erb
|
||||
|
||||
# Vim temporary files.
|
||||
*.sw[pon]
|
||||
|
||||
# Zipped files (by e.g. asset pipeline)
|
||||
*.gz
|
||||
*.bz2
|
||||
|
||||
#### Folders ####
|
||||
node_modules/
|
||||
tmp/
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
38812995ea07e43b12b4151c24bf6b960a70f74d
|
||||
6d9ffab522aae0f2fac5d3ff152064f56b01081d
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import Api, { DEFAULT_PER_PAGE } from '~/api';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_PAYLOAD_TOO_LARGE } from '~/lib/utils/http_status';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
|
@ -145,7 +145,7 @@ export default {
|
|||
let message = '';
|
||||
if (error?.response?.data?.message?.name) {
|
||||
message = this.$options.i18n.uploadErrorMessages.duplicate;
|
||||
} else if (error.response.status === httpStatusCodes.PAYLOAD_TOO_LARGE) {
|
||||
} else if (error.response.status === HTTP_STATUS_PAYLOAD_TOO_LARGE) {
|
||||
message = sprintf(this.$options.i18n.uploadErrorMessages.tooLarge, {
|
||||
limit: this.fileSizeLimit,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
import * as terminalService from '../../../../services/terminals';
|
||||
import { STARTING, STOPPING, STOPPED } from '../constants';
|
||||
import * as messages from '../messages';
|
||||
|
|
@ -108,7 +108,7 @@ export const restartSession = ({ state, dispatch, rootState }) => {
|
|||
// We may have removed the build, in this case we'll just create a new session
|
||||
if (
|
||||
responseStatus === httpStatus.NOT_FOUND ||
|
||||
responseStatus === httpStatus.UNPROCESSABLE_ENTITY
|
||||
responseStatus === HTTP_STATUS_UNPROCESSABLE_ENTITY
|
||||
) {
|
||||
dispatch('startSession');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { escape } from 'lodash';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
export const UNEXPECTED_ERROR_CONFIG = __(
|
||||
|
|
@ -28,7 +28,7 @@ export const ERROR_PERMISSION = __(
|
|||
);
|
||||
|
||||
export const configCheckError = (status, helpUrl) => {
|
||||
if (status === httpStatus.UNPROCESSABLE_ENTITY) {
|
||||
if (status === HTTP_STATUS_UNPROCESSABLE_ENTITY) {
|
||||
return sprintf(
|
||||
ERROR_CONFIG,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import _ from 'lodash';
|
|||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_TOO_MANY_REQUESTS } from '~/lib/utils/http_status';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
|
||||
import { visitUrl, objectToQuery } from '~/lib/utils/url_utility';
|
||||
|
|
@ -16,7 +16,7 @@ let eTagPoll;
|
|||
|
||||
const hasRedirectInError = (e) => e?.response?.data?.error?.redirect;
|
||||
const redirectToUrlInError = (e) => visitUrl(e.response.data.error.redirect);
|
||||
const tooManyRequests = (e) => e.response.status === httpStatusCodes.TOO_MANY_REQUESTS;
|
||||
const tooManyRequests = (e) => e.response.status === HTTP_STATUS_TOO_MANY_REQUESTS;
|
||||
const pathWithParams = ({ path, ...params }) => {
|
||||
const filteredParams = Object.fromEntries(
|
||||
Object.entries(params).filter(([, value]) => value !== ''),
|
||||
|
|
|
|||
|
|
@ -1,45 +1,43 @@
|
|||
/**
|
||||
* exports HTTP status codes
|
||||
*/
|
||||
export const HTTP_STATUS_ABORTED = 0;
|
||||
export const HTTP_STATUS_CREATED = 201;
|
||||
export const HTTP_STATUS_ACCEPTED = 202;
|
||||
export const HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
export const HTTP_STATUS_NO_CONTENT = 204;
|
||||
export const HTTP_STATUS_RESET_CONTENT = 205;
|
||||
export const HTTP_STATUS_PARTIAL_CONTENT = 206;
|
||||
export const HTTP_STATUS_MULTI_STATUS = 207;
|
||||
export const HTTP_STATUS_ALREADY_REPORTED = 208;
|
||||
export const HTTP_STATUS_IM_USED = 226;
|
||||
export const HTTP_STATUS_METHOD_NOT_ALLOWED = 405;
|
||||
export const HTTP_STATUS_CONFLICT = 409;
|
||||
export const HTTP_STATUS_GONE = 410;
|
||||
export const HTTP_STATUS_PAYLOAD_TOO_LARGE = 413;
|
||||
export const HTTP_STATUS_UNPROCESSABLE_ENTITY = 422;
|
||||
export const HTTP_STATUS_TOO_MANY_REQUESTS = 429;
|
||||
|
||||
// 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 = {
|
||||
ABORTED: 0,
|
||||
OK: 200,
|
||||
CREATED: 201,
|
||||
ACCEPTED: 202,
|
||||
NON_AUTHORITATIVE_INFORMATION: 203,
|
||||
NO_CONTENT: 204,
|
||||
RESET_CONTENT: 205,
|
||||
PARTIAL_CONTENT: 206,
|
||||
MULTI_STATUS: 207,
|
||||
ALREADY_REPORTED: 208,
|
||||
IM_USED: 226,
|
||||
MULTIPLE_CHOICES: 300,
|
||||
BAD_REQUEST: 400,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
METHOD_NOT_ALLOWED: 405,
|
||||
CONFLICT: 409,
|
||||
GONE: 410,
|
||||
PAYLOAD_TOO_LARGE: 413,
|
||||
UNPROCESSABLE_ENTITY: 422,
|
||||
TOO_MANY_REQUESTS: 429,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
SERVICE_UNAVAILABLE: 503,
|
||||
};
|
||||
|
||||
export const successCodes = [
|
||||
httpStatusCodes.OK,
|
||||
httpStatusCodes.CREATED,
|
||||
httpStatusCodes.ACCEPTED,
|
||||
httpStatusCodes.NON_AUTHORITATIVE_INFORMATION,
|
||||
httpStatusCodes.NO_CONTENT,
|
||||
httpStatusCodes.RESET_CONTENT,
|
||||
httpStatusCodes.PARTIAL_CONTENT,
|
||||
httpStatusCodes.MULTI_STATUS,
|
||||
httpStatusCodes.ALREADY_REPORTED,
|
||||
httpStatusCodes.IM_USED,
|
||||
HTTP_STATUS_CREATED,
|
||||
HTTP_STATUS_ACCEPTED,
|
||||
HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_RESET_CONTENT,
|
||||
HTTP_STATUS_PARTIAL_CONTENT,
|
||||
HTTP_STATUS_MULTI_STATUS,
|
||||
HTTP_STATUS_ALREADY_REPORTED,
|
||||
HTTP_STATUS_IM_USED,
|
||||
];
|
||||
|
||||
export default httpStatusCodes;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { normalizeHeaders } from './common_utils';
|
||||
import httpStatusCodes, { successCodes } from './http_status';
|
||||
import { HTTP_STATUS_ABORTED, successCodes } from './http_status';
|
||||
|
||||
/**
|
||||
* Polling utility for handling realtime updates.
|
||||
|
|
@ -108,7 +108,7 @@ export default class Poll {
|
|||
})
|
||||
.catch((error) => {
|
||||
notificationCallback(false);
|
||||
if (error.status === httpStatusCodes.ABORTED) {
|
||||
if (error.status === HTTP_STATUS_ABORTED) {
|
||||
return;
|
||||
}
|
||||
errorCallback(error);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { backOff } from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import statusCodes, {
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { PROMETHEUS_TIMEOUT } from '../constants';
|
||||
|
||||
const cancellableBackOffRequest = (makeRequestCallback) =>
|
||||
backOff((next, stop) => {
|
||||
makeRequestCallback()
|
||||
.then((resp) => {
|
||||
if (resp.status === statusCodes.NO_CONTENT) {
|
||||
if (resp.status === HTTP_STATUS_NO_CONTENT) {
|
||||
next();
|
||||
} else {
|
||||
stop(resp);
|
||||
|
|
@ -34,7 +37,7 @@ export const getPrometheusQueryData = (prometheusEndpoint, params, opts) =>
|
|||
const { response = {} } = error;
|
||||
if (
|
||||
response.status === statusCodes.BAD_REQUEST ||
|
||||
response.status === statusCodes.UNPROCESSABLE_ENTITY ||
|
||||
response.status === HTTP_STATUS_UNPROCESSABLE_ENTITY ||
|
||||
response.status === statusCodes.SERVICE_UNAVAILABLE
|
||||
) {
|
||||
const { data } = response;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Autosave from '~/autosave';
|
|||
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
|
||||
import { createAlert } from '~/flash';
|
||||
import { badgeState } from '~/issuable/components/status_box.vue';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
import {
|
||||
capitalizeFirstCharacter,
|
||||
convertToCamelCase,
|
||||
|
|
@ -28,8 +28,6 @@ import CommentTypeDropdown from './comment_type_dropdown.vue';
|
|||
import DiscussionLockedWidget from './discussion_locked_widget.vue';
|
||||
import NoteSignedOutWidget from './note_signed_out_widget.vue';
|
||||
|
||||
const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
|
||||
|
||||
export default {
|
||||
name: 'CommentForm',
|
||||
i18n: COMMENT_FORM,
|
||||
|
|
@ -198,7 +196,7 @@ export default {
|
|||
'toggleIssueLocalState',
|
||||
]),
|
||||
handleSaveError({ data, status }) {
|
||||
if (status === UNPROCESSABLE_ENTITY && data.errors?.commands_only?.length) {
|
||||
if (status === HTTP_STATUS_UNPROCESSABLE_ENTITY && data.errors?.commands_only?.length) {
|
||||
this.errors = data.errors.commands_only;
|
||||
} else {
|
||||
this.errors = [this.$options.i18n.GENERIC_UNSUBMITTABLE_NETWORK];
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import SafeHtml from '~/vue_shared/directives/safe_html';
|
|||
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
|
||||
import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants';
|
||||
import { createAlert } from '~/flash';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_GONE } from '~/lib/utils/http_status';
|
||||
import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
|
||||
import { truncateSha } from '~/lib/utils/text_utility';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
|
|
@ -338,7 +338,7 @@ export default {
|
|||
callback();
|
||||
})
|
||||
.catch((response) => {
|
||||
if (response.status === httpStatusCodes.GONE) {
|
||||
if (response.status === HTTP_STATUS_GONE) {
|
||||
this.removeNote(this.note);
|
||||
this.updateSuccess();
|
||||
callback();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { backOff } from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import statusCodes, { HTTP_STATUS_ACCEPTED } from '~/lib/utils/http_status';
|
||||
import { __, s__ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ function backOffRequest(makeRequestCallback) {
|
|||
return backOff((next, stop) => {
|
||||
makeRequestCallback()
|
||||
.then((resp) => {
|
||||
if (resp.status === statusCodes.ACCEPTED) {
|
||||
if (resp.status === HTTP_STATUS_ACCEPTED) {
|
||||
next();
|
||||
} else {
|
||||
stop(resp);
|
||||
|
|
@ -31,7 +31,7 @@ export const requestCreateProject = ({ dispatch, state, commit }) => {
|
|||
axios
|
||||
.post(state.createProjectEndpoint)
|
||||
.then((resp) => {
|
||||
if (resp.status === statusCodes.ACCEPTED) {
|
||||
if (resp.status === HTTP_STATUS_ACCEPTED) {
|
||||
dispatch('requestCreateProjectStatus', resp.data.job_id);
|
||||
}
|
||||
})
|
||||
|
|
@ -83,7 +83,7 @@ export const requestDeleteProject = ({ dispatch, state, commit }) => {
|
|||
axios
|
||||
.delete(state.deleteProjectEndpoint)
|
||||
.then((resp) => {
|
||||
if (resp.status === statusCodes.ACCEPTED) {
|
||||
if (resp.status === HTTP_STATUS_ACCEPTED) {
|
||||
dispatch('requestDeleteProjectStatus', resp.data.job_id);
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { backOff } from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import { bytesToMiB } from '~/lib/utils/number_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import MemoryGraph from '~/vue_shared/components/memory_graph.vue';
|
||||
|
|
@ -107,7 +107,7 @@ export default {
|
|||
backOff((next, stop) => {
|
||||
MRWidgetService.fetchMetrics(this.metricsUrl)
|
||||
.then((res) => {
|
||||
if (res.status === statusCodes.NO_CONTENT) {
|
||||
if (res.status === HTTP_STATUS_NO_CONTENT) {
|
||||
this.backOffRequestCounter += 1;
|
||||
/* eslint-disable no-unused-expressions */
|
||||
this.backOffRequestCounter < 3 ? next() : stop(res);
|
||||
|
|
@ -118,7 +118,7 @@ export default {
|
|||
.catch(stop);
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.status === statusCodes.NO_CONTENT) {
|
||||
if (res.status === HTTP_STATUS_NO_CONTENT) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { EXTENSION_ICONS } from '~/vue_merge_request_widget/constants';
|
||||
import { SEVERITY_ICONS_MR_WIDGET } from '~/ci/reports/codequality_report/constants';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import { parseCodeclimateMetrics } from '~/ci/reports/codequality_report/store/utils/codequality_parser';
|
||||
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
|
||||
import { i18n } from './constants';
|
||||
|
|
@ -43,7 +43,7 @@ export default {
|
|||
return {
|
||||
...response,
|
||||
data: {
|
||||
parsingInProgress: status === httpStatusCodes.NO_CONTENT,
|
||||
parsingInProgress: status === HTTP_STATUS_NO_CONTENT,
|
||||
resolvedErrors: parseCodeclimateMetrics(data.resolved_errors, this.blobPath.head_path),
|
||||
newErrors: parseCodeclimateMetrics(data.new_errors, this.blobPath.head_path),
|
||||
existingErrors: parseCodeclimateMetrics(data.existing_errors, this.blobPath.head_path),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
STATE_OPEN,
|
||||
TASK_TYPE_NAME,
|
||||
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
WIDGET_TYPE_PROGRESS,
|
||||
WIDGET_TYPE_MILESTONE,
|
||||
WIDGET_TYPE_HIERARCHY,
|
||||
WIDGET_TYPE_ASSIGNEES,
|
||||
|
|
@ -113,7 +114,15 @@ export default {
|
|||
return this.isExpanded ? __('Collapse') : __('Expand');
|
||||
},
|
||||
hasMetadata() {
|
||||
return this.milestone || this.assignees.length > 0 || this.labels.length > 0;
|
||||
return (
|
||||
this.progress !== undefined ||
|
||||
this.milestone !== undefined ||
|
||||
this.assignees.length > 0 ||
|
||||
this.labels.length > 0
|
||||
);
|
||||
},
|
||||
progress() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_PROGRESS)?.progress;
|
||||
},
|
||||
milestone() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_MILESTONE)?.milestone;
|
||||
|
|
@ -231,6 +240,7 @@ export default {
|
|||
<work-item-link-child-metadata
|
||||
v-if="hasMetadata"
|
||||
:allows-scoped-labels="allowsScopedLabels"
|
||||
:progress="progress"
|
||||
:milestone="milestone"
|
||||
:assignees="assignees"
|
||||
:labels="labels"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
<script>
|
||||
import { GlLabel, GlAvatar, GlAvatarLink, GlAvatarsInline, GlTooltipDirective } from '@gitlab/ui';
|
||||
import {
|
||||
GlIcon,
|
||||
GlLabel,
|
||||
GlAvatar,
|
||||
GlAvatarLink,
|
||||
GlAvatarsInline,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||
|
|
@ -8,6 +15,7 @@ import ItemMilestone from '~/issuable/components/issue_milestone.vue';
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlLabel,
|
||||
GlAvatar,
|
||||
GlAvatarLink,
|
||||
|
|
@ -23,6 +31,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
progress: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
milestone: {
|
||||
type: Object,
|
||||
required: false,
|
||||
|
|
@ -40,6 +53,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
hasProgress() {
|
||||
return !(this.progress === null || this.progress === undefined);
|
||||
},
|
||||
assigneesCollapsedTooltip() {
|
||||
if (this.assignees.length > 2) {
|
||||
return sprintf(s__('WorkItem|%{count} more assignees'), {
|
||||
|
|
@ -56,12 +72,6 @@ export default {
|
|||
}
|
||||
return '';
|
||||
},
|
||||
labelsContainerClass() {
|
||||
if (this.milestone || this.assignees.length) {
|
||||
return 'gl-sm-ml-5';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showScopedLabel(label) {
|
||||
|
|
@ -73,6 +83,16 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="gl-display-flex gl-flex-wrap gl-align-items-center">
|
||||
<div
|
||||
v-if="hasProgress"
|
||||
v-gl-tooltip.bottom
|
||||
:title="__('Progress')"
|
||||
class="gl-display-flex gl-align-items-center gl-mr-5 gl-cursor-help gl-line-height-normal"
|
||||
data-testid="item-progress"
|
||||
>
|
||||
<gl-icon name="progress" />
|
||||
<span class="gl-text-primary gl-ml-2">{{ progress }}%</span>
|
||||
</div>
|
||||
<item-milestone
|
||||
v-if="milestone"
|
||||
:milestone="milestone"
|
||||
|
|
@ -87,6 +107,7 @@ export default {
|
|||
badge-tooltip-prop="name"
|
||||
:badge-sr-only-text="assigneesCollapsedTooltip"
|
||||
:class="assigneesContainerClass"
|
||||
class="gl-mr-5"
|
||||
>
|
||||
<template #avatar="{ avatar }">
|
||||
<gl-avatar-link v-gl-tooltip target="blank" :href="avatar.webUrl" :title="avatar.name">
|
||||
|
|
@ -94,7 +115,7 @@ export default {
|
|||
</gl-avatar-link>
|
||||
</template>
|
||||
</gl-avatars-inline>
|
||||
<div v-if="labels.length" class="gl-display-flex gl-flex-wrap" :class="labelsContainerClass">
|
||||
<div v-if="labels.length" class="gl-display-flex gl-flex-wrap">
|
||||
<gl-label
|
||||
v-for="label in labels"
|
||||
:key="label.id"
|
||||
|
|
@ -102,7 +123,7 @@ export default {
|
|||
:background-color="label.color"
|
||||
:description="label.description"
|
||||
:scoped="showScopedLabel(label)"
|
||||
class="gl-mt-2 gl-sm-mt-0 gl-mr-2 gl-mb-auto gl-label-sm"
|
||||
class="gl-mt-3 gl-sm-mt-0 gl-mr-2 gl-mb-auto gl-label-sm"
|
||||
tooltip-placement="top"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
#import "~/work_items/graphql/milestone.fragment.graphql"
|
||||
|
||||
fragment WorkItemMetadataWidgets on WorkItemWidget {
|
||||
... on WorkItemWidgetProgress {
|
||||
type
|
||||
progress
|
||||
}
|
||||
... on WorkItemWidgetMilestone {
|
||||
type
|
||||
milestone {
|
||||
|
|
|
|||
|
|
@ -236,12 +236,6 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Remove once https: //gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/3198 is merged
|
||||
.gl-sm-ml-5 {
|
||||
@include gl-media-breakpoint-up(sm) {
|
||||
@include gl-ml-5;
|
||||
}
|
||||
}
|
||||
|
||||
/* End gitlab-ui#1709 */
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ module TodosHelper
|
|||
)
|
||||
when Todo::UNMERGEABLE then s_('Todos|Could not merge')
|
||||
when Todo::MERGE_TRAIN_REMOVED then s_("Todos|Removed from Merge Train")
|
||||
when Todo::MEMBER_ACCESS_REQUESTED then s_("Todos|has requested access")
|
||||
when Todo::MEMBER_ACCESS_REQUESTED then format(
|
||||
s_("Todos|has requested access to group %{which}"), which: _(todo.target.name)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,8 @@ class Todo < ApplicationRecord
|
|||
def body
|
||||
if note.present?
|
||||
note.note
|
||||
elsif member_access_requested?
|
||||
target.full_path
|
||||
else
|
||||
target.title
|
||||
end
|
||||
|
|
@ -258,6 +260,8 @@ class Todo < ApplicationRecord
|
|||
def target_reference
|
||||
if for_commit?
|
||||
target.reference_link_text
|
||||
elsif member_access_requested?
|
||||
target.full_path
|
||||
else
|
||||
target.to_reference
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"^[A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*$": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100
|
||||
"maxLength": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,11 @@
|
|||
%span
|
||||
= todo_parent_path(todo)
|
||||
|
||||
- unless todo.member_access_requested?
|
||||
%span.todo-label
|
||||
- if todo.target
|
||||
= link_to todo_target_name(todo), todo_target_path(todo), class: 'todo-target-link gl-text-gray-500! gl-text-decoration-none!', :'aria-describedby' => dom_id(todo) + "_describer", :'aria-label' => todo_target_aria_label(todo)
|
||||
- else
|
||||
= _("(removed)")
|
||||
%span.todo-label
|
||||
- if todo.target
|
||||
= link_to todo_target_name(todo), todo_target_path(todo), class: 'todo-target-link gl-text-gray-500! gl-text-decoration-none!', :'aria-describedby' => dom_id(todo) + "_describer", :'aria-label' => todo_target_aria_label(todo)
|
||||
- else
|
||||
= _("(removed)")
|
||||
|
||||
.todo-body.gl-mb-2.gl-px-2.gl-display-flex.gl-align-items-flex-start.gl-lg-align-items-center
|
||||
.todo-avatar.gl-display-none.gl-sm-display-inline-block
|
||||
|
|
|
|||
|
|
@ -11,15 +11,16 @@
|
|||
|
||||
.col-lg-8.gl-mb-3
|
||||
- if file_hooks.any?
|
||||
.card
|
||||
.card-header
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.header do
|
||||
= _('File Hooks (%{count})') % { count: file_hooks.count }
|
||||
%ul.content-list
|
||||
- file_hooks.each do |file|
|
||||
%li
|
||||
.monospace
|
||||
= File.basename(file)
|
||||
|
||||
- c.body do
|
||||
%ul.content-list
|
||||
- file_hooks.each do |file|
|
||||
%li
|
||||
.monospace
|
||||
= File.basename(file)
|
||||
- else
|
||||
.card.bg-light.text-center
|
||||
.nothing-here-block= _('No file hooks found.')
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.body do
|
||||
.nothing-here-block= _('No file hooks found.')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: multiple_environment_approval_rules_fe
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105719
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384334
|
||||
milestone: '15.7'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: false
|
||||
|
|
@ -20,14 +20,14 @@ GET /todos
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, `approval_required`, `unmergeable`, `directly_addressed`, `merge_train_removed` or `member_access_requested`. |
|
||||
| `author_id` | integer | no | The ID of an author |
|
||||
| `project_id` | integer | no | The ID of a project |
|
||||
| `group_id` | integer | no | The ID of a group |
|
||||
| `state` | string | no | The state of the to-do item. Can be either `pending` or `done` |
|
||||
| `type` | string | no | The type of to-do item. Can be either `Issue`, `MergeRequest`, `Commit`, `Epic`, `DesignManagement::Design` or `AlertManagement::Alert` |
|
||||
| `author_id` | integer | no | The ID of an author |
|
||||
| `project_id` | integer | no | The ID of a project |
|
||||
| `group_id` | integer | no | The ID of a group |
|
||||
| `state` | string | no | The state of the to-do item. Can be either `pending` or `done` |
|
||||
| `type` | string | no | The type of to-do item. Can be either `Issue`, `MergeRequest`, `Commit`, `Epic`, `DesignManagement::Design`, `AlertManagement::Alert` or `Namespace` |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos"
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ page, with these behaviors:
|
|||
branch name (unless their out-of-office (`OOO`) status changes, as in point 1). It
|
||||
removes leading `ce-` and `ee-`, and trailing `-ce` and `-ee`, so
|
||||
that it can be stable for backport branches.
|
||||
- People whose Slack or [GitLab status](../user/profile/index.md#set-your-current-status) emoji
|
||||
is Ⓜ `:m:`are only suggested as reviewers on projects they are a maintainer of.
|
||||
|
||||
The [Roulette dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) contains:
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ This needs to be done for any new, or updated gems.
|
|||
|
||||
We do not allow gems that are fetched from Git repositories. All gems have
|
||||
to be available in the RubyGems index. We want to minimize external build
|
||||
dependencies and build times.
|
||||
dependencies and build times. It's enforced by the RuboCop rule
|
||||
[`Cop/GemFetcher`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/lib/rubocop/cop/gem_fetcher.rb).
|
||||
|
||||
## Review the new dependency for quality
|
||||
|
||||
|
|
|
|||
|
|
@ -122,10 +122,21 @@ which has a related schema in `/config/metrics/objects_schemas/topology_schema.j
|
|||
|
||||
### Metric `time_frame`
|
||||
|
||||
- `7d`: The metric data applies to the most recent 7-day interval. For example, the following metric counts the number of users that create epics over a 7-day interval: `ee/config/metrics/counts_7d/20210305145820_g_product_planning_epic_created_weekly.yml`.
|
||||
- `28d`: The metric data applies to the most recent 28-day interval. For example, the following metric counts the number of unique users that create issues over a 28-day interval: `config/metrics/counts_28d/20210216181139_issues.yml`.
|
||||
- `all`: The metric data applies for the whole time the metric has been active (all-time interval). For example, the following metric counts all users that create issues: `/config/metrics/counts_all/20210216181115_issues.yml`.
|
||||
- `none`: The metric collects a type of data that's not tracked over time, such as settings and configuration information. Therefore, a time interval is not applicable. For example, `uuid` has no time interval applicable: `config/metrics/license/20210201124933_uuid.yml`.
|
||||
A metric's time frame is calculated based on the `time_frame` field and the `data_source` of the metric.
|
||||
For `redis_hll` metrics, the type of aggregation is also taken into consideration. In this context, the term "aggregation" refers to [chosen events data storage interval](implement.md#add-new-events), and is **NOT** related to the Aggregated Metrics feature.
|
||||
For more information about the aggregation type of each feature, see the [`common.yml` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/common.yml). Weeks run from Monday to Sunday.
|
||||
|
||||
| data_source | time_frame | aggregation | Description |
|
||||
|------------------------|------------|----------------|-------------------------------------------------|
|
||||
| any | `none` | not applicable | A type of data that’s not tracked over time, such as settings and configuration information |
|
||||
| `database` | `all` | not applicable | The whole time the metric has been active (all-time interval) |
|
||||
| `database` | `7d` | not applicable | 9 days ago to 2 days ago |
|
||||
| `database` | `28d` | not applicable | 30 days ago to 2 days ago |
|
||||
| `redis` | `all` | not applicable | The whole time the metric has been active (all-time interval) |
|
||||
| `redis_hll` | `7d` | `daily` | Most recent 7 complete days |
|
||||
| `redis_hll` | `7d` | `weekly` | Most recent complete week |
|
||||
| `redis_hll` | `28d` | `daily` | Most recent 28 complete days |
|
||||
| `redis_hll` | `28d` | `weekly` | Most recent 4 complete weeks |
|
||||
|
||||
### Data category
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ GitLab.
|
|||
For this association to succeed, each GitHub author and assignee in the repository
|
||||
must have a [public-facing email address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)
|
||||
on GitHub that matches their GitLab email address (regardless of how the account was created).
|
||||
If their email address from GitHub is set as their secondary email address in GitLab, it must be
|
||||
confirmed.
|
||||
|
||||
GitLab content imports that use GitHub accounts require that the GitHub public-facing email address is populated. This means
|
||||
all comments and contributions are properly mapped to the same user in GitLab. GitHub Enterprise does not require this
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pre-push:
|
|||
yamllint:
|
||||
tags: backend style
|
||||
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
|
||||
glob: '*.{yml,yaml}'
|
||||
glob: '*.{yml,yaml}{,.*}'
|
||||
run: scripts/lint-yaml.sh {files}
|
||||
stylelint:
|
||||
tags: stylesheet css style
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ module API
|
|||
|
||||
def todo_target_url(todo)
|
||||
return design_todo_target_url(todo) if todo.for_design?
|
||||
return todo.access_request_url if todo.member_access_requested?
|
||||
|
||||
target_type = todo.target_type.gsub('::', '_').underscore
|
||||
target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def read_hash
|
||||
Gitlab::Json.parse(IO.read(@path))
|
||||
Gitlab::Json.parse(::File.read(@path))
|
||||
rescue StandardError => e
|
||||
Gitlab::ErrorTracking.log_exception(e)
|
||||
raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ module Gitlab
|
|||
|
||||
@lfs_json ||=
|
||||
begin
|
||||
json = IO.read(lfs_json_path)
|
||||
json = File.read(lfs_json_path)
|
||||
Gitlab::Json.parse(json)
|
||||
rescue StandardError
|
||||
raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ namespace :gitlab do
|
|||
File.open(gzip, 'wb+') do |f|
|
||||
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
|
||||
gz.mtime = mtime
|
||||
gz.write IO.binread(file)
|
||||
gz.write File.binread(file)
|
||||
gz.close
|
||||
|
||||
File.utime(mtime, mtime, f.path)
|
||||
|
|
|
|||
|
|
@ -5078,6 +5078,9 @@ msgstr ""
|
|||
msgid "Approvals are optional."
|
||||
msgstr ""
|
||||
|
||||
msgid "Approvals required"
|
||||
msgstr ""
|
||||
|
||||
msgid "Approvals|Section: %section"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -22161,7 +22164,7 @@ msgstr ""
|
|||
msgid "Insights|Configure a custom report for insights into your group processes such as amount of issues, bugs, and merge requests per month. %{linkStart}How do I configure an insights report?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config for more information)."
|
||||
msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config in the YAML file or the enabled project features (issues, merge requests) in the project settings)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Insights|This project is filtered out in the insights.yml file (see the projects.only config for more information)."
|
||||
|
|
@ -33669,6 +33672,24 @@ msgstr ""
|
|||
msgid "ProtectedBranch|default"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|Allowed to deploy"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|An error occurred while fetching information on the selected approvers."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|Approval rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|Number of approvals must be between 1 and 5"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|Set which groups, access levels or users are required to approve."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironments|Set which groups, access levels or users that are allowed to deploy to this environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33681,6 +33702,9 @@ msgstr ""
|
|||
msgid "ProtectedEnvironment|Allowed to deploy to %{project} / %{environment}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironment|Approvers"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironment|Environment"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33717,6 +33741,9 @@ msgstr ""
|
|||
msgid "ProtectedEnvironment|Select an environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironment|Select environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedEnvironment|Select groups"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -43479,7 +43506,7 @@ msgstr ""
|
|||
msgid "Todos|added a to-do item"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|has requested access"
|
||||
msgid "Todos|has requested access to group %{which}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|mentioned %{who}"
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ module QA
|
|||
reload: false,
|
||||
skip_finished_loading_check_on_refresh: true
|
||||
) do
|
||||
is_partial_import = has_css?(:import_status_indicator, text: "Partial import")
|
||||
status_selector = 'import_status_indicator'
|
||||
is_partial_import = has_css?(status_selector, text: "Partial import")
|
||||
|
||||
# Temporarily adding this for investigation purposes. This makes sure that the details section is
|
||||
# expanded when the screenshot is taken when the test fails. This can be removed or repurposed later
|
||||
|
|
@ -92,7 +93,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
has_element?(:import_status_indicator, text: "Complete")
|
||||
has_element?(status_selector, text: "Complete")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module QA
|
|||
# https://github.com/gitlab-qa-github/import-test <- project under test
|
||||
#
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'GitHub import', :reliable do
|
||||
describe 'GitHub import' do
|
||||
include_context 'with github import'
|
||||
|
||||
context 'when imported via api' do
|
||||
|
|
|
|||
|
|
@ -1,23 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
# Spec uses real github.com, which means outage of github can actually block deployment
|
||||
# Keep spec in reliable bucket but don't run in blocking pipelines
|
||||
RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'GitHub import' do
|
||||
include QA::Support::Data::Github
|
||||
include_context 'with github import'
|
||||
|
||||
context 'when imported via UI' do
|
||||
let(:github_repo) { "#{github_username}/import-test" }
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
let(:imported_project) do
|
||||
Resource::ProjectImportedFromGithub.init do |project|
|
||||
project.import = true
|
||||
|
|
@ -41,8 +29,6 @@ module QA
|
|||
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|
|
||||
|
|
@ -51,10 +37,6 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
after do
|
||||
user.remove_via_api!
|
||||
end
|
||||
|
||||
it 'imports a project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do
|
||||
Page::Project::Import::Github.perform do |import_page|
|
||||
import_page.add_personal_access_token(Runtime::Env.github_access_token)
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.shared_context "with github import", :github, :skip_live_env, :requires_admin, quarantine: {
|
||||
type: :broken,
|
||||
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/382166"
|
||||
} do
|
||||
RSpec.shared_context "with github import", :github, :import, :requires_admin, :orchestrated do
|
||||
include QA::Support::Data::Github
|
||||
|
||||
let!(:github_repo) { "#{github_username}/import-test" }
|
||||
let!(:api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
let!(:group) do
|
||||
|
|
@ -30,7 +28,7 @@ module QA
|
|||
project.name = 'imported-project'
|
||||
project.group = group
|
||||
project.github_personal_access_token = Runtime::Env.github_access_token
|
||||
project.github_repository_path = "#{github_username}/import-test"
|
||||
project.github_repository_path = github_repo
|
||||
project.api_client = user_api_client
|
||||
project.issue_events_import = true
|
||||
project.full_notes_import = true
|
||||
|
|
@ -40,9 +38,5 @@ module QA
|
|||
before do
|
||||
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||
end
|
||||
|
||||
after do
|
||||
user.remove_via_api!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ gitlab:
|
|||
memory: 920Mi
|
||||
limits:
|
||||
cpu: 800m
|
||||
memory: 1100Mi
|
||||
memory: 1380Mi
|
||||
|
||||
sidekiq:
|
||||
resources:
|
||||
|
|
@ -99,7 +99,7 @@ gitlab:
|
|||
cpu: 746m
|
||||
memory: 2809Mi
|
||||
limits:
|
||||
cpu: 1119m
|
||||
cpu: 1300m
|
||||
memory: 4214Mi
|
||||
minReplicas: 1
|
||||
maxReplicas: 1
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ FactoryBot.define do
|
|||
action { Todo::UNMERGEABLE }
|
||||
end
|
||||
|
||||
trait :member_access_requested do
|
||||
action { Todo::MEMBER_ACCESS_REQUESTED }
|
||||
end
|
||||
|
||||
trait :pending do
|
||||
state { :pending }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -445,4 +445,28 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
|
|||
expect(page).to have_selector('.todos-list .todo', count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'User has a todo for an access requested raised for group membership' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
|
||||
let_it_be(:todo) do
|
||||
create(:todo, :member_access_requested,
|
||||
user: user,
|
||||
target: group,
|
||||
author: author,
|
||||
group: group)
|
||||
end
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
sign_in(user)
|
||||
|
||||
visit dashboard_todos_path
|
||||
end
|
||||
|
||||
it 'has todo present with access request content' do
|
||||
expect(page).to have_selector('.todos-list .todo', count: 1)
|
||||
expect(page).to have_content "#{author.name} has requested access to group #{group.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ RSpec.describe 'Import/Export - project export integration test', :js, feature_c
|
|||
project_json_path = File.join(tmpdir, 'project.json')
|
||||
expect(File).to exist(project_json_path)
|
||||
|
||||
project_hash = Gitlab::Json.parse(IO.read(project_json_path))
|
||||
project_hash = Gitlab::Json.parse(File.read(project_json_path))
|
||||
|
||||
sensitive_words.each do |sensitive_word|
|
||||
found = find_sensitive_attributes(sensitive_word, project_hash)
|
||||
|
|
@ -79,7 +79,7 @@ RSpec.describe 'Import/Export - project export integration test', :js, feature_c
|
|||
expect(File).to exist(project_json_path)
|
||||
|
||||
relations = []
|
||||
relations << Gitlab::Json.parse(IO.read(project_json_path))
|
||||
relations << Gitlab::Json.parse(File.read(project_json_path))
|
||||
Dir.glob(File.join(tmpdir, 'tree/project', '*.ndjson')) do |rb_filename|
|
||||
File.foreach(rb_filename) do |line|
|
||||
relations << Gitlab::Json.parse(line)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ const useMockLocation = (fn) => {
|
|||
afterEach(() => {
|
||||
currentWindowLocation = origWindowLocation;
|
||||
});
|
||||
|
||||
return () => {
|
||||
beforeEach(() => {
|
||||
currentWindowLocation = origWindowLocation;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
} from '~/alerts_settings/utils/error_messages';
|
||||
import { createAlert, VARIANT_SUCCESS } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
import {
|
||||
createHttpVariables,
|
||||
updateHttpVariables,
|
||||
|
|
@ -358,7 +358,7 @@ describe('AlertsSettingsWrapper', () => {
|
|||
});
|
||||
|
||||
it('shows an error alert when integration test payload is invalid', async () => {
|
||||
mock.onPost(/(.*)/).replyOnce(httpStatusCodes.UNPROCESSABLE_ENTITY);
|
||||
mock.onPost(/(.*)/).replyOnce(HTTP_STATUS_UNPROCESSABLE_ENTITY);
|
||||
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
|
||||
expect(createAlert).toHaveBeenCalledTimes(1);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Api, { DEFAULT_PER_PAGE } from '~/api';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, {
|
||||
HTTP_STATUS_ACCEPTED,
|
||||
HTTP_STATUS_CREATED,
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
} from '~/lib/utils/http_status';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
|
@ -1069,7 +1073,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when the release is successfully created', () => {
|
||||
it('resolves the Promise', () => {
|
||||
mock.onPost(expectedUrl, release).replyOnce(httpStatus.CREATED);
|
||||
mock.onPost(expectedUrl, release).replyOnce(HTTP_STATUS_CREATED);
|
||||
|
||||
return Api.createRelease(dummyProjectPath, release).then(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
@ -1125,7 +1129,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when the Release is successfully created', () => {
|
||||
it('resolves the Promise', () => {
|
||||
mock.onPost(expectedUrl, expectedLink).replyOnce(httpStatus.CREATED);
|
||||
mock.onPost(expectedUrl, expectedLink).replyOnce(HTTP_STATUS_CREATED);
|
||||
|
||||
return Api.createReleaseLink(dummyProjectPath, dummyTagName, expectedLink).then(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
@ -1224,7 +1228,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when the merge request is successfully created', () => {
|
||||
it('resolves the Promise', () => {
|
||||
mock.onPost(expectedUrl, options).replyOnce(httpStatus.CREATED);
|
||||
mock.onPost(expectedUrl, options).replyOnce(HTTP_STATUS_CREATED);
|
||||
|
||||
return Api.createProjectMergeRequest(dummyProjectPath, options).then(() => {
|
||||
expect(mock.history.post).toHaveLength(1);
|
||||
|
|
@ -1332,7 +1336,7 @@ describe('Api', () => {
|
|||
|
||||
describe('when the freeze period is successfully created', () => {
|
||||
it('resolves the Promise', () => {
|
||||
mock.onPost(expectedUrl, options).replyOnce(httpStatus.CREATED, expectedResult);
|
||||
mock.onPost(expectedUrl, options).replyOnce(HTTP_STATUS_CREATED, expectedResult);
|
||||
|
||||
return Api.createFreezePeriod(projectId, options).then(({ data }) => {
|
||||
expect(data).toStrictEqual(expectedResult);
|
||||
|
|
@ -1598,7 +1602,7 @@ describe('Api', () => {
|
|||
const secureFileId = 2;
|
||||
|
||||
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectId}/secure_files/${secureFileId}`;
|
||||
mock.onDelete(expectedUrl).reply(httpStatus.NO_CONTENT, '');
|
||||
mock.onDelete(expectedUrl).reply(HTTP_STATUS_NO_CONTENT, '');
|
||||
const { data } = await Api.deleteProjectSecureFile(projectId, secureFileId);
|
||||
expect(data).toEqual('');
|
||||
});
|
||||
|
|
@ -1609,10 +1613,10 @@ describe('Api', () => {
|
|||
const groupId = 1;
|
||||
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/groups/${groupId}/dependency_proxy/cache`;
|
||||
|
||||
mock.onDelete(expectedUrl).reply(httpStatus.ACCEPTED);
|
||||
mock.onDelete(expectedUrl).reply(HTTP_STATUS_ACCEPTED);
|
||||
const { status } = await Api.deleteDependencyProxyCacheList(groupId, {});
|
||||
|
||||
expect(status).toBe(httpStatus.ACCEPTED);
|
||||
expect(status).toBe(HTTP_STATUS_ACCEPTED);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_i
|
|||
import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
|
||||
import { waitForCaptchaToBeSolved } from '~/captcha/wait_for_captcha_to_be_solved';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, {
|
||||
HTTP_STATUS_CONFLICT,
|
||||
HTTP_STATUS_METHOD_NOT_ALLOWED,
|
||||
} from '~/lib/utils/http_status';
|
||||
|
||||
jest.mock('~/captcha/wait_for_captcha_to_be_solved');
|
||||
|
||||
|
|
@ -33,7 +36,7 @@ describe('registerCaptchaModalInterceptor', () => {
|
|||
mock.onAny('/endpoint-with-unrelated-error').reply(404, AXIOS_RESPONSE);
|
||||
mock.onAny('/endpoint-with-captcha').reply((config) => {
|
||||
if (!supportedMethods.includes(config.method)) {
|
||||
return [httpStatusCodes.METHOD_NOT_ALLOWED, { method: config.method }];
|
||||
return [HTTP_STATUS_METHOD_NOT_ALLOWED, { method: config.method }];
|
||||
}
|
||||
|
||||
const data = JSON.parse(config.data);
|
||||
|
|
@ -46,7 +49,7 @@ describe('registerCaptchaModalInterceptor', () => {
|
|||
return [httpStatusCodes.OK, { ...data, method: config.method, CAPTCHA_SUCCESS }];
|
||||
}
|
||||
|
||||
return [httpStatusCodes.CONFLICT, NEEDS_CAPTCHA_RESPONSE];
|
||||
return [HTTP_STATUS_CONFLICT, NEEDS_CAPTCHA_RESPONSE];
|
||||
});
|
||||
|
||||
axios.interceptors.response.handlers = [];
|
||||
|
|
@ -123,7 +126,7 @@ describe('registerCaptchaModalInterceptor', () => {
|
|||
await expect(() => axios[method]('/endpoint-with-captcha')).rejects.toThrow(
|
||||
expect.objectContaining({
|
||||
response: expect.objectContaining({
|
||||
status: httpStatusCodes.METHOD_NOT_ALLOWED,
|
||||
status: HTTP_STATUS_METHOD_NOT_ALLOWED,
|
||||
data: { method },
|
||||
}),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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 from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ describe('feature highlight helper', () => {
|
|||
let mockAxios;
|
||||
const endpoint = '/-/callouts/dismiss';
|
||||
const highlightId = '123';
|
||||
const { CREATED, INTERNAL_SERVER_ERROR } = httpStatusCodes;
|
||||
const { INTERNAL_SERVER_ERROR } = httpStatusCodes;
|
||||
|
||||
beforeEach(() => {
|
||||
mockAxios = new MockAdapter(axios);
|
||||
|
|
@ -22,7 +22,7 @@ describe('feature highlight helper', () => {
|
|||
});
|
||||
|
||||
it('calls persistent dismissal endpoint with highlightId', async () => {
|
||||
mockAxios.onPost(endpoint, { feature_name: highlightId }).replyOnce(CREATED);
|
||||
mockAxios.onPost(endpoint, { feature_name: highlightId }).replyOnce(HTTP_STATUS_CREATED);
|
||||
|
||||
await expect(dismiss(endpoint, highlightId)).resolves.toEqual(expect.anything());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
import * as messages from '~/ide/stores/modules/terminal/messages';
|
||||
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
|
||||
const TEST_PROJECT_PATH = 'lorem/root';
|
||||
const TEST_BRANCH_ID = 'main';
|
||||
|
|
@ -78,7 +78,7 @@ describe('IDE store terminal check actions', () => {
|
|||
|
||||
describe('receiveConfigCheckError', () => {
|
||||
it('handles error response', () => {
|
||||
const status = httpStatus.UNPROCESSABLE_ENTITY;
|
||||
const status = HTTP_STATUS_UNPROCESSABLE_ENTITY;
|
||||
const payload = { response: { status } };
|
||||
|
||||
return testAction(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { STARTING, PENDING, STOPPING, STOPPED } from '~/ide/stores/modules/termi
|
|||
import * as messages from '~/ide/stores/modules/terminal/messages';
|
||||
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ describe('IDE store terminal session controls actions', () => {
|
|||
);
|
||||
});
|
||||
|
||||
[httpStatus.NOT_FOUND, httpStatus.UNPROCESSABLE_ENTITY].forEach((status) => {
|
||||
[httpStatus.NOT_FOUND, HTTP_STATUS_UNPROCESSABLE_ENTITY].forEach((status) => {
|
||||
it(`dispatches request and startSession on ${status}`, () => {
|
||||
mock
|
||||
.onPost(state.session.retryPath, { branch: rootState.currentBranchId, format: 'json' })
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { escape } from 'lodash';
|
||||
import { TEST_HOST } from 'spec/test_constants';
|
||||
import * as messages from '~/ide/stores/modules/terminal/messages';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
|
||||
import { sprintf } from '~/locale';
|
||||
|
||||
const TEST_HELP_URL = `${TEST_HOST}/help`;
|
||||
|
|
@ -9,7 +9,7 @@ const TEST_HELP_URL = `${TEST_HOST}/help`;
|
|||
describe('IDE store terminal messages', () => {
|
||||
describe('configCheckError', () => {
|
||||
it('returns job error, with status UNPROCESSABLE_ENTITY', () => {
|
||||
const result = messages.configCheckError(httpStatus.UNPROCESSABLE_ENTITY, TEST_HELP_URL);
|
||||
const result = messages.configCheckError(HTTP_STATUS_UNPROCESSABLE_ENTITY, TEST_HELP_URL);
|
||||
|
||||
expect(result).toBe(
|
||||
sprintf(
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ 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 from '~/lib/utils/http_status';
|
||||
import httpStatus, { HTTP_STATUS_CREATED } from '~/lib/utils/http_status';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
import { GROUPS_INVITATIONS_PATH, invitationsApiResponse } from '../mock_data/api_responses';
|
||||
import {
|
||||
|
|
@ -467,7 +467,7 @@ describe('InviteMembersModal', () => {
|
|||
|
||||
describe('clearing the invalid state and message', () => {
|
||||
beforeEach(async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_TAKEN);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
@ -526,7 +526,7 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the restricted user api message for response with bad request', async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
@ -539,7 +539,7 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays all errors when there are multiple existing users that are restricted by email', async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
@ -636,7 +636,7 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the restricted email error when restricted email is invited', async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EMAIL_RESTRICTED);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays all errors when there are multiple emails that return a restricted error message', async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.MULTIPLE_RESTRICTED);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
@ -701,7 +701,7 @@ describe('InviteMembersModal', () => {
|
|||
createInviteMembersToGroupWrapper();
|
||||
|
||||
await triggerMembersTokenSelect([user3, user4, user5, user6]);
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EXPANDED_RESTRICTED);
|
||||
mockInvitationsApi(HTTP_STATUS_CREATED, invitationsApiResponse.EXPANDED_RESTRICTED);
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import pollUntilComplete from '~/lib/utils/poll_until_complete';
|
||||
|
||||
const endpoint = `${TEST_HOST}/foo`;
|
||||
|
|
@ -37,7 +37,7 @@ describe('pollUntilComplete', () => {
|
|||
beforeEach(() => {
|
||||
mock
|
||||
.onGet(endpoint)
|
||||
.replyOnce(httpStatusCodes.NO_CONTENT, undefined, pollIntervalHeader)
|
||||
.replyOnce(HTTP_STATUS_NO_CONTENT, undefined, pollIntervalHeader)
|
||||
.onGet(endpoint)
|
||||
.replyOnce(httpStatusCodes.OK, mockData);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { backoffMockImplementation } from 'helpers/backoff_helper';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as commonUtils from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import statusCodes, {
|
||||
HTTP_STATUS_NO_CONTENT,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { getDashboard, getPrometheusQueryData } from '~/monitoring/requests';
|
||||
import { metricsDashboardResponse } from '../fixture_data';
|
||||
|
||||
|
|
@ -37,8 +40,8 @@ describe('monitoring metrics_requests', () => {
|
|||
});
|
||||
|
||||
it('returns a dashboard response after retrying twice', () => {
|
||||
mock.onGet(dashboardEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(dashboardEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(dashboardEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(dashboardEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(dashboardEndpoint).reply(statusCodes.OK, response);
|
||||
|
||||
return getDashboard(dashboardEndpoint, params).then((data) => {
|
||||
|
|
@ -81,8 +84,8 @@ describe('monitoring metrics_requests', () => {
|
|||
|
||||
it('returns a dashboard response after retrying twice', () => {
|
||||
// Mock multiple attempts while the cache is filling up
|
||||
mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).reply(statusCodes.OK, response); // 3rd attempt
|
||||
|
||||
return getPrometheusQueryData(prometheusEndpoint, params).then((data) => {
|
||||
|
|
@ -116,8 +119,8 @@ describe('monitoring metrics_requests', () => {
|
|||
|
||||
it('rejects after retrying twice and getting an HTTP 500 error', () => {
|
||||
// Mock multiple attempts while the cache is filling up and fails
|
||||
mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(statusCodes.NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).replyOnce(HTTP_STATUS_NO_CONTENT);
|
||||
mock.onGet(prometheusEndpoint).reply(500, {
|
||||
status: 'error',
|
||||
error: 'An error occurred',
|
||||
|
|
@ -132,7 +135,7 @@ describe('monitoring metrics_requests', () => {
|
|||
it.each`
|
||||
code | reason
|
||||
${statusCodes.BAD_REQUEST} | ${'Parameters are missing or incorrect'}
|
||||
${statusCodes.UNPROCESSABLE_ENTITY} | ${"Expression can't be executed"}
|
||||
${HTTP_STATUS_UNPROCESSABLE_ENTITY} | ${"Expression can't be executed"}
|
||||
${statusCodes.SERVICE_UNAVAILABLE} | ${'Query timed out or aborted'}
|
||||
`('rejects with details: "$reason" after getting an HTTP $code error', ({ code, reason }) => {
|
||||
mock.onGet(prometheusEndpoint).reply(code, {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import testAction from 'helpers/vuex_action_helper';
|
|||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import * as commonUtils from '~/lib/utils/common_utils';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import statusCodes, {
|
||||
HTTP_STATUS_CREATED,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
|
||||
|
||||
import getAnnotations from '~/monitoring/queries/get_annotations.query.graphql';
|
||||
|
|
@ -944,7 +947,7 @@ describe('Monitoring store actions', () => {
|
|||
});
|
||||
|
||||
it('Succesful POST request resolves', async () => {
|
||||
mock.onPost(state.dashboardsEndpoint).reply(statusCodes.CREATED, {
|
||||
mock.onPost(state.dashboardsEndpoint).reply(HTTP_STATUS_CREATED, {
|
||||
dashboard: dashboardGitResponse[1],
|
||||
});
|
||||
|
||||
|
|
@ -969,7 +972,7 @@ describe('Monitoring store actions', () => {
|
|||
commit_message: 'A new commit message',
|
||||
});
|
||||
|
||||
mock.onPost(state.dashboardsEndpoint).reply(statusCodes.CREATED, {
|
||||
mock.onPost(state.dashboardsEndpoint).reply(HTTP_STATUS_CREATED, {
|
||||
dashboard: mockCreatedDashboard,
|
||||
});
|
||||
|
||||
|
|
@ -1133,7 +1136,7 @@ describe('Monitoring store actions', () => {
|
|||
|
||||
mock
|
||||
.onPost(panelPreviewEndpoint, { panel_yaml: mockYmlContent })
|
||||
.reply(statusCodes.UNPROCESSABLE_ENTITY, {
|
||||
.reply(HTTP_STATUS_UNPROCESSABLE_ENTITY, {
|
||||
message: mockErrorMsg,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import statusCodes, { HTTP_STATUS_ACCEPTED } from '~/lib/utils/http_status';
|
||||
import * as actions from '~/self_monitor/store/actions';
|
||||
import * as types from '~/self_monitor/store/mutation_types';
|
||||
import createState from '~/self_monitor/store/state';
|
||||
|
|
@ -44,7 +44,7 @@ describe('self-monitor actions', () => {
|
|||
beforeEach(() => {
|
||||
state.createProjectEndpoint = '/create';
|
||||
state.createProjectStatusEndpoint = '/create_status';
|
||||
mock.onPost(state.createProjectEndpoint).reply(statusCodes.ACCEPTED, {
|
||||
mock.onPost(state.createProjectEndpoint).reply(HTTP_STATUS_ACCEPTED, {
|
||||
job_id: '123',
|
||||
});
|
||||
mock.onGet(state.createProjectStatusEndpoint).reply(statusCodes.OK, {
|
||||
|
|
@ -151,7 +151,7 @@ describe('self-monitor actions', () => {
|
|||
beforeEach(() => {
|
||||
state.deleteProjectEndpoint = '/delete';
|
||||
state.deleteProjectStatusEndpoint = '/delete-status';
|
||||
mock.onDelete(state.deleteProjectEndpoint).reply(statusCodes.ACCEPTED, {
|
||||
mock.onDelete(state.deleteProjectEndpoint).reply(HTTP_STATUS_ACCEPTED, {
|
||||
job_id: '456',
|
||||
});
|
||||
mock.onGet(state.deleteProjectStatusEndpoint).reply(statusCodes.OK, {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ 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 from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { 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';
|
||||
|
|
@ -82,7 +82,7 @@ describe('Test report extension', () => {
|
|||
});
|
||||
|
||||
it('with a 204 response, continues to display loading state', async () => {
|
||||
mockApi(httpStatusCodes.NO_CONTENT, '');
|
||||
mockApi(HTTP_STATUS_NO_CONTENT, '');
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,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 codeQualityExtension from '~/vue_merge_request_widget/extensions/code_quality';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import httpStatusCodes, { HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
|
||||
import { i18n } from '~/vue_merge_request_widget/extensions/code_quality/constants';
|
||||
import {
|
||||
codeQualityResponseNewErrors,
|
||||
|
|
@ -63,7 +63,7 @@ describe('Code Quality extension', () => {
|
|||
});
|
||||
|
||||
it('with a 204 response, continues to display loading state', async () => {
|
||||
mockApi(httpStatusCodes.NO_CONTENT, '');
|
||||
mockApi(HTTP_STATUS_NO_CONTENT, '');
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlLabel, GlAvatarsInline } from '@gitlab/ui';
|
||||
import { GlIcon, GlLabel, GlAvatarsInline } from '@gitlab/ui';
|
||||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ describe('WorkItemLinkChildMetadata', () => {
|
|||
|
||||
const createComponent = ({
|
||||
allowsScopedLabels = true,
|
||||
progress = 10,
|
||||
milestone = mockMilestone,
|
||||
assignees = mockAssignees,
|
||||
labels = mockLabels,
|
||||
|
|
@ -19,6 +20,7 @@ describe('WorkItemLinkChildMetadata', () => {
|
|||
wrapper = shallowMountExtended(WorkItemLinkChildMetadata, {
|
||||
propsData: {
|
||||
allowsScopedLabels,
|
||||
progress,
|
||||
milestone,
|
||||
assignees,
|
||||
labels,
|
||||
|
|
@ -30,7 +32,16 @@ describe('WorkItemLinkChildMetadata', () => {
|
|||
createComponent();
|
||||
});
|
||||
|
||||
it('renders milestone link button', () => {
|
||||
it('renders item progress', () => {
|
||||
const progressEl = wrapper.findByTestId('item-progress');
|
||||
|
||||
expect(progressEl.exists()).toBe(true);
|
||||
expect(progressEl.attributes('title')).toBe('Progress');
|
||||
expect(progressEl.text().trim()).toBe('10%');
|
||||
expect(progressEl.findComponent(GlIcon).props('name')).toBe('progress');
|
||||
});
|
||||
|
||||
it('renders item milestone', () => {
|
||||
const milestoneLink = wrapper.findComponent(ItemMilestone);
|
||||
|
||||
expect(milestoneLink.exists()).toBe(true);
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ describe('WorkItemLinkChild', () => {
|
|||
expect(metadataEl.exists()).toBe(true);
|
||||
expect(metadataEl.props()).toMatchObject({
|
||||
allowsScopedLabels: true,
|
||||
progress: 10,
|
||||
milestone: mockMilestone,
|
||||
assignees: mockAssignees,
|
||||
labels: mockLabels,
|
||||
|
|
|
|||
|
|
@ -968,6 +968,11 @@ export const workItemObjectiveWithChild = {
|
|||
},
|
||||
__typename: 'WorkItemWidgetHierarchy',
|
||||
},
|
||||
{
|
||||
type: 'PROGRESS',
|
||||
__typename: 'WorkItemWidgetProgress',
|
||||
progress: 10,
|
||||
},
|
||||
{
|
||||
type: 'MILESTONE',
|
||||
__typename: 'WorkItemWidgetMilestone',
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ RSpec.describe TodosHelper do
|
|||
note: 'I am note, hear me roar')
|
||||
end
|
||||
|
||||
let_it_be(:group) { create(:group, :public, name: 'Group 1') }
|
||||
|
||||
let_it_be(:design_todo) do
|
||||
create(:todo, :mentioned,
|
||||
user: user,
|
||||
|
|
@ -37,6 +39,10 @@ RSpec.describe TodosHelper do
|
|||
create(:todo, target: issue)
|
||||
end
|
||||
|
||||
let_it_be(:group_todo) do
|
||||
create(:todo, target: group)
|
||||
end
|
||||
|
||||
describe '#todos_count_format' do
|
||||
it 'shows fuzzy count for 100 or more items' do
|
||||
expect(helper.todos_count_format(100)).to eq '99+'
|
||||
|
|
@ -155,9 +161,7 @@ RSpec.describe TodosHelper do
|
|||
end
|
||||
|
||||
context 'when a user requests access to group' do
|
||||
let(:group) { create(:group, :public) }
|
||||
|
||||
let(:group_access_request_todo) do
|
||||
let_it_be(:group_access_request_todo) do
|
||||
create(:todo,
|
||||
target_id: group.id,
|
||||
target_type: group.class.polymorphic_name,
|
||||
|
|
@ -358,7 +362,6 @@ RSpec.describe TodosHelper do
|
|||
Todo::APPROVAL_REQUIRED | false | format(s_("Todos|set %{who} as an approver"), who: _('you'))
|
||||
Todo::UNMERGEABLE | true | s_('Todos|Could not merge')
|
||||
Todo::MERGE_TRAIN_REMOVED | true | s_("Todos|Removed from Merge Train")
|
||||
Todo::MEMBER_ACCESS_REQUESTED | true | s_("Todos|has requested access")
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
@ -369,6 +372,18 @@ RSpec.describe TodosHelper do
|
|||
|
||||
it { expect(helper.todo_action_name(alert_todo)).to eq(expected_action_name) }
|
||||
end
|
||||
|
||||
context 'member access requested' do
|
||||
context 'when source is group' do
|
||||
it 'returns group access message' do
|
||||
group_todo.action = Todo::MEMBER_ACCESS_REQUESTED
|
||||
|
||||
expect(helper.todo_action_name(group_todo)).to eq(
|
||||
format(s_("Todos|has requested access to group %{which}"), which: _(group.name))
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#todo_due_date' do
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ RSpec.describe Backup::GitalyBackup do
|
|||
|
||||
it 'erases any existing repository backups' do
|
||||
existing_file = File.join(destination, 'some_existing_file')
|
||||
IO.write(existing_file, "Some existing file.\n")
|
||||
File.write(existing_file, "Some existing file.\n")
|
||||
|
||||
subject.start(:create, destination, backup_id: backup_id)
|
||||
subject.finish!
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeRestorer do
|
|||
let(:group) { create(:group) }
|
||||
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
|
||||
let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group, group_hash: nil) }
|
||||
let(:group_json) { Gitlab::Json.parse(IO.read(File.join(shared.export_path, 'group.json'))) }
|
||||
let(:group_json) { Gitlab::Json.parse(File.read(File.join(shared.export_path, 'group.json'))) }
|
||||
|
||||
shared_examples 'excluded attributes' do
|
||||
excluded_attributes = %w[
|
||||
|
|
|
|||
|
|
@ -154,6 +154,6 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
|
|||
end
|
||||
|
||||
def group_json(filename)
|
||||
::JSON.parse(IO.read(filename))
|
||||
::JSON.parse(File.read(filename))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer, feature: :subgroups do
|
|||
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
|
||||
let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group) }
|
||||
let(:exported_file) { File.join(shared.export_path, 'tree/groups/4352.json') }
|
||||
let(:group_json) { Gitlab::Json.parse(IO.read(exported_file)) }
|
||||
let(:group_json) { Gitlab::Json.parse(File.read(exported_file)) }
|
||||
|
||||
shared_examples 'excluded attributes' do
|
||||
excluded_attributes = %w[
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ RSpec.describe 'Test coverage of the Project Import' do
|
|||
end
|
||||
|
||||
def relations_from_json(json_file)
|
||||
json = Gitlab::Json.parse(IO.read(json_file))
|
||||
json = Gitlab::Json.parse(File.read(json_file))
|
||||
|
||||
[].tap { |res| gather_relations({ project: json }, res, []) }
|
||||
.map { |relation_names| relation_names.join('.') }
|
||||
|
|
|
|||
|
|
@ -96,6 +96,6 @@ RSpec.describe Gitlab::ImportExport::Json::LegacyWriter do
|
|||
def subject_json
|
||||
subject.close
|
||||
|
||||
::JSON.parse(IO.read(subject.path))
|
||||
::JSON.parse(File.read(subject.path))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ RSpec.describe Gitlab::ImportExport::LfsSaver do
|
|||
let(:lfs_json_file) { File.join(shared.export_path, Gitlab::ImportExport.lfs_objects_filename) }
|
||||
|
||||
def lfs_json
|
||||
Gitlab::Json.parse(IO.read(lfs_json_file))
|
||||
Gitlab::Json.parse(File.read(lfs_json_file))
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationSaver do
|
|||
end
|
||||
|
||||
def read_json(path)
|
||||
Gitlab::Json.parse(IO.read(path))
|
||||
Gitlab::Json.parse(File.read(path))
|
||||
end
|
||||
|
||||
def read_ndjson(path)
|
||||
|
|
|
|||
|
|
@ -2816,7 +2816,7 @@ RSpec.describe Group do
|
|||
end
|
||||
|
||||
describe 'has_project_with_service_desk_enabled?' do
|
||||
let_it_be(:group) { create(:group, :private) }
|
||||
let_it_be_with_refind(:group) { create(:group, :private) }
|
||||
|
||||
subject { group.has_project_with_service_desk_enabled? }
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ RSpec.describe WebHook do
|
|||
it { is_expected.to allow_value({ 'MY_TOKEN' => 'bar' }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'foo2' => 'bar' }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'x' => 'y' }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'x' => ('a' * 100) }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'x' => ('a' * 2048) }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'foo' => 'bar', 'bar' => 'baz' }).for(:url_variables) }
|
||||
it { is_expected.to allow_value((1..20).to_h { ["k#{_1}", 'value'] }).for(:url_variables) }
|
||||
it { is_expected.to allow_value({ 'MY-TOKEN' => 'bar' }).for(:url_variables) }
|
||||
|
|
@ -45,7 +45,7 @@ RSpec.describe WebHook do
|
|||
it { is_expected.not_to allow_value({ 'bar' => :baz }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ 'bar' => nil }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ 'foo' => '' }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ 'foo' => ('a' * 101) }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ 'foo' => ('a' * 2049) }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ 'has spaces' => 'foo' }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ '' => 'foo' }).for(:url_variables) }
|
||||
it { is_expected.not_to allow_value({ '1foo' => 'foo' }).for(:url_variables) }
|
||||
|
|
|
|||
|
|
@ -56,6 +56,15 @@ RSpec.describe Todo do
|
|||
|
||||
expect(subject.body).to eq 'quick fix'
|
||||
end
|
||||
|
||||
it 'returns full path of target when action is member_access_requested' do
|
||||
group = create(:group)
|
||||
|
||||
subject.target = group
|
||||
subject.action = Todo::MEMBER_ACCESS_REQUESTED
|
||||
|
||||
expect(subject.body).to eq group.full_path
|
||||
end
|
||||
end
|
||||
|
||||
describe '#done' do
|
||||
|
|
@ -182,6 +191,17 @@ RSpec.describe Todo do
|
|||
|
||||
expect(subject.target_reference).to eq issue.to_reference(full: false)
|
||||
end
|
||||
|
||||
context 'when target is member access requested' do
|
||||
it 'returns group full path' do
|
||||
group = create(:group)
|
||||
|
||||
subject.target = group
|
||||
subject.action = Todo::MEMBER_ACCESS_REQUESTED
|
||||
|
||||
expect(subject.target_reference).to eq group.full_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#self_added?' do
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
|
|||
let_it_be(:work_item) { create(:work_item, :task, project: project_1) }
|
||||
let_it_be(:merge_request) { create(:merge_request, source_project: project_1) }
|
||||
let_it_be(:alert) { create(:alert_management_alert, project: project_1) }
|
||||
let_it_be(:group_request_todo) { create(:todo, author: author_1, user: john_doe, target: group, action: Todo::MEMBER_ACCESS_REQUESTED) }
|
||||
let_it_be(:alert_todo) { create(:todo, project: project_1, author: john_doe, user: john_doe, target: alert) }
|
||||
let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
|
||||
let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: issue) }
|
||||
|
|
@ -71,7 +72,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.length).to eq(6)
|
||||
expect(json_response.length).to eq(7)
|
||||
|
||||
expect(json_response[0]).to include(
|
||||
'id' => pending_5.id,
|
||||
|
|
@ -127,6 +128,17 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
|
|||
'title' => alert.title
|
||||
)
|
||||
)
|
||||
|
||||
expect(json_response[6]).to include(
|
||||
'target_type' => 'Namespace',
|
||||
'action_name' => 'member_access_requested',
|
||||
'target' => hash_including(
|
||||
'id' => group.id,
|
||||
'name' => group.name,
|
||||
'full_path' => group.full_path
|
||||
),
|
||||
'target_url' => Gitlab::Routing.url_helpers.group_group_members_url(group, tab: 'access_requests')
|
||||
)
|
||||
end
|
||||
|
||||
context "when current user does not have access to one of the TODO's target" do
|
||||
|
|
@ -137,7 +149,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
|
|||
|
||||
get api('/todos', john_doe)
|
||||
|
||||
expect(json_response.count).to eq(6)
|
||||
expect(json_response.count).to eq(7)
|
||||
expect(json_response.map { |t| t['id'] }).not_to include(no_access_todo.id, pending_4.id)
|
||||
end
|
||||
end
|
||||
|
|
@ -231,7 +243,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
|
|||
create(:on_commit_todo, project: new_todo.project, author: author_1, user: john_doe, target: merge_request_3)
|
||||
create(:todo, project: new_todo.project, author: author_2, user: john_doe, target: merge_request_3)
|
||||
|
||||
expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(5)
|
||||
expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(6)
|
||||
control2 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
|
||||
|
||||
create_issue_todo_for(john_doe)
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ module GitalySetup
|
|||
# This code needs to work in an environment where we cannot use bundler,
|
||||
# so we cannot easily use the toml-rb gem. This ad-hoc parser should be
|
||||
# good enough.
|
||||
config_text = IO.read(toml)
|
||||
config_text = File.read(toml)
|
||||
|
||||
config_text.lines.each do |line|
|
||||
match_data = line.match(/^\s*(socket_path|listen_addr)\s*=\s*"([^"]*)"$/)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ module ImportExport
|
|||
path = File.join(dir_path, "#{exportable_path}.json")
|
||||
return unless File.exist?(path)
|
||||
|
||||
Gitlab::Json.parse(IO.read(path))
|
||||
Gitlab::Json.parse(File.read(path))
|
||||
end
|
||||
|
||||
def consume_relations(dir_path, exportable_path, key)
|
||||
|
|
@ -101,7 +101,7 @@ module ImportExport
|
|||
end
|
||||
|
||||
def project_json(filename)
|
||||
Gitlab::Json.parse(IO.read(filename))
|
||||
Gitlab::Json.parse(File.read(filename))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue