Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/organizations/groups_and_projects/graphql/resolvers.js6
-rw-r--r--app/assets/javascripts/organizations/mock_data.js (renamed from spec/frontend/organizations/groups_and_projects/mock_data.js)6
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue3
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_add_note.vue6
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue150
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_discussion.vue6
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_note.vue4
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue1
-rw-r--r--app/assets/javascripts/work_items/components/work_item_notes.vue7
-rw-r--r--app/helpers/environment_helper.rb2
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/users/update_service.rb1
-rw-r--r--config/initializers/00_deprecations.rb3
-rw-r--r--doc/development/rake_tasks.md12
-rw-r--r--doc/update/versions/gitlab_15_changes.md7
-rw-r--r--doc/user/application_security/dependency_list/index.md1
-rw-r--r--locale/gitlab.pot11
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.rb1
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.stub.rb24
-rw-r--r--spec/factories/users.rb4
-rw-r--r--spec/finders/deployments_finder_spec.rb16
-rw-r--r--spec/fixtures/api/schemas/job/job.json1
-rw-r--r--spec/fixtures/api/schemas/status/ci_detailed_status.json2
-rw-r--r--spec/frontend/organizations/groups_and_projects/components/groups_page_spec.js2
-rw-r--r--spec/frontend/organizations/groups_and_projects/components/projects_page_spec.js2
-rw-r--r--spec/frontend/organizations/groups_and_projects/utils_spec.js2
-rw-r--r--spec/frontend/work_items/components/notes/work_item_add_note_spec.js14
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_spec.js7
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js3
-rw-r--r--spec/frontend/work_items/components/work_item_notes_spec.js12
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/helpers/environment_helper_spec.rb9
-rw-r--r--spec/helpers/sessions_helper_spec.rb4
-rw-r--r--spec/models/user_spec.rb94
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb4
-rw-r--r--spec/requests/api/members_spec.rb2
-rw-r--r--spec/requests/git_http_spec.rb2
-rw-r--r--spec/requests/search_controller_spec.rb4
-rw-r--r--spec/serializers/deployment_entity_spec.rb89
-rw-r--r--spec/services/deployments/update_environment_service_spec.rb21
-rw-r--r--spec/support/shared_examples/ci/deployable_shared_examples.rb10
41 files changed, 375 insertions, 186 deletions
diff --git a/app/assets/javascripts/organizations/groups_and_projects/graphql/resolvers.js b/app/assets/javascripts/organizations/groups_and_projects/graphql/resolvers.js
index 8a375b28797..c78266b0476 100644
--- a/app/assets/javascripts/organizations/groups_and_projects/graphql/resolvers.js
+++ b/app/assets/javascripts/organizations/groups_and_projects/graphql/resolvers.js
@@ -1,8 +1,4 @@
-import {
- organization,
- organizationProjects,
- organizationGroups,
-} from 'jest/organizations/groups_and_projects/mock_data';
+import { organization, organizationProjects, organizationGroups } from '../../mock_data';
export default {
Query: {
diff --git a/spec/frontend/organizations/groups_and_projects/mock_data.js b/app/assets/javascripts/organizations/mock_data.js
index eb829a24f50..17ab7bd1d34 100644
--- a/spec/frontend/organizations/groups_and_projects/mock_data.js
+++ b/app/assets/javascripts/organizations/mock_data.js
@@ -1,3 +1,9 @@
+/* eslint-disable @gitlab/require-i18n-strings */
+
+// This is temporary mock data that will be removed when completing the following:
+// https://gitlab.com/gitlab-org/gitlab/-/issues/420777
+// https://gitlab.com/gitlab-org/gitlab/-/issues/421441
+
export const organization = {
id: 'gid://gitlab/Organization/1',
__typename: 'Organization',
diff --git a/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue b/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
index 9179331cdec..0ec8b6e2a0a 100644
--- a/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
@@ -6,6 +6,9 @@ const noteableTypeText = {
Issue: __('issue'),
Epic: __('epic'),
MergeRequest: __('merge request'),
+ Task: __('task'),
+ KeyResult: __('key result'),
+ Objective: __('objective'),
};
export default {
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
index 66ad3d50287..57faed61280 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
@@ -74,6 +74,11 @@ export default {
required: false,
default: false,
},
+ isWorkItemConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -263,6 +268,7 @@ export default {
:work-item-id="workItemId"
:autofocus="autofocus"
:comment-button-text="commentButtonText"
+ :is-work-item-confidential="isWorkItemConfidential"
@submitForm="updateWorkItem"
@cancelEditing="cancelEditing"
@error="$emit('error', $event)"
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
index b143c529014..a79169bde1e 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
@@ -3,11 +3,13 @@ import { GlButton, GlFormCheckbox, GlIcon, GlTooltipDirective } from '@gitlab/ui
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
-import { STATE_OPEN, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { STATE_OPEN, TRACKING_CATEGORY_SHOW, TASK_TYPE_NAME } from '~/work_items/constants';
import { getDraft, clearDraft, updateDraft } from '~/lib/utils/autosave';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import WorkItemStateToggleButton from '~/work_items/components/work_item_state_toggle_button.vue';
+import CommentFieldLayout from '~/notes/components/comment_field_layout.vue';
export default {
i18n: {
@@ -22,6 +24,7 @@ export default {
markdownDocsPath: helpPagePath('user/markdown'),
},
components: {
+ CommentFieldLayout,
GlButton,
MarkdownEditor,
GlFormCheckbox,
@@ -89,6 +92,11 @@ export default {
required: false,
default: false,
},
+ isWorkItemConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -119,6 +127,23 @@ export default {
commentButtonTextComputed() {
return this.isNoteInternal ? this.$options.i18n.addInternalNote : this.commentButtonText;
},
+ workItemDocPath() {
+ return this.workItemType === TASK_TYPE_NAME ? 'user/tasks.html' : 'user/okrs.html';
+ },
+ workItemDocAnchor() {
+ return this.workItemType === TASK_TYPE_NAME ? 'confidential-tasks' : 'confidential-okrs';
+ },
+ getWorkItemData() {
+ return {
+ confidential: this.isWorkItemConfidential,
+ confidential_issues_docs_path: helpPagePath(this.workItemDocPath, {
+ anchor: this.workItemDocAnchor,
+ }),
+ };
+ },
+ workItemTypeKey() {
+ return capitalizeFirstCharacter(this.workItemType).replace(' ', '');
+ },
},
methods: {
setCommentText(newText) {
@@ -158,66 +183,73 @@ export default {
<template>
<div class="timeline-discussion-body gl-overflow-visible!">
<div class="note-body gl-p-0! gl-overflow-visible!">
- <form class="common-note-form gfm-form js-main-target-form gl-flex-grow-1">
- <markdown-editor
- :value="commentText"
- :render-markdown-path="markdownPreviewPath"
- :markdown-docs-path="$options.constantOptions.markdownDocsPath"
- :autocomplete-data-sources="autocompleteDataSources"
- :form-field-props="formFieldProps"
- :add-spacing-classes="false"
- data-testid="work-item-add-comment"
- class="gl-mb-5"
- use-bottom-toolbar
- supports-quick-actions
- :autofocus="autofocus"
- @input="setCommentText"
- @keydown.meta.enter="$emit('submitForm', { commentText, isNoteInternal })"
- @keydown.ctrl.enter="$emit('submitForm', { commentText, isNoteInternal })"
- @keydown.esc.stop="cancelEditing"
- />
- <gl-form-checkbox
- v-if="isNewDiscussion"
- v-model="isNoteInternal"
- class="gl-mb-2"
- data-testid="internal-note-checkbox"
+ <form class="common-note-form gfm-form js-main-target-form gl-flex-grow-1 new-note">
+ <comment-field-layout
+ :with-alert-container="isWorkItemConfidential"
+ :noteable-data="getWorkItemData"
+ :noteable-type="workItemTypeKey"
>
- {{ $options.i18n.internal }}
- <gl-icon
- v-gl-tooltip:tooltipcontainer.bottom
- name="question-o"
- :size="16"
- :title="$options.i18n.internalVisibility"
- class="gl-text-blue-500"
+ <markdown-editor
+ :value="commentText"
+ :render-markdown-path="markdownPreviewPath"
+ :markdown-docs-path="$options.constantOptions.markdownDocsPath"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :form-field-props="formFieldProps"
+ :add-spacing-classes="false"
+ data-testid="work-item-add-comment"
+ use-bottom-toolbar
+ supports-quick-actions
+ :autofocus="autofocus"
+ @input="setCommentText"
+ @keydown.meta.enter="$emit('submitForm', { commentText, isNoteInternal })"
+ @keydown.ctrl.enter="$emit('submitForm', { commentText, isNoteInternal })"
+ @keydown.esc.stop="cancelEditing"
+ />
+ </comment-field-layout>
+ <div class="note-form-actions">
+ <gl-form-checkbox
+ v-if="isNewDiscussion"
+ v-model="isNoteInternal"
+ class="gl-mb-2"
+ data-testid="internal-note-checkbox"
+ >
+ {{ $options.i18n.internal }}
+ <gl-icon
+ v-gl-tooltip:tooltipcontainer.bottom
+ name="question-o"
+ :size="16"
+ :title="$options.i18n.internalVisibility"
+ class="gl-text-blue-500"
+ />
+ </gl-form-checkbox>
+ <gl-button
+ category="primary"
+ variant="confirm"
+ data-testid="confirm-button"
+ :disabled="!commentText.length"
+ :loading="isSubmitting"
+ @click="$emit('submitForm', { commentText, isNoteInternal })"
+ >{{ commentButtonTextComputed }}
+ </gl-button>
+ <work-item-state-toggle-button
+ v-if="isNewDiscussion"
+ class="gl-ml-3"
+ :work-item-id="workItemId"
+ :work-item-state="workItemState"
+ :work-item-type="workItemType"
+ can-update
+ @error="$emit('error', $event)"
/>
- </gl-form-checkbox>
- <gl-button
- category="primary"
- variant="confirm"
- data-testid="confirm-button"
- :disabled="!commentText.length"
- :loading="isSubmitting"
- @click="$emit('submitForm', { commentText, isNoteInternal })"
- >{{ commentButtonTextComputed }}
- </gl-button>
- <work-item-state-toggle-button
- v-if="isNewDiscussion"
- class="gl-ml-3"
- :work-item-id="workItemId"
- :work-item-state="workItemState"
- :work-item-type="workItemType"
- can-update
- @error="$emit('error', $event)"
- />
- <gl-button
- v-else
- data-testid="cancel-button"
- category="primary"
- class="gl-ml-3"
- :loading="updateInProgress"
- @click="cancelEditing"
- >{{ $options.i18n.cancelButtonText }}
- </gl-button>
+ <gl-button
+ v-else
+ data-testid="cancel-button"
+ category="primary"
+ class="gl-ml-3"
+ :loading="updateInProgress"
+ @click="cancelEditing"
+ >{{ $options.i18n.cancelButtonText }}
+ </gl-button>
+ </div>
</form>
</div>
</div>
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
index f030363664f..fd8842aa01a 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
@@ -65,6 +65,11 @@ export default {
required: false,
default: false,
},
+ isWorkItemConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -235,6 +240,7 @@ export default {
:autocomplete-data-sources="autocompleteDataSources"
:markdown-preview-path="markdownPreviewPath"
:is-internal-thread="note.internal"
+ :is-work-item-confidential="isWorkItemConfidential"
@startReplying="showReplyForm"
@cancelEditing="hideReplyForm"
@replied="onReplied"
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue
index 92560f2da9e..b5e3ea68725 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue
@@ -163,6 +163,9 @@ export default {
projectName() {
return this.workItem?.project?.name;
},
+ isWorkItemConfidential() {
+ return this.workItem?.confidential;
+ },
},
apollo: {
workItem: {
@@ -314,6 +317,7 @@ export default {
:markdown-preview-path="markdownPreviewPath"
:work-item-id="workItemId"
:autofocus="isEditing"
+ :is-work-item-confidential="isWorkItemConfidential"
class="gl-pl-3 gl-mt-3"
@cancelEditing="isEditing = false"
@submitForm="updateNote"
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index c12f7374610..8146b66dc1f 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -600,6 +600,7 @@ export default {
:assignees="workItemAssignees && workItemAssignees.assignees.nodes"
:can-set-work-item-metadata="canAssignUnassignUser"
:report-abuse-path="reportAbusePath"
+ :is-work-item-confidential="workItem.confidential"
class="gl-pt-5"
@error="updateError = $event"
@has-notes="updateHasNotes"
diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue
index 8fc460294e6..256f8ed53d1 100644
--- a/app/assets/javascripts/work_items/components/work_item_notes.vue
+++ b/app/assets/javascripts/work_items/components/work_item_notes.vue
@@ -79,6 +79,11 @@ export default {
type: String,
required: true,
},
+ isWorkItemConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -124,6 +129,7 @@ export default {
isNewDiscussion: true,
markdownPreviewPath: this.markdownPreviewPath,
autocompleteDataSources: this.autocompleteDataSources,
+ isWorkItemConfidential: this.isWorkItemConfidential,
};
},
notesArray() {
@@ -366,6 +372,7 @@ export default {
:markdown-preview-path="markdownPreviewPath"
:assignees="assignees"
:can-set-work-item-metadata="canSetWorkItemMetadata"
+ :is-work-item-confidential="isWorkItemConfidential"
@deleteNote="showDeleteNoteModal($event, discussion)"
@reportAbuse="reportAbuse(true, $event)"
@error="$emit('error', $event)"
diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb
index 8140ee97291..6e9379a5926 100644
--- a/app/helpers/environment_helper.rb
+++ b/app/helpers/environment_helper.rb
@@ -62,7 +62,7 @@ module EnvironmentHelper
klass = "ci-status ci-#{status.dasherize} #{ci_icon_utilities}"
text = "#{ci_icon_for_status(status)} <span class=\"gl-ml-2\">#{status_text}</span>".html_safe
- if deployment.deployable
+ if deployment.deployable.instance_of?(::Ci::Build)
link_to(text, deployment_path(deployment), class: klass)
else
content_tag(:span, text, class: klass)
diff --git a/app/models/user.rb b/app/models/user.rb
index 7f84d1e7310..849c3824ae1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2050,7 +2050,7 @@ class User < MainClusterwide::ApplicationRecord
end
def user_detail
- super.presence || build_user_detail
+ super.presence || (persisted? ? create_user_detail! : build_user_detail)
end
def pending_todo_for(target)
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index cc179ba964a..eb09f315953 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -21,7 +21,6 @@ module Users
yield(@user) if block
user_exists = @user.persisted?
- @user.user_detail # prevent assignment
discard_read_only_attributes
assign_attributes
diff --git a/config/initializers/00_deprecations.rb b/config/initializers/00_deprecations.rb
index 3d6a6491176..120e818e49a 100644
--- a/config/initializers/00_deprecations.rb
+++ b/config/initializers/00_deprecations.rb
@@ -44,7 +44,8 @@ else
# https://gitlab.com/gitlab-org/gitlab/-/issues/414556
/Merging .* no longer maintain both conditions, and will be replaced by the latter in Rails 7\.0/,
# https://gitlab.com/gitlab-org/gitlab/-/issues/415890
- /(Date|Time|TimeWithZone)#to_s.+ is deprecated/
+ /(Date|Time|TimeWithZone)#to_s.+ is deprecated/,
+ /Sum of non-numeric elements requires an initial argument/
]
view_component_3_warnings = [
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index f7876e16464..d879668649d 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -30,7 +30,9 @@ See also [Mass inserting Rails models](mass_insert.md).
**LARGE_PROJECTS**: Create large projects (through import) from a predefined set of URLs.
-### Seeding issues for all or a given project
+### Seeding Data
+
+#### Seeding issues for all projects or a single project
You can seed issues for all or a given project with the `gitlab:seed:issues`
task:
@@ -197,6 +199,14 @@ bundle exec rake "gitlab:seed:ci_variables_instance"
bundle exec rake "gitlab:seed:ci_variables_instance[25, CI_VAR_]"
```
+#### Seed a project for merge train development
+
+Seeds a project with merge trains configured and 20 merge requests(each with 3 commits). The command:
+
+```shell
+rake gitlab:seed:merge_trains:project
+```
+
### Automation
If you're very sure that you want to **wipe the current database** and refill
diff --git a/doc/update/versions/gitlab_15_changes.md b/doc/update/versions/gitlab_15_changes.md
index a70d137f1a9..f343f306490 100644
--- a/doc/update/versions/gitlab_15_changes.md
+++ b/doc/update/versions/gitlab_15_changes.md
@@ -558,6 +558,13 @@ A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706)
upgrading to 15.2.0 or later:
1. Ensure all GitLab web nodes are running GitLab 15.1.Z.
+ 1. If you run [GitLab on Kubernetes](https://docs.gitlab.com/charts/installation/) by using the cloud native GitLab Helm chart, make sure that all
+ webservice pods are running GitLab 15.1.Z:
+
+ ```shell
+ kubectl get pods -l app=webservice -o custom-columns=webservice-image:{.spec.containers[0].image},workhorse-image:{.spec.containers[1].image}
+ ```
+
1. [Enable the `active_support_hash_digest_sha256` feature flag](../../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to switch `ActiveSupport::Digest` to use SHA256:
1. [Start the rails console](../../administration/operations/rails_console.md)
diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md
index 944c5eac709..5d5ada0ebb5 100644
--- a/doc/user/application_security/dependency_list/index.md
+++ b/doc/user/application_security/dependency_list/index.md
@@ -73,6 +73,7 @@ Dependency paths are supported for the following package managers:
- [NuGet](https://www.nuget.org/)
- [Yarn 1.x](https://classic.yarnpkg.com/lang/en/)
- [sbt](https://www.scala-sbt.org)
+- [Conan](https://conan.io)
### Licenses
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 663bbe17b5d..6e98869e564 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -41516,7 +41516,7 @@ msgstr ""
msgid "ScanResultPolicy|Attributes are automatically applied by the scanners"
msgstr ""
-msgid "ScanResultPolicy|Block users from unprotecting branches"
+msgid "ScanResultPolicy|Block users from modifying protected branches"
msgstr ""
msgid "ScanResultPolicy|Choose criteria type"
@@ -56173,6 +56173,9 @@ msgstr ""
msgid "jigsaw is not defined"
msgstr ""
+msgid "key result"
+msgstr ""
+
msgid "kuromoji custom analyzer"
msgstr ""
@@ -56744,6 +56747,9 @@ msgstr ""
msgid "nounSeries|%{item}, and %{lastItem}"
msgstr ""
+msgid "objective"
+msgstr ""
+
msgid "on or after"
msgstr ""
@@ -57087,6 +57093,9 @@ msgstr ""
msgid "targeting "
msgstr ""
+msgid "task"
+msgstr ""
+
msgid "terraform states"
msgstr ""
diff --git a/qa/lib/gitlab/page/group/settings/billing.rb b/qa/lib/gitlab/page/group/settings/billing.rb
index 0565da0d353..086cb42a778 100644
--- a/qa/lib/gitlab/page/group/settings/billing.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.rb
@@ -17,7 +17,6 @@ module Gitlab
# Usage
p :seats_in_subscription
p :seats_currently_in_use
- link :see_seats_usage
p :max_seats_used
p :seats_owed
diff --git a/qa/lib/gitlab/page/group/settings/billing.stub.rb b/qa/lib/gitlab/page/group/settings/billing.stub.rb
index c49d744d61f..9aa1a23ec14 100644
--- a/qa/lib/gitlab/page/group/settings/billing.stub.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.stub.rb
@@ -197,30 +197,6 @@ module Gitlab
# This is a stub, used for indexing. The method is dynamically generated.
end
- # @note Defined as +link :see_seats_usage+
- # Clicks +see_seats_usage+
- def see_seats_usage
- # This is a stub, used for indexing. The method is dynamically generated.
- end
-
- # @example
- # Gitlab::Page::Group::Settings::Billing.perform do |billing|
- # expect(billing.see_seats_usage_element).to exist
- # end
- # @return [Watir::Link] The raw +Link+ element
- def see_seats_usage_element
- # This is a stub, used for indexing. The method is dynamically generated.
- end
-
- # @example
- # Gitlab::Page::Group::Settings::Billing.perform do |billing|
- # expect(billing).to be_see_seats_usage
- # end
- # @return [Boolean] true if the +see_seats_usage+ element is present on the page
- def see_seats_usage?
- # This is a stub, used for indexing. The method is dynamically generated.
- end
-
# @note Defined as +p :max_seats_used+
# @return [String] The text content or value of +max_seats_used+
def max_seats_used
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 67c857165fc..e29ed02297f 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -111,6 +111,10 @@ FactoryBot.define do
last_sign_in_ip { '127.0.0.1' }
end
+ trait :with_user_detail do
+ after :build, &:user_detail
+ end
+
trait :with_credit_card_validation do
after :create do |user|
create :credit_card_validation, user: user
diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb
index 5a803ee2a0d..807a7ca8e26 100644
--- a/spec/finders/deployments_finder_spec.rb
+++ b/spec/finders/deployments_finder_spec.rb
@@ -280,6 +280,22 @@ RSpec.describe DeploymentsFinder, feature_category: :deployment_management do
it { is_expected.to match_array([deployment_2]) }
end
end
+
+ context 'with mixed deployable types' do
+ let!(:deployment_1) do
+ create(:deployment, :success, project: project, deployable: create(:ci_build))
+ end
+
+ let!(:deployment_2) do
+ create(:deployment, :success, project: project, deployable: create(:ci_bridge))
+ end
+
+ let(:params) { { **base_params, status: 'success' } }
+
+ it 'successfuly fetches deployments' do
+ is_expected.to contain_exactly(deployment_1, deployment_2)
+ end
+ end
end
context 'at group scope' do
diff --git a/spec/fixtures/api/schemas/job/job.json b/spec/fixtures/api/schemas/job/job.json
index f3d5e9b038a..34668f309a6 100644
--- a/spec/fixtures/api/schemas/job/job.json
+++ b/spec/fixtures/api/schemas/job/job.json
@@ -5,7 +5,6 @@
"id",
"name",
"started",
- "build_path",
"playable",
"created_at",
"updated_at",
diff --git a/spec/fixtures/api/schemas/status/ci_detailed_status.json b/spec/fixtures/api/schemas/status/ci_detailed_status.json
index 8d0f1e4a6af..0d9e4975858 100644
--- a/spec/fixtures/api/schemas/status/ci_detailed_status.json
+++ b/spec/fixtures/api/schemas/status/ci_detailed_status.json
@@ -17,7 +17,7 @@
"group": { "type": "string" },
"tooltip": { "type": "string" },
"has_details": { "type": "boolean" },
- "details_path": { "type": "string" },
+ "details_path": { "oneOf": [{ "type": "null" }, {"type": "string" }] },
"favicon": { "type": "string" },
"illustration": { "$ref": "illustration.json" },
"action": { "$ref": "action.json" }
diff --git a/spec/frontend/organizations/groups_and_projects/components/groups_page_spec.js b/spec/frontend/organizations/groups_and_projects/components/groups_page_spec.js
index 537f8114fcf..57f4911e5f3 100644
--- a/spec/frontend/organizations/groups_and_projects/components/groups_page_spec.js
+++ b/spec/frontend/organizations/groups_and_projects/components/groups_page_spec.js
@@ -9,7 +9,7 @@ import { createAlert } from '~/alert';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { organizationGroups } from '../mock_data';
+import { organizationGroups } from '~/organizations/mock_data';
jest.mock('~/alert');
diff --git a/spec/frontend/organizations/groups_and_projects/components/projects_page_spec.js b/spec/frontend/organizations/groups_and_projects/components/projects_page_spec.js
index 7cadcab5021..2965de7cfc8 100644
--- a/spec/frontend/organizations/groups_and_projects/components/projects_page_spec.js
+++ b/spec/frontend/organizations/groups_and_projects/components/projects_page_spec.js
@@ -9,7 +9,7 @@ import { createAlert } from '~/alert';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { organizationProjects } from '../mock_data';
+import { organizationProjects } from '~/organizations/mock_data';
jest.mock('~/alert');
diff --git a/spec/frontend/organizations/groups_and_projects/utils_spec.js b/spec/frontend/organizations/groups_and_projects/utils_spec.js
index 4469ef1c4d5..8b88e1ff64d 100644
--- a/spec/frontend/organizations/groups_and_projects/utils_spec.js
+++ b/spec/frontend/organizations/groups_and_projects/utils_spec.js
@@ -1,7 +1,7 @@
import { formatProjects, formatGroups } from '~/organizations/groups_and_projects/utils';
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { organizationProjects, organizationGroups } from './mock_data';
+import { organizationProjects, organizationGroups } from '~/organizations/mock_data';
describe('formatProjects', () => {
it('correctly formats the projects', () => {
diff --git a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
index 4b1b7b27ad9..826fc2b2230 100644
--- a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
@@ -255,6 +255,20 @@ describe('Work item add note', () => {
expect(wrapper.emitted('error')).toEqual([[error]]);
});
+
+ it('sends confidential prop to work item comment form', async () => {
+ await createComponent({ isEditing: true, signedIn: true });
+
+ const {
+ data: {
+ workspace: {
+ workItems: { nodes },
+ },
+ },
+ } = workItemByIidResponseFactory({ canUpdate: true, canCreateNote: true });
+
+ expect(findCommentForm().props('isWorkItemConfidential')).toBe(nodes[0].confidential);
+ });
});
});
diff --git a/spec/frontend/work_items/components/notes/work_item_note_spec.js b/spec/frontend/work_items/components/notes/work_item_note_spec.js
index c5d1decfb42..9049a69656a 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_spec.js
@@ -388,6 +388,13 @@ describe('Work Item Note', () => {
});
});
+ it('confidential information on note', async () => {
+ createComponent();
+ await findNoteActions().vm.$emit('startEditing');
+ const { confidential } = workItemByIidResponseFactory().data.workspace.workItems.nodes[0];
+ expect(findCommentForm().props('isWorkItemConfidential')).toBe(confidential);
+ });
+
describe('author and user role badges', () => {
describe('author badge props', () => {
it.each`
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index d3c7c9e2074..638a92dbd17 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -586,7 +586,10 @@ describe('WorkItemDetail component', () => {
createComponent();
await waitForPromises();
+ const { confidential } = workItemQueryResponse.data.workspace.workItems.nodes[0];
+
expect(findNotesWidget().exists()).toBe(true);
+ expect(findNotesWidget().props('isWorkItemConfidential')).toBe(confidential);
});
});
diff --git a/spec/frontend/work_items/components/work_item_notes_spec.js b/spec/frontend/work_items/components/work_item_notes_spec.js
index c2821cc99f9..35f01c85ec8 100644
--- a/spec/frontend/work_items/components/work_item_notes_spec.js
+++ b/spec/frontend/work_items/components/work_item_notes_spec.js
@@ -88,6 +88,7 @@ describe('WorkItemNotes component', () => {
defaultWorkItemNotesQueryHandler = workItemNotesQueryHandler,
deleteWINoteMutationHandler = deleteWorkItemNoteMutationSuccessHandler,
isModal = false,
+ isWorkItemConfidential = false,
} = {}) => {
wrapper = shallowMount(WorkItemNotes, {
apolloProvider: createMockApollo([
@@ -106,6 +107,7 @@ describe('WorkItemNotes component', () => {
workItemType: 'task',
reportAbusePath: '/report/abuse/path',
isModal,
+ isWorkItemConfidential,
},
stubs: {
GlModal: stubComponent(GlModal, { methods: { show: showModal } }),
@@ -344,4 +346,14 @@ describe('WorkItemNotes component', () => {
});
});
});
+
+ it('passes confidential props when the work item is confidential', async () => {
+ createComponent({
+ isWorkItemConfidential: true,
+ defaultWorkItemNotesQueryHandler: workItemNotesWithCommentsQueryHandler,
+ });
+ await waitForPromises();
+
+ expect(findWorkItemCommentNoteAtIndex(0).props('isWorkItemConfidential')).toBe(true);
+ });
});
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index ad81c125055..adae96f34fa 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -203,7 +203,7 @@ RSpec.describe ApplicationHelper do
describe '#linkedin_url?' do
using RSpec::Parameterized::TableSyntax
- let(:user) { build_stubbed(:user) }
+ let(:user) { build_stubbed(:user, user_detail: build_stubbed(:user_detail)) }
subject { helper.linkedin_url(user) }
@@ -230,7 +230,7 @@ RSpec.describe ApplicationHelper do
describe '#twitter_url?' do
using RSpec::Parameterized::TableSyntax
- let(:user) { build_stubbed(:user) }
+ let(:user) { build_stubbed(:user, user_detail: build_stubbed(:user_detail)) }
subject { helper.twitter_url(user) }
diff --git a/spec/helpers/environment_helper_spec.rb b/spec/helpers/environment_helper_spec.rb
index b9316e46d9d..1383bf34881 100644
--- a/spec/helpers/environment_helper_spec.rb
+++ b/spec/helpers/environment_helper_spec.rb
@@ -22,6 +22,15 @@ RSpec.describe EnvironmentHelper, feature_category: :environment_management do
end
end
+ context 'when deploying from a bridge' do
+ it 'renders a span tag' do
+ deploy = build(:deployment, deployable: create(:ci_bridge), status: :success)
+ html = helper.render_deployment_status(deploy)
+
+ expect(html).to have_css('span.ci-status.ci-success')
+ end
+ end
+
context 'for a blocked deployment' do
subject { helper.render_deployment_status(deployment) }
diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb
index 366032100de..e75aa2e2729 100644
--- a/spec/helpers/sessions_helper_spec.rb
+++ b/spec/helpers/sessions_helper_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe SessionsHelper, feature_category: :system_access do
describe '#unconfirmed_verification_email?', :freeze_time do
using RSpec::Parameterized::TableSyntax
- let(:user) { build_stubbed(:user) }
+ let(:user) { build_stubbed(:user, user_detail: build_stubbed(:user_detail)) }
let(:token_valid_for) { ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES }
subject { helper.unconfirmed_verification_email?(user) }
@@ -101,7 +101,7 @@ RSpec.describe SessionsHelper, feature_category: :system_access do
end
describe '#verification_data' do
- let(:user) { build_stubbed(:user) }
+ let(:user) { build_stubbed(:user, user_detail: build_stubbed(:user_detail)) }
it 'returns the expected data' do
expect(helper.verification_data(user)).to eq({
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 0ed745f28fd..ded024462b5 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -219,28 +219,53 @@ RSpec.describe User, feature_category: :user_profile do
end
describe '#user_detail' do
- it 'does not persist `user_detail` by default' do
- expect(create(:user).user_detail).not_to be_persisted
+ context 'when user is not persisted' do
+ let(:user) { build(:user) }
+
+ it 'builds `user_detail`' do
+ expect(user.user_detail).to be_instance_of(UserDetail)
+ expect(user.user_detail).not_to be_persisted
+ end
end
- shared_examples 'delegated field' do |field|
- it 'creates `user_detail` when the field is given' do
- user = create(:user, field => 'my field')
+ context 'when user is persisted' do
+ let(:user) { create(:user) }
+
+ it 'creates `user_detail`' do
+ expect(UserDetail.exists?(user_id: user.id)).to eq(false)
+
+ user.user_detail
+
+ expect(UserDetail.exists?(user_id: user.id)).to eq(true)
+ expect(user.user_detail).to be_instance_of(UserDetail)
expect(user.user_detail).to be_persisted
- expect(user.user_detail[field]).to eq('my field')
end
+ end
- it 'delegates to `user_detail`' do
- user = create(:user, field => 'my field')
+ shared_examples 'delegated field' do |field, value = 'field value'|
+ context "when #{field} field" do
+ it 'creates `user_detail` when the field is given', :aggregate_failures do
+ user = create(:user, field => value)
- expect(user.public_send(field)).to eq(user.user_detail[field])
- end
+ expect(user.user_detail).to be_persisted
+ expect(user.user_detail[field]).to eq(value)
+ end
- it 'creates `user_detail` when first updated' do
- user = create(:user)
+ it 'delegates the field to `user_detail`' do
+ user = create(:user, field => value)
+
+ expect(user.public_send(field)).to eq(user.user_detail[field])
+ end
+
+ it 'creates `user_detail` when the field is first updated', :aggregate_failures do
+ user = create(:user)
- expect { user.update!(field => 'my field') }.to change { user.user_detail.persisted? }.from(false).to(true)
+ user.update!(field => value)
+
+ expect(user.user_detail).to be_persisted
+ expect(user.user_detail[field]).to eq(value)
+ end
end
end
@@ -250,36 +275,27 @@ RSpec.describe User, feature_category: :user_profile do
it_behaves_like 'delegated field', :skype
it_behaves_like 'delegated field', :location
it_behaves_like 'delegated field', :organization
+ it_behaves_like 'delegated field', :website_url, 'https://example.com'
+ it_behaves_like 'delegated field', :pronouns, 'they/them'
+ it_behaves_like 'delegated field', :pronunciation, 'uhg-zaam-pl'
- it 'creates `user_detail` when `website_url` is given' do
- user = create(:user, website_url: 'https://example.com')
-
- expect(user.user_detail).to be_persisted
- expect(user.user_detail.website_url).to eq('https://example.com')
- end
-
- it 'delegates `website_url` to `user_detail`' do
- user = create(:user, website_url: 'http://example.com')
-
- expect(user.website_url).to eq(user.user_detail.website_url)
- end
-
- it 'creates `user_detail` when `website_url` is first updated' do
- user = create(:user)
+ context 'when race condition' do
+ it 'handles it properly' do
+ user = create(:user)
+ stale_user = described_class.find(user.id)
- expect { user.update!(website_url: 'https://example.com') }.to change { user.user_detail.persisted? }.from(false).to(true)
- end
+ user.user_detail
+ stale_user.user_detail
- it 'delegates `pronouns` to `user_detail`' do
- user = create(:user, pronouns: 'they/them')
+ user.update!(bio: 'hello')
- expect(user.pronouns).to eq(user.user_detail.pronouns)
- end
+ expect { stale_user.update!(pronunciation: 'my-pronunciation') }.not_to raise_error
- it 'delegates `pronunciation` to `user_detail`' do
- user = create(:user, name: 'Example', pronunciation: 'uhg-zaam-pl')
+ user.reload
- expect(user.pronunciation).to eq(user.user_detail.pronunciation)
+ expect(user.bio).to eq('hello')
+ expect(user.pronunciation).to eq('my-pronunciation')
+ end
end
end
@@ -450,7 +466,7 @@ RSpec.describe User, feature_category: :user_profile do
describe 'validations' do
describe 'password' do
- let!(:user) { build_stubbed(:user) }
+ let!(:user) { build_stubbed(:user, user_detail: build_stubbed(:user_detail)) }
before do
allow(Devise).to receive(:password_length).and_return(8..128)
@@ -622,7 +638,7 @@ RSpec.describe User, feature_category: :user_profile do
end
context 'when username is changed' do
- let(:user) { build_stubbed(:user, username: 'old_path', namespace: build_stubbed(:user_namespace)) }
+ let(:user) { build_stubbed(:user, username: 'old_path', namespace: build_stubbed(:user_namespace), user_detail: build_stubbed(:user_detail)) }
it 'validates move_dir is allowed for the namespace' do
expect(user.namespace).to receive(:any_project_has_container_registry_tags?).and_return(true)
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index 4aba83dae92..3a475267dc1 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, :public, group: group) }
let_it_be(:current_user) { create(:user) }
- let_it_be(:reporter) { create(:user).tap { |reporter| project.add_reporter(reporter) } }
+ let_it_be(:reporter) { create(:user, :with_user_detail).tap { |reporter| project.add_reporter(reporter) } }
let_it_be(:label1) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let_it_be(:milestone1) { create(:milestone, project: project) }
@@ -410,7 +410,7 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
# TODO: Fix N+1 queries executed for the linked work item widgets
# https://gitlab.com/gitlab-org/gitlab/-/issues/420605
expect { post_graphql(query, current_user: current_user) }
- .not_to exceed_all_query_limit(control).with_threshold(11)
+ .not_to exceed_all_query_limit(control).with_threshold(13)
end
end
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index f3e5f3ab891..025f9887c62 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe API::Members, feature_category: :groups_and_projects do
get api(members_url, maintainer)
end
- project.add_developer(create(:user))
+ project.add_developer(create(:user, :with_user_detail))
expect do
get api(members_url, maintainer)
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index d3d1a2a6cd0..9140db8f81f 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -206,7 +206,7 @@ RSpec.describe 'Git HTTP requests', feature_category: :source_code_management do
end
describe "User with no identities" do
- let(:user) { create(:user) }
+ let(:user) { create(:user, :with_user_detail) }
context "when the project doesn't exist" do
context "when namespace doesn't exist" do
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index 365b20ad4aa..7815e22dc46 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe SearchController, type: :request, feature_category: :global_search do
- let_it_be(:user) { create(:user) }
+ let_it_be(:user) { create(:user, :with_user_detail) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, :repository, :wiki_repo, name: 'awesome project', group: group) }
let_it_be(:projects) { create_list(:project, 5, :public, :repository, :wiki_repo) }
@@ -44,7 +44,7 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
let(:params) { { search: 'foo', scope: 'issues' } }
# some N+1 queries still exist
# each issue runs an extra query for group namespaces
- let(:threshold) { 1 }
+ let(:threshold) { 3 }
it_behaves_like 'an efficient database result'
end
diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb
index b0f3f328a4f..53f0e0c34e1 100644
--- a/spec/serializers/deployment_entity_spec.rb
+++ b/spec/serializers/deployment_entity_spec.rb
@@ -10,8 +10,11 @@ RSpec.describe DeploymentEntity do
let_it_be(:environment) { create(:environment, project: project) }
let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project, user: user) }
let_it_be_with_reload(:build) { create(:ci_build, :manual, :environment_with_deployment_tier, pipeline: pipeline) }
+ let_it_be_with_reload(:bridge) do
+ create(:ci_bridge, :manual, :environment_with_deployment_tier, pipeline: pipeline, downstream: project)
+ end
- let_it_be_with_refind(:deployment) { create(:deployment, deployable: build, environment: environment) }
+ let!(:deployment) { create(:deployment, deployable: job, environment: environment, project: project) }
let(:request) { double('request') }
let(:entity) { described_class.new(deployment, request: request) }
@@ -28,22 +31,33 @@ RSpec.describe DeploymentEntity do
allow(request).to receive(:project).and_return(project)
end
- it 'exposes fields', :aggregate_failures do
- expect(subject).to include(:iid)
- expect(subject[:ref][:name]).to eq 'master'
- expect(subject).to include(:status)
- expect(subject).to include(:created_at)
- expect(subject).to include(:deployed_at)
- expect(subject).to include(:is_last)
- expect(subject).to include(:tier_in_yaml)
+ shared_examples_for 'exposes fields' do
+ it 'exposes fields', :aggregate_failures do
+ expect(subject).to include(:iid)
+ expect(subject[:ref][:name]).to eq 'master'
+ expect(subject).to include(:status)
+ expect(subject).to include(:created_at)
+ expect(subject).to include(:deployed_at)
+ expect(subject).to include(:is_last)
+ expect(subject).to include(:tier_in_yaml)
+ end
+ end
+
+ context 'when deployable is build job' do
+ let(:job) { build }
+
+ it_behaves_like 'exposes fields'
+ end
+
+ context 'when deployable is bridge job' do
+ let(:job) { bridge }
+
+ it_behaves_like 'exposes fields'
end
context 'when deployable is nil' do
let(:entity) { described_class.new(deployment, request: request, deployment_details: false) }
-
- before do
- deployment.update!(deployable: nil)
- end
+ let(:job) { nil }
it 'does not expose deployable entry' do
expect(subject).not_to include(:deployable)
@@ -51,15 +65,17 @@ RSpec.describe DeploymentEntity do
end
context 'when the pipeline has another manual action' do
- let_it_be(:other_build) do
- create(:ci_build, :manual, name: 'another deploy', pipeline: pipeline, environment: build.environment)
+ let!(:other_job) do
+ create(:ci_build, :manual, name: 'another deploy', pipeline: pipeline, environment: job.environment)
end
- let_it_be(:other_deployment) { create(:deployment, deployable: build, environment: environment) }
+ let!(:other_deployment) { create(:deployment, deployable: job, environment: environment) }
+
+ let(:job) { build }
it 'returns another manual action' do
- expect(subject[:manual_actions].count).to eq(1)
- expect(subject[:manual_actions].pluck(:name)).to match_array(['another deploy'])
+ expect(subject[:manual_actions].count).to eq(2)
+ expect(subject[:manual_actions].pluck(:name)).to match_array(['another deploy', 'bridge'])
end
context 'when user is a reporter' do
@@ -82,18 +98,22 @@ RSpec.describe DeploymentEntity do
end
describe 'scheduled_actions' do
- let(:build) { create(:ci_build, :success, pipeline: pipeline) }
-
- before do
- deployment.update!(deployable: build)
- end
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
context 'when the same pipeline has a scheduled action' do
- let(:other_build) { create(:ci_build, :schedulable, :success, pipeline: pipeline, name: 'other build') }
- let!(:other_deployment) { create(:deployment, deployable: other_build, environment: environment) }
+ let(:other_job) { create(:ci_build, :schedulable, :success, pipeline: pipeline, name: 'other job') }
+ let!(:other_deployment) { create(:deployment, deployable: other_job, environment: environment) }
it 'returns other scheduled actions' do
- expect(subject[:scheduled_actions][0][:name]).to eq 'other build'
+ expect(subject[:scheduled_actions][0][:name]).to eq 'other job'
+ end
+
+ context 'when deployable is bridge job' do
+ let(:job) { create(:ci_bridge, :success, pipeline: pipeline) }
+
+ it 'returns nil' do
+ expect(subject[:scheduled_actions]).to be_nil
+ end
end
end
@@ -115,10 +135,6 @@ RSpec.describe DeploymentEntity do
end
describe 'playable_build' do
- before do
- deployment.update!(deployable: job)
- end
-
context 'when the deployment has a playable deployable' do
context 'when this job is build and ready to be played' do
let(:job) { create(:ci_build, :playable, :scheduled, pipeline: pipeline) }
@@ -161,6 +177,8 @@ RSpec.describe DeploymentEntity do
described_class.new(deployment, request: request, deployment_details: false)
end
+ let(:job) { build }
+
it 'does not serialize deployment details' do
expect(subject.with_indifferent_access)
.not_to include(:commit, :manual_actions, :scheduled_actions)
@@ -172,5 +190,16 @@ RSpec.describe DeploymentEntity do
.to eq(name: 'test', build_path: path)
end
end
+
+ context 'when deployable is bridge' do
+ let(:job) { bridge }
+
+ it 'only exposes deployable name and path' do
+ project_job_path(project, deployment.deployable).tap do |path|
+ expect(subject.fetch(:deployable))
+ .to eq(name: 'bridge', build_path: path)
+ end
+ end
+ end
end
end
diff --git a/spec/services/deployments/update_environment_service_spec.rb b/spec/services/deployments/update_environment_service_spec.rb
index 0a93e300eb6..79bf0d972d4 100644
--- a/spec/services/deployments/update_environment_service_spec.rb
+++ b/spec/services/deployments/update_environment_service_spec.rb
@@ -79,6 +79,27 @@ RSpec.describe Deployments::UpdateEnvironmentService, feature_category: :continu
expect(subject.execute).to eq(deployment)
end
+ context 'when deployable is bridge job' do
+ let(:job) do
+ create(:ci_bridge,
+ :with_deployment,
+ pipeline: pipeline,
+ ref: 'master',
+ tag: false,
+ environment: environment_name,
+ options: { environment: options },
+ project: project)
+ end
+
+ it 'creates ref' do
+ expect_any_instance_of(Repository)
+ .to receive(:create_ref)
+ .with(deployment.sha, "refs/environments/production/deployments/#{deployment.iid}")
+
+ service.execute
+ end
+ end
+
context 'when start action is defined' do
let(:options) { { name: 'production', action: 'start' } }
diff --git a/spec/support/shared_examples/ci/deployable_shared_examples.rb b/spec/support/shared_examples/ci/deployable_shared_examples.rb
index b51a8fa20e2..6f56d9dae11 100644
--- a/spec/support/shared_examples/ci/deployable_shared_examples.rb
+++ b/spec/support/shared_examples/ci/deployable_shared_examples.rb
@@ -96,7 +96,7 @@ RSpec.shared_examples 'a deployable job' do
ActiveRecord::QueryRecorder.new { subject }
end
- index_for_build = recorded.log.index { |l| l.include?("UPDATE #{Ci::Build.quoted_table_name}") }
+ index_for_build = recorded.log.index { |l| l.include?("UPDATE #{described_class.quoted_table_name}") }
index_for_deployment = recorded.log.index { |l| l.include?("UPDATE \"deployments\"") }
expect(index_for_build).to be < index_for_deployment
@@ -259,7 +259,7 @@ RSpec.shared_examples 'a deployable job' do
describe '#environment_tier_from_options' do
subject { job.environment_tier_from_options }
- let(:job) { Ci::Build.new(options: options) }
+ let(:job) { described_class.new(options: options) }
let(:options) { { environment: { deployment_tier: 'production' } } }
it { is_expected.to eq('production') }
@@ -276,7 +276,7 @@ RSpec.shared_examples 'a deployable job' do
let(:options) { { environment: { deployment_tier: 'production' } } }
let!(:environment) { create(:environment, name: 'production', tier: 'development', project: project) }
- let(:job) { Ci::Build.new(options: options, environment: 'production', project: project) }
+ let(:job) { described_class.new(options: options, environment: 'production', project: project) }
it { is_expected.to eq('production') }
@@ -536,10 +536,6 @@ RSpec.shared_examples 'a deployable job' do
end
describe '#deployment_status' do
- before do
- allow_any_instance_of(Ci::Build).to receive(:create_deployment) # rubocop:disable RSpec/AnyInstanceOf
- end
-
context 'when job is a last deployment' do
let(:job) { create(factory_type, :success, environment: 'production', pipeline: pipeline) }
let(:environment) { create(:environment, name: 'production', project: job.project) }