diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-22 12:10:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-22 12:10:46 +0300 |
commit | 2e9f877e8b6dd58c8011745b1d9a28dd67c8179c (patch) | |
tree | 0e2d42aa2ef1c3ae386b54962815623466a3dd54 | |
parent | c86c0e01469fd7c9aa72283db23d539c02f44bdb (diff) |
Add latest changes from gitlab-org/gitlab@master
63 files changed, 398 insertions, 708 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index a4a74aa4100..b9f52ebc055 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -18,6 +18,7 @@ inherit_from: inherit_mode: merge: - Include + - Exclude AllCops: TargetRubyVersion: 2.7 @@ -597,3 +598,14 @@ FactoryBot/InlineAssociation: Include: - 'spec/factories/**/*.rb' - 'ee/spec/factories/**/*.rb' + +# WIP: https://gitlab.com/gitlab-org/gitlab/-/issues/321982 +Gitlab/NamespacedClass: + Exclude: + - 'config/**/*.rb' + - 'db/**/*.rb' + - 'ee/bin/**/*' + - 'ee/db/**/*.rb' + - 'ee/elastic/**/*.rb' + - 'scripts/**/*' + - 'spec/migrations/**/*.rb' diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 8b43895de57..6c1af1a5455 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -10,6 +10,7 @@ # - guidelines for use found in # https://docs.gitlab.com/ee/development/contributing/style_guides.html#resolving-rubocop-exceptions. +# WIP See https://gitlab.com/gitlab-org/gitlab/-/issues/267606 FactoryBot/InlineAssociation: Exclude: - 'ee/spec/factories/analytics/cycle_analytics/group_stages.rb' @@ -28,6 +29,7 @@ FactoryBot/InlineAssociation: - 'spec/factories/uploads.rb' - 'spec/factories/wiki_pages.rb' +# WIP: See https://gitlab.com/gitlab-org/gitlab/-/issues/220040 Rails/SaveBang: Exclude: - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb' @@ -1174,22 +1176,9 @@ RSpec/AnyInstanceOf: - 'spec/workers/wait_for_cluster_creation_worker_spec.rb' - 'ee/spec/workers/security/auto_fix_worker_spec.rb' +# WIP: https://gitlab.com/gitlab-org/gitlab/-/issues/321982 Gitlab/NamespacedClass: Exclude: - - 'config/**/*.rb' - - 'db/**/*.rb' - - 'ee/bin/**/*' - - 'ee/db/**/*.rb' - - 'ee/elastic/**/*.rb' - - 'scripts/**/*' - - 'spec/migrations/**/*.rb' - # The list above represents the permanent exclusions for this rule - # due to the fact these files are related to infrastructure code. - # This list should eventually be moved to .rubocop.yml after all TODOs - # are addressed. - # - # The list below represents the classes that require - # a namespace as they make the domain related code. - 'app/channels/issues_channel.rb' - 'app/controllers/abuse_reports_controller.rb' - 'app/controllers/acme_challenges_controller.rb' diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index 13ad820477f..cf7e8cb94d1 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -36,11 +36,11 @@ export function formatIssue(issue) { } export function formatListIssues(listIssues) { - const issues = {}; - let listIssuesCount; + const boardItems = {}; + let listItemsCount; const listData = listIssues.nodes.reduce((map, list) => { - listIssuesCount = list.issues.count; + listItemsCount = list.issues.count; let sortedIssues = list.issues.edges.map((issueNode) => ({ ...issueNode.node, })); @@ -58,14 +58,14 @@ export function formatListIssues(listIssues) { assignees: i.assignees?.nodes || [], }; - issues[id] = listIssue; + boardItems[id] = listIssue; return id; }), }; }, {}); - return { listData, issues, listIssuesCount }; + return { listData, boardItems, listItemsCount }; } export function formatListsPageInfo(lists) { diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue index 7c32626a4ce..95a90d7ab11 100644 --- a/app/assets/javascripts/boards/components/board_column.vue +++ b/app/assets/javascripts/boards/components/board_column.vue @@ -32,12 +32,12 @@ export default { }, computed: { ...mapState(['filterParams', 'highlightedLists']), - ...mapGetters(['getIssuesByList']), + ...mapGetters(['getBoardItemsByList']), highlighted() { return this.highlightedLists.includes(this.list.id); }, - listIssues() { - return this.getIssuesByList(this.list.id); + listItems() { + return this.getBoardItemsByList(this.list.id); }, isListDraggable() { return isListDraggable(this.list); @@ -87,7 +87,7 @@ export default { <board-list ref="board-list" :disabled="disabled" - :issues="listIssues" + :board-items="listItems" :list="list" :can-admin-list="canAdminList" /> diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 0142b0f4e31..4bd5a530b8c 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -12,8 +12,8 @@ import BoardNewIssue from './board_new_issue.vue'; export default { name: 'BoardList', i18n: { - loadingIssues: __('Loading issues'), - loadingMoreissues: __('Loading more issues'), + loading: __('Loading'), + loadingMoreboardItems: __('Loading more'), showingAllIssues: __('Showing all issues'), }, components: { @@ -30,7 +30,7 @@ export default { type: Object, required: true, }, - issues: { + boardItems: { type: Array, required: true, }, @@ -51,11 +51,11 @@ export default { ...mapState(['pageInfoByListId', 'listsFlags']), paginatedIssueText() { return sprintf(__('Showing %{pageSize} of %{total} issues'), { - pageSize: this.issues.length, + pageSize: this.boardItems.length, total: this.list.issuesCount, }); }, - issuesSizeExceedsMax() { + boardItemsSizeExceedsMax() { return this.list.maxIssueCount > 0 && this.list.issuesCount > this.list.maxIssueCount; }, hasNextPage() { @@ -72,7 +72,7 @@ export default { return this.canAdminList ? this.$refs.list.$el : this.$refs.list; }, showingAllIssues() { - return this.issues.length === this.list.issuesCount; + return this.boardItems.length === this.list.issuesCount; }, treeRootWrapper() { return this.canAdminList ? Draggable : 'ul'; @@ -85,14 +85,14 @@ export default { tag: 'ul', 'ghost-class': 'board-card-drag-active', 'data-list-id': this.list.id, - value: this.issues, + value: this.boardItems, }; return this.canAdminList ? options : {}; }, }, watch: { - issues() { + boardItems() { this.$nextTick(() => { this.showCount = this.scrollHeight() > Math.ceil(this.listHeight()); }); @@ -201,7 +201,7 @@ export default { <div v-if="loading" class="gl-mt-4 gl-text-center" - :aria-label="$options.i18n.loadingIssues" + :aria-label="$options.i18n.loading" data-testid="board_list_loading" > <gl-loading-icon /> @@ -214,25 +214,25 @@ export default { v-bind="treeRootOptions" :data-board="list.id" :data-board-type="list.listType" - :class="{ 'bg-danger-100': issuesSizeExceedsMax }" + :class="{ 'bg-danger-100': boardItemsSizeExceedsMax }" class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list" data-testid="tree-root-wrapper" @start="handleDragOnStart" @end="handleDragOnEnd" > <board-card - v-for="(issue, index) in issues" + v-for="(item, index) in boardItems" ref="issue" - :key="issue.id" + :key="item.id" :index="index" :list="list" - :issue="issue" + :issue="item" :disabled="disabled" /> <li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1"> <gl-loading-icon v-if="loadingMore" - :label="$options.i18n.loadingMoreissues" + :label="$options.i18n.loadingMoreboardItems" data-testid="count-loading-icon" /> <span v-if="showingAllIssues">{{ $options.i18n.showingAllIssues }}</span> diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 9cb8d664e70..b8d84899782 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -289,9 +289,9 @@ export default { }) .then(({ data }) => { const { lists } = data[boardType]?.board; - const listIssues = formatListIssues(lists); + const listItems = formatListIssues(lists); const listPageInfo = formatListsPageInfo(lists); - commit(types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, { listIssues, listPageInfo, listId }); + commit(types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, { listItems, listPageInfo, listId }); }) .catch(() => commit(types.RECEIVE_ITEMS_FOR_LIST_FAILURE, listId)); }, @@ -304,8 +304,8 @@ export default { { state, commit }, { issueId, issueIid, issuePath, fromListId, toListId, moveBeforeId, moveAfterId }, ) => { - const originalIssue = state.issues[issueId]; - const fromList = state.issuesByListId[fromListId]; + const originalIssue = state.boardItems[issueId]; + const fromList = state.boardItemsByListId[fromListId]; const originalIndex = fromList.indexOf(Number(issueId)); commit(types.MOVE_ISSUE, { originalIssue, fromListId, toListId, moveBeforeId, moveAfterId }); diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js index cab97088bc6..308dbd0f1b0 100644 --- a/app/assets/javascripts/boards/stores/getters.js +++ b/app/assets/javascripts/boards/stores/getters.js @@ -4,17 +4,17 @@ import { inactiveId } from '../constants'; export default { isSidebarOpen: (state) => state.activeId !== inactiveId, isSwimlanesOn: () => false, - getIssueById: (state) => (id) => { - return state.issues[id] || {}; + getBoardItemById: (state) => (id) => { + return state.boardItems[id] || {}; }, - getIssuesByList: (state, getters) => (listId) => { - const listIssueIds = state.issuesByListId[listId] || []; - return listIssueIds.map((id) => getters.getIssueById(id)); + getBoardItemsByList: (state, getters) => (listId) => { + const listItemsIds = state.boardItemsByListId[listId] || []; + return listItemsIds.map((id) => getters.getBoardItemById(id)); }, activeIssue: (state) => { - return state.issues[state.activeId] || {}; + return state.boardItems[state.activeId] || {}; }, groupPathForActiveIssue: (_, getters) => { diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 12e96e7c9eb..8246ed8eb09 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -11,13 +11,13 @@ const notImplemented = () => { }; export const removeIssueFromList = ({ state, listId, issueId }) => { - Vue.set(state.issuesByListId, listId, pull(state.issuesByListId[listId], issueId)); + Vue.set(state.boardItemsByListId, listId, pull(state.boardItemsByListId[listId], issueId)); const list = state.boardLists[listId]; Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount - 1 }); }; export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfterId, atIndex }) => { - const listIssues = state.issuesByListId[listId]; + const listIssues = state.boardItemsByListId[listId]; let newIndex = atIndex || 0; if (moveBeforeId) { newIndex = listIssues.indexOf(moveBeforeId) + 1; @@ -25,7 +25,7 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter newIndex = listIssues.indexOf(moveAfterId); } listIssues.splice(newIndex, 0, issueId); - Vue.set(state.issuesByListId, listId, listIssues); + Vue.set(state.boardItemsByListId, listId, listIssues); const list = state.boardLists[listId]; Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount + 1 }); }; @@ -108,14 +108,13 @@ export default { Vue.set(state.listsFlags, listId, { [fetchNext ? 'isLoadingMore' : 'isLoading']: true }); }, - [mutationTypes.RECEIVE_ITEMS_FOR_LIST_SUCCESS]: (state, { listIssues, listPageInfo, listId }) => { - const { listData, issues } = listIssues; - Vue.set(state, 'issues', { ...state.issues, ...issues }); - + [mutationTypes.RECEIVE_ITEMS_FOR_LIST_SUCCESS]: (state, { listItems, listPageInfo, listId }) => { + const { listData, boardItems } = listItems; + Vue.set(state, 'boardItems', { ...state.boardItems, ...boardItems }); Vue.set( - state.issuesByListId, + state.boardItemsByListId, listId, - union(state.issuesByListId[listId] || [], listData[listId]), + union(state.boardItemsByListId[listId] || [], listData[listId]), ); Vue.set(state.pageInfoByListId, listId, listPageInfo[listId]); Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false }); @@ -129,18 +128,18 @@ export default { }, [mutationTypes.RESET_ISSUES]: (state) => { - Object.keys(state.issuesByListId).forEach((listId) => { - Vue.set(state.issuesByListId, listId, []); + Object.keys(state.boardItemsByListId).forEach((listId) => { + Vue.set(state.boardItemsByListId, listId, []); }); }, [mutationTypes.UPDATE_ISSUE_BY_ID]: (state, { issueId, prop, value }) => { - if (!state.issues[issueId]) { + if (!state.boardItems[issueId]) { /* eslint-disable-next-line @gitlab/require-i18n-strings */ throw new Error('No issue found.'); } - Vue.set(state.issues[issueId], prop, value); + Vue.set(state.boardItems[issueId], prop, value); }, [mutationTypes.SET_ASSIGNEE_LOADING](state, isLoading) { @@ -167,7 +166,7 @@ export default { const toList = state.boardLists[toListId]; const issue = moveIssueListHelper(originalIssue, fromList, toList); - Vue.set(state.issues, issue.id, issue); + Vue.set(state.boardItems, issue.id, issue); removeIssueFromList({ state, listId: fromListId, issueId: issue.id }); addIssueToList({ state, listId: toListId, issueId: issue.id, moveBeforeId, moveAfterId }); @@ -175,7 +174,7 @@ export default { [mutationTypes.MOVE_ISSUE_SUCCESS]: (state, { issue }) => { const issueId = getIdFromGraphQLId(issue.id); - Vue.set(state.issues, issueId, formatIssue({ ...issue, id: issueId })); + Vue.set(state.boardItems, issueId, formatIssue({ ...issue, id: issueId })); }, [mutationTypes.MOVE_ISSUE_FAILURE]: ( @@ -183,7 +182,7 @@ export default { { originalIssue, fromListId, toListId, originalIndex }, ) => { state.error = s__('Boards|An error occurred while moving the issue. Please try again.'); - Vue.set(state.issues, originalIssue.id, originalIssue); + Vue.set(state.boardItems, originalIssue.id, originalIssue); removeIssueFromList({ state, listId: toListId, issueId: originalIssue.id }); addIssueToList({ state, @@ -216,7 +215,7 @@ export default { issueId: issue.id, atIndex: position, }); - Vue.set(state.issues, issue.id, issue); + Vue.set(state.boardItems, issue.id, issue); }, [mutationTypes.ADD_ISSUE_TO_LIST_FAILURE]: (state, { list, issueId }) => { @@ -226,7 +225,7 @@ export default { [mutationTypes.REMOVE_ISSUE_FROM_LIST]: (state, { list, issue }) => { removeIssueFromList({ state, listId: list.id, issueId: issue.id }); - Vue.delete(state.issues, issue.id); + Vue.delete(state.boardItems, issue.id); }, [mutationTypes.SET_CURRENT_PAGE]: () => { diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index 1b4b8dd5c10..85d92589d30 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -9,10 +9,10 @@ export default () => ({ sidebarType: '', boardLists: {}, listsFlags: {}, - issuesByListId: {}, + boardItemsByListId: {}, isSettingAssignees: false, pageInfoByListId: {}, - issues: {}, + boardItems: {}, filterParams: {}, boardConfig: {}, labels: [], diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 4bf9236407f..089f130843f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -443,7 +443,7 @@ $gl-avatar-size: 40px; $border-radius-default: 4px; $border-radius-small: 2px; $border-radius-large: 8px; -$default-icon-size: 18px; +$default-icon-size: 16px; $layout-link-gray: #7e7c7c; $btn-side-margin: 10px; $btn-sm-side-margin: 7px; diff --git a/app/assets/stylesheets/page_bundles/signup.scss b/app/assets/stylesheets/page_bundles/signup.scss index a207c10b04f..d6c3a3ff5da 100644 --- a/app/assets/stylesheets/page_bundles/signup.scss +++ b/app/assets/stylesheets/page_bundles/signup.scss @@ -32,11 +32,6 @@ @include media-breakpoint-down(md) { width: 100%; } - - img { - width: $default-icon-size; - height: $default-icon-size; - } } .decline-page { diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 019d827798c..2d04354a99d 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -106,17 +106,6 @@ width: 100%; } } - - .omniauth-btn { - width: 100%; - padding: $gl-padding-8; - - img { - width: $default-icon-size; - height: $default-icon-size; - margin-right: $gl-padding; - } - } } .new-session-tabs { diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss index 4b88b94f3a6..6b78abdb5e0 100644 --- a/app/assets/stylesheets/startup/startup-signin.scss +++ b/app/assets/stylesheets/startup/startup-signin.scss @@ -2142,11 +2142,6 @@ table.code { width: 100%; padding: 8px; } -.login-page .omniauth-container .omniauth-btn img { - width: 1.125rem; - height: 1.125rem; - margin-right: 16px; -} .login-page .new-session-tabs { display: flex; box-shadow: 0 0 0 1px #dbdbdb; diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 59b14bbb91d..47c0bb6979d 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -13,7 +13,7 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action do - push_frontend_feature_flag(:pipelines_security_report_summary, project) + push_frontend_feature_flag(:pipelines_security_report_summary, project, default_enabled: :yaml) push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: true) push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index ad995a6ce68..305b9a1faf3 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -138,11 +138,11 @@ module AuthHelper label = label_for_provider(provider) if provider_has_custom_icon?(provider) - image_tag(icon_for_provider(provider), alt: label, title: "Sign in with #{label}") + image_tag(icon_for_provider(provider), alt: label, title: "Sign in with #{label}", class: "gl-button-icon") elsif provider_has_builtin_icon?(provider) file_name = "#{provider.to_s.split('_').first}_#{size}.png" - image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}") + image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}", class: "gl-button-icon") else label end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b287c1dad0d..8d558098d94 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -376,8 +376,7 @@ class MergeRequest < ApplicationRecord alias_attribute :auto_merge_enabled, :merge_when_pipeline_succeeds alias_method :issuing_parent, :target_project - delegate :active?, :builds_with_coverage, to: :head_pipeline, prefix: true, allow_nil: true - delegate :success?, :active?, to: :actual_head_pipeline, prefix: true, allow_nil: true + delegate :builds_with_coverage, to: :head_pipeline, prefix: true, allow_nil: true RebaseLockTimeout = Class.new(StandardError) @@ -437,6 +436,18 @@ class MergeRequest < ApplicationRecord target_project.latest_pipeline(target_branch, sha) end + def head_pipeline_active? + !!head_pipeline&.active? + end + + def actual_head_pipeline_active? + !!actual_head_pipeline&.active? + end + + def actual_head_pipeline_success? + !!actual_head_pipeline&.success? + end + # Pattern used to extract `!123` merge request references from text # # This pattern supports cross-project references. diff --git a/app/models/snippet.rb b/app/models/snippet.rb index ab8782ed87f..b68e166af48 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -216,8 +216,10 @@ class Snippet < ApplicationRecord def blobs return [] unless repository_exists? - branch = default_branch - list_files(branch).map { |file| Blob.lazy(repository, branch, file) } + files = list_files(default_branch) + items = files.map { |file| [default_branch, file] } + + repository.blobs_at(items).compact end def hook_attrs diff --git a/app/models/snippet_repository.rb b/app/models/snippet_repository.rb index 0219ddc45d5..54dbc579d54 100644 --- a/app/models/snippet_repository.rb +++ b/app/models/snippet_repository.rb @@ -115,10 +115,8 @@ class SnippetRepository < ApplicationRecord end def invalid_path_error?(err) - (err.is_a?(Gitlab::Git::Index::IndexError) && - err.message.downcase.start_with?('invalid path', 'path cannot include directory traversal')) || - (err.is_a?(Gitlab::Git::CommandError) && - err.message.include?('CreateFile: invalid path')) + err.is_a?(Gitlab::Git::Index::IndexError) && + err.message.downcase.start_with?('invalid path', 'path cannot include directory traversal') end def invalid_signature_error?(err) diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 270652483b7..98af69d43b7 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -20,4 +20,4 @@ = recaptcha_tags .submit-container.move-submit-down - = f.submit _('Sign in'), class: 'gl-button btn btn-success', data: { qa_selector: 'sign_in_button' } + = f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' } diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index 705fd9bbd0f..3ec859551ca 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -7,7 +7,7 @@ .d-flex.justify-content-between.flex-wrap - providers.each do |provider| - has_icon = provider_has_icon?(provider) - = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default d-flex align-items-center omniauth-btn text-left oauth-login #{qa_class_for_provider(provider)}" do + = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default omniauth-btn oauth-login #{qa_class_for_provider(provider)}" do - if has_icon = provider_image_tag(provider) %span diff --git a/changelogs/unreleased/273292-fy21q4-foundations-kr2-audit-and-update-buttons-on-sessionscontrol.yml b/changelogs/unreleased/273292-fy21q4-foundations-kr2-audit-and-update-buttons-on-sessionscontrol.yml new file mode 100644 index 00000000000..c6f180481bd --- /dev/null +++ b/changelogs/unreleased/273292-fy21q4-foundations-kr2-audit-and-update-buttons-on-sessionscontrol.yml @@ -0,0 +1,6 @@ +--- +title: Update the Sign In button to use the new confirm button variant, migrate OAuth + buttons to use the default variant of GlButton. +merge_request: 53254 +author: +type: other diff --git a/changelogs/unreleased/321834-fj-remove-nil-values-from-snippet-blobs.yml b/changelogs/unreleased/321834-fj-remove-nil-values-from-snippet-blobs.yml new file mode 100644 index 00000000000..ffbe850246e --- /dev/null +++ b/changelogs/unreleased/321834-fj-remove-nil-values-from-snippet-blobs.yml @@ -0,0 +1,5 @@ +--- +title: Fix bug when snippet blobs array contain a nil value +merge_request: 54552 +author: +type: fixed diff --git a/changelogs/unreleased/322096-fj-fix-regression-in-snippet-background-migration.yml b/changelogs/unreleased/322096-fj-fix-regression-in-snippet-background-migration.yml deleted file mode 100644 index 82ab6906348..00000000000 --- a/changelogs/unreleased/322096-fj-fix-regression-in-snippet-background-migration.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix snippet commit bug because of different Gitaly error -merge_request: 54662 -author: -type: fixed diff --git a/changelogs/unreleased/kassio-bulkimports-import-group-label-timestamps.yml b/changelogs/unreleased/kassio-bulkimports-import-group-label-timestamps.yml new file mode 100644 index 00000000000..6abf01ddfeb --- /dev/null +++ b/changelogs/unreleased/kassio-bulkimports-import-group-label-timestamps.yml @@ -0,0 +1,5 @@ +--- +title: 'BulkImports: Import Label timestamps' +merge_request: 54678 +author: +type: changed diff --git a/changelogs/unreleased/lm-fix-authorization-lint.yml b/changelogs/unreleased/lm-fix-authorization-lint.yml new file mode 100644 index 00000000000..cca59b7b43e --- /dev/null +++ b/changelogs/unreleased/lm-fix-authorization-lint.yml @@ -0,0 +1,5 @@ +--- +title: Updates authorization for linting endpoint +merge_request: 54492 +author: +type: changed diff --git a/config/feature_flags/development/pipelines_security_report_summary.yml b/config/feature_flags/development/pipelines_security_report_summary.yml index 7b67212269c..d01d08c9042 100644 --- a/config/feature_flags/development/pipelines_security_report_summary.yml +++ b/config/feature_flags/development/pipelines_security_report_summary.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235943 milestone: '13.0' type: development group: group::dynamic analysis -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/ops/marginalia.yml b/config/feature_flags/ops/marginalia.yml deleted file mode 100644 index fb82f274eb2..00000000000 --- a/config/feature_flags/ops/marginalia.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: marginalia -introduced_by_url: -rollout_issue_url: -milestone: -type: ops -group: -default_enabled: false diff --git a/config/initializers/0_marginalia.rb b/config/initializers/0_marginalia.rb index 952dd75886d..05893f29de9 100644 --- a/config/initializers/0_marginalia.rb +++ b/config/initializers/0_marginalia.rb @@ -4,11 +4,6 @@ require 'marginalia' ::Marginalia::Comment.extend(::Gitlab::Marginalia::Comment) -# Patch to modify 'Marginalia::ActiveRecordInstrumentation.annotate_sql' method with feature check. -# Orignal Marginalia::ActiveRecordInstrumentation is included to ActiveRecord::ConnectionAdapters::PostgreSQLAdapter in the Marginalia Railtie. -# Refer: https://github.com/basecamp/marginalia/blob/v1.8.0/lib/marginalia/railtie.rb#L67 -ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Marginalia::ActiveRecordInstrumentation) - # By default, PostgreSQL only tracks the first 1024 bytes of a SQL # query. Prepending the comment allows us to trace the source of the # query without having to increase the `track_activity_query_size` @@ -25,5 +20,3 @@ Marginalia::Comment.components << :line if Rails.env.development? Gitlab::Marginalia.set_application_name Gitlab::Marginalia.enable_sidekiq_instrumentation - -Gitlab::Marginalia.set_enabled_from_feature_flag diff --git a/danger/changes_size/Dangerfile b/danger/changes_size/Dangerfile index f37632ced33..52e6cb65d04 100644 --- a/danger/changes_size/Dangerfile +++ b/danger/changes_size/Dangerfile @@ -13,7 +13,7 @@ # end if git.lines_of_code > 2_000 - warn "This merge request is definitely too big (more than #{git.lines_of_code} lines changed), please split it into multiple merge requests." + warn "This merge request is definitely too big (#{git.lines_of_code} lines changed), please split it into multiple merge requests." elsif git.lines_of_code > 500 - warn "This merge request is quite big (more than #{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests." + warn "This merge request is quite big (#{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests." end diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md index f54886469f2..db1d01383ad 100644 --- a/doc/development/contributing/style_guides.md +++ b/doc/development/contributing/style_guides.md @@ -161,6 +161,9 @@ To reveal existing RuboCop exceptions in the code that have been excluded via `. This allows you to reveal existing RuboCop exceptions during your daily work cycle and fix them along the way. +NOTE: +Permanent `Exclude`s should be defined in `.rubocop.yml` instead of `.rubocop_manual_todo.yml`. + ## Database migrations See the dedicated [Database Migrations Style Guide](../migration_style_guide.md). diff --git a/doc/development/database_query_comments.md b/doc/development/database_query_comments.md index 409acbd8449..39b14074073 100644 --- a/doc/development/database_query_comments.md +++ b/doc/development/database_query_comments.md @@ -47,16 +47,3 @@ Examples of queries with comments as observed in `development.log`: ```sql /*application:sidekiq,jid:e7d6668a39a991e323009833,job_class:ExpireJobCacheWorker,correlation_id:rYF4mey9CH3,line:/app/workers/expire_job_cache_worker.rb:14:in `perform'*/ SELECT "ci_pipelines".* FROM "ci_pipelines" WHERE "ci_pipelines"."id" = $1 LIMIT $2 [["id", 64], ["LIMIT", 1]] ``` - -## Enable/Disable the feature - -Enabling or disabling the feature requires a **restart/SIGHUP** of the Web and -Sidekiq workers, as the feature flag's state is memoized upon starting up. - -The `feature_flag` for this feature is **disabled** by default. You can enable -or disable it with: - -```ruby -Feature.enable(:marginalia) -Feature.disable(:marginalia) -``` diff --git a/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png b/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png Binary files differnew file mode 100644 index 00000000000..8adb58b8143 --- /dev/null +++ b/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md index 64b386d7828..96d584737e2 100644 --- a/doc/user/application_security/security_dashboard/index.md +++ b/doc/user/application_security/security_dashboard/index.md @@ -52,7 +52,7 @@ To use the security dashboards and vulnerability reports: At the pipeline level, the Security section displays the vulnerabilities present in the branch of the project the pipeline ran against. -![Pipeline Security Dashboard](img/pipeline_security_dashboard_v13_3.png) +![Pipeline Security Dashboard](img/pipeline_security_dashboard_v13_10.png) Visit the page for any pipeline that ran any of the [supported reports](#supported-reports). To view the pipeline's security findings, select the **Security** tab when viewing the pipeline. diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index 762b08bacd6..7f02c7b169f 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -24,7 +24,12 @@ The following resources are migrated to the target instance: - description - attributes - subgroups -- Labels ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429)) +- Group Labels ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429)) + - title + - description + - color + - created_at ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300007)) + - updated_at ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300007)) - Members ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415)) Group members are associated with the imported group if: - The user already exists in the target GitLab instance and diff --git a/lib/api/lint.rb b/lib/api/lint.rb index 2d30754a36d..e0806674c6a 100644 --- a/lib/api/lint.rb +++ b/lib/api/lint.rb @@ -11,7 +11,7 @@ module API optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response' end post '/lint' do - unauthorized! unless Gitlab::CurrentSettings.signup_enabled? && current_user + unauthorized! if Gitlab::CurrentSettings.signup_disabled? && current_user.nil? result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute diff --git a/lib/bulk_imports/groups/graphql/get_labels_query.rb b/lib/bulk_imports/groups/graphql/get_labels_query.rb index fd450dce599..23efbc33581 100644 --- a/lib/bulk_imports/groups/graphql/get_labels_query.rb +++ b/lib/bulk_imports/groups/graphql/get_labels_query.rb @@ -19,6 +19,8 @@ module BulkImports title description color + created_at: createdAt + updated_at: updatedAt } } } diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 0bf41f9dc0d..55f381fcb64 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -3,6 +3,10 @@ module Gitlab module CurrentSettings class << self + def signup_disabled? + !signup_enabled? + end + def current_application_settings Gitlab::SafeRequestStore.fetch(:current_application_settings) { ensure_application_settings! } end diff --git a/lib/gitlab/marginalia.rb b/lib/gitlab/marginalia.rb index 325a8c5c325..c99cf113638 100644 --- a/lib/gitlab/marginalia.rb +++ b/lib/gitlab/marginalia.rb @@ -2,8 +2,6 @@ module Gitlab module Marginalia - cattr_accessor :enabled, default: false - def self.set_application_name ::Marginalia.application_name = Gitlab.process_name end @@ -13,12 +11,5 @@ module Gitlab ::Marginalia::SidekiqInstrumentation.enable! end end - - def self.set_enabled_from_feature_flag - # During db:create and db:bootstrap skip feature query as DB is not available yet. - return false unless Gitlab::Database.cached_table_exists?('features') - - self.enabled = Feature.enabled?(:marginalia, type: :ops) - end end end diff --git a/lib/gitlab/marginalia/active_record_instrumentation.rb b/lib/gitlab/marginalia/active_record_instrumentation.rb deleted file mode 100644 index 452f472bf6a..00000000000 --- a/lib/gitlab/marginalia/active_record_instrumentation.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# Patch to annotate sql only when the feature is enabled. -module Gitlab - module Marginalia - module ActiveRecordInstrumentation - def annotate_sql(sql) - Gitlab::Marginalia.enabled ? super(sql) : sql - end - end - end -end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d55571ab580..a231b2f2fbf 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4808,6 +4808,9 @@ msgstr "" msgid "Boards|An error occurred while fetching issues. Please reload the page." msgstr "" +msgid "Boards|An error occurred while fetching the board epics. Please reload the page." +msgstr "" + msgid "Boards|An error occurred while fetching the board issues. Please reload the page." msgstr "" @@ -17897,7 +17900,7 @@ msgstr "" msgid "Loading issues" msgstr "" -msgid "Loading more issues" +msgid "Loading more" msgstr "" msgid "Loading snippet" diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index f9221c5a4ef..793ffbbfad9 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -207,14 +207,14 @@ RSpec.describe Projects::SnippetsController do subject expect(assigns(:snippet)).to eq(project_snippet) - expect(assigns(:blobs)).to eq(project_snippet.blobs) + expect(assigns(:blobs).map(&:name)).to eq(project_snippet.blobs.map(&:name)) expect(response).to have_gitlab_http_status(:ok) end it 'does not show the blobs expanded by default' do subject - expect(project_snippet.blobs.map(&:expanded?)).to be_all(false) + expect(assigns(:blobs).map(&:expanded?)).to be_all(false) end context 'when param expanded is set' do @@ -223,7 +223,7 @@ RSpec.describe Projects::SnippetsController do it 'shows all blobs expanded' do subject - expect(project_snippet.blobs.map(&:expanded?)).to be_all(true) + expect(assigns(:blobs).map(&:expanded?)).to be_all(true) end end end diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js index ef5cc4c3cd1..ca9aa673359 100644 --- a/spec/frontend/boards/board_list_spec.js +++ b/spec/frontend/boards/board_list_spec.js @@ -29,8 +29,8 @@ const createComponent = ({ state = {}, } = {}) => { const store = createStore({ - issuesByListId: mockIssuesByListId, - issues, + boardItemsByListId: mockIssuesByListId, + boardItems: issues, pageInfoByListId: { 'gid://gitlab/List/1': { hasNextPage: true }, 'gid://gitlab/List/2': {}, @@ -65,7 +65,7 @@ const createComponent = ({ propsData: { disabled: false, list, - issues: [issue], + boardItems: [issue], canAdminList: true, ...componentProps, }, diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js index 7838b5a0b2f..8fd178a0856 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_due_date_spec.js @@ -24,7 +24,7 @@ describe('~/boards/components/sidebar/board_sidebar_due_date.vue', () => { const createWrapper = ({ dueDate = null } = {}) => { store = createStore(); - store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, dueDate } }; + store.state.boardItems = { [TEST_ISSUE.id]: { ...TEST_ISSUE, dueDate } }; store.state.activeId = TEST_ISSUE.id; wrapper = shallowMount(BoardSidebarDueDate, { @@ -61,7 +61,7 @@ describe('~/boards/components/sidebar/board_sidebar_due_date.vue', () => { createWrapper(); jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { - store.state.issues[TEST_ISSUE.id].dueDate = TEST_DUE_DATE; + store.state.boardItems[TEST_ISSUE.id].dueDate = TEST_DUE_DATE; }); findDatePicker().vm.$emit('input', TEST_PARSED_DATE); await wrapper.vm.$nextTick(); @@ -86,7 +86,7 @@ describe('~/boards/components/sidebar/board_sidebar_due_date.vue', () => { createWrapper(); jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { - store.state.issues[TEST_ISSUE.id].dueDate = null; + store.state.boardItems[TEST_ISSUE.id].dueDate = null; }); findDatePicker().vm.$emit('clear'); await wrapper.vm.$nextTick(); @@ -104,7 +104,7 @@ describe('~/boards/components/sidebar/board_sidebar_due_date.vue', () => { createWrapper({ dueDate: TEST_DUE_DATE }); jest.spyOn(wrapper.vm, 'setActiveIssueDueDate').mockImplementation(() => { - store.state.issues[TEST_ISSUE.id].dueDate = null; + store.state.boardItems[TEST_ISSUE.id].dueDate = null; }); findResetButton().vm.$emit('click'); await wrapper.vm.$nextTick(); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_issue_title_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_issue_title_spec.js index bc7df1c76c6..723d0345f76 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_issue_title_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_issue_title_spec.js @@ -34,7 +34,7 @@ describe('~/boards/components/sidebar/board_sidebar_issue_title.vue', () => { const createWrapper = (issue = TEST_ISSUE_A) => { store = createStore(); - store.state.issues = { [issue.id]: { ...issue } }; + store.state.boardItems = { [issue.id]: { ...issue } }; store.dispatch('setActiveId', { id: issue.id }); wrapper = shallowMount(BoardSidebarIssueTitle, { @@ -74,7 +74,7 @@ describe('~/boards/components/sidebar/board_sidebar_issue_title.vue', () => { createWrapper(); jest.spyOn(wrapper.vm, 'setActiveIssueTitle').mockImplementation(() => { - store.state.issues[TEST_ISSUE_A.id].title = TEST_TITLE; + store.state.boardItems[TEST_ISSUE_A.id].title = TEST_TITLE; }); findFormInput().vm.$emit('input', TEST_TITLE); findForm().vm.$emit('submit', { preventDefault: () => {} }); @@ -147,7 +147,7 @@ describe('~/boards/components/sidebar/board_sidebar_issue_title.vue', () => { createWrapper(TEST_ISSUE_B); jest.spyOn(wrapper.vm, 'setActiveIssueTitle').mockImplementation(() => { - store.state.issues[TEST_ISSUE_B.id].title = TEST_TITLE; + store.state.boardItems[TEST_ISSUE_B.id].title = TEST_TITLE; }); findFormInput().vm.$emit('input', TEST_TITLE); findCancelButton().vm.$emit('click'); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_labels_select_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_labels_select_spec.js index 12b873ba7d8..98ac211238c 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_labels_select_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_labels_select_spec.js @@ -25,7 +25,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => { const createWrapper = ({ labels = [] } = {}) => { store = createStore(); - store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, labels } }; + store.state.boardItems = { [TEST_ISSUE.id]: { ...TEST_ISSUE, labels } }; store.state.activeId = TEST_ISSUE.id; wrapper = shallowMount(BoardSidebarLabelsSelect, { @@ -66,7 +66,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => { jest.spyOn(wrapper.vm, 'setActiveIssueLabels').mockImplementation(() => TEST_LABELS); findLabelsSelect().vm.$emit('updateSelectedLabels', TEST_LABELS_PAYLOAD); - store.state.issues[TEST_ISSUE.id].labels = TEST_LABELS; + store.state.boardItems[TEST_ISSUE.id].labels = TEST_LABELS; await wrapper.vm.$nextTick(); }); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js index 8820ec7ae63..8706424a296 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js @@ -22,7 +22,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () => const createWrapper = ({ milestone = null, loading = false } = {}) => { store = createStore(); - store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, milestone } }; + store.state.boardItems = { [TEST_ISSUE.id]: { ...TEST_ISSUE, milestone } }; store.state.activeId = TEST_ISSUE.id; wrapper = shallowMount(BoardSidebarMilestoneSelect, { @@ -113,7 +113,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () => createWrapper(); jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => { - store.state.issues[TEST_ISSUE.id].milestone = TEST_MILESTONE; + store.state.boardItems[TEST_ISSUE.id].milestone = TEST_MILESTONE; }); findDropdownItem().vm.$emit('click'); await wrapper.vm.$nextTick(); @@ -137,7 +137,7 @@ describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () => createWrapper({ milestone: TEST_MILESTONE }); jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => { - store.state.issues[TEST_ISSUE.id].milestone = null; + store.state.boardItems[TEST_ISSUE.id].milestone = null; }); findUnsetMilestoneItem().vm.$emit('click'); await wrapper.vm.$nextTick(); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_subscription_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_subscription_spec.js index 3e6b0be0267..d2d10563ae5 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_subscription_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_subscription_spec.js @@ -22,7 +22,7 @@ describe('~/boards/components/sidebar/board_sidebar_subscription_spec.vue', () = const createComponent = (activeIssue = { ...mockActiveIssue }) => { store = createStore(); - store.state.issues = { [activeIssue.id]: activeIssue }; + store.state.boardItems = { [activeIssue.id]: activeIssue }; store.state.activeId = activeIssue.id; wrapper = mount(BoardSidebarSubscription, { diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index c59e5876346..80d98c5eb6b 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -573,7 +573,7 @@ describe('fetchItemsForList', () => { }, { type: types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, - payload: { listIssues: formattedIssues, listPageInfo, listId }, + payload: { listItems: formattedIssues, listPageInfo, listId }, }, ], [], @@ -624,8 +624,8 @@ describe('moveIssue', () => { boardType: 'group', disabled: false, boardLists: mockLists, - issuesByListId: listIssues, - issues, + boardItemsByListId: listIssues, + boardItems: issues, }; it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_SUCCESS mutation when successful', (done) => { @@ -905,7 +905,7 @@ describe('addListIssue', () => { }); describe('setActiveIssueLabels', () => { - const state = { issues: { [mockIssue.id]: mockIssue } }; + const state = { boardItems: { [mockIssue.id]: mockIssue } }; const getters = { activeIssue: mockIssue }; const testLabelIds = labels.map((label) => label.id); const input = { @@ -950,7 +950,7 @@ describe('setActiveIssueLabels', () => { }); describe('setActiveIssueDueDate', () => { - const state = { issues: { [mockIssue.id]: mockIssue } }; + const state = { boardItems: { [mockIssue.id]: mockIssue } }; const getters = { activeIssue: mockIssue }; const testDueDate = '2020-02-20'; const input = { @@ -1001,7 +1001,7 @@ describe('setActiveIssueDueDate', () => { }); describe('setActiveIssueSubscribed', () => { - const state = { issues: { [mockActiveIssue.id]: mockActiveIssue } }; + const state = { boardItems: { [mockActiveIssue.id]: mockActiveIssue } }; const getters = { activeIssue: mockActiveIssue }; const subscribedState = true; const input = { @@ -1052,7 +1052,7 @@ describe('setActiveIssueSubscribed', () => { }); describe('setActiveIssueMilestone', () => { - const state = { issues: { [mockIssue.id]: mockIssue } }; + const state = { boardItems: { [mockIssue.id]: mockIssue } }; const getters = { activeIssue: mockIssue }; const testMilestone = { ...mockMilestone, @@ -1106,7 +1106,7 @@ describe('setActiveIssueMilestone', () => { }); describe('setActiveIssueTitle', () => { - const state = { issues: { [mockIssue.id]: mockIssue } }; + const state = { boardItems: { [mockIssue.id]: mockIssue } }; const getters = { activeIssue: mockIssue }; const testTitle = 'Test Title'; const input = { diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js index d5a19bf613f..d030b34ef80 100644 --- a/spec/frontend/boards/stores/getters_spec.js +++ b/spec/frontend/boards/stores/getters_spec.js @@ -38,15 +38,15 @@ describe('Boards - Getters', () => { }); }); - describe('getIssueById', () => { - const state = { issues: { 1: 'issue' } }; + describe('getBoardItemById', () => { + const state = { boardItems: { 1: 'issue' } }; it.each` id | expected ${'1'} | ${'issue'} ${''} | ${{}} `('returns $expected when $id is passed to state', ({ id, expected }) => { - expect(getters.getIssueById(state)(id)).toEqual(expected); + expect(getters.getBoardItemById(state)(id)).toEqual(expected); }); }); @@ -56,7 +56,7 @@ describe('Boards - Getters', () => { ${'1'} | ${'issue'} ${''} | ${{}} `('returns $expected when $id is passed to state', ({ id, expected }) => { - const state = { issues: { 1: 'issue' }, activeId: id }; + const state = { boardItems: { 1: 'issue' }, activeId: id }; expect(getters.activeIssue(state)).toEqual(expected); }); @@ -94,17 +94,18 @@ describe('Boards - Getters', () => { }); }); - describe('getIssuesByList', () => { + describe('getBoardItemsByList', () => { const boardsState = { - issuesByListId: mockIssuesByListId, - issues, + boardItemsByListId: mockIssuesByListId, + boardItems: issues, }; it('returns issues for a given listId', () => { - const getIssueById = (issueId) => [mockIssue, mockIssue2].find(({ id }) => id === issueId); + const getBoardItemById = (issueId) => + [mockIssue, mockIssue2].find(({ id }) => id === issueId); - expect(getters.getIssuesByList(boardsState, { getIssueById })('gid://gitlab/List/2')).toEqual( - mockIssues, - ); + expect( + getters.getBoardItemsByList(boardsState, { getBoardItemById })('gid://gitlab/List/2'), + ).toEqual(mockIssues); }); }); diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index e4dd368889a..2d7b80a997a 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -222,24 +222,24 @@ describe('Board Store Mutations', () => { }); describe('RESET_ISSUES', () => { - it('should remove issues from issuesByListId state', () => { - const issuesByListId = { + it('should remove issues from boardItemsByListId state', () => { + const boardItemsByListId = { 'gid://gitlab/List/1': [mockIssue.id], }; state = { ...state, - issuesByListId, + boardItemsByListId, }; mutations[types.RESET_ISSUES](state); - expect(state.issuesByListId).toEqual({ 'gid://gitlab/List/1': [] }); + expect(state.boardItemsByListId).toEqual({ 'gid://gitlab/List/1': [] }); }); }); describe('RECEIVE_ITEMS_FOR_LIST_SUCCESS', () => { - it('updates issuesByListId and issues on state', () => { + it('updates boardItemsByListId and issues on state', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id], }; @@ -249,10 +249,10 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: { + boardItemsByListId: { 'gid://gitlab/List/1': [], }, - issues: {}, + boardItems: {}, boardLists: initialBoardListsState, }; @@ -264,13 +264,13 @@ describe('Board Store Mutations', () => { }; mutations.RECEIVE_ITEMS_FOR_LIST_SUCCESS(state, { - listIssues: { listData: listIssues, issues }, + listItems: { listData: listIssues, boardItems: issues }, listPageInfo, listId: 'gid://gitlab/List/1', }); - expect(state.issuesByListId).toEqual(listIssues); - expect(state.issues).toEqual(issues); + expect(state.boardItemsByListId).toEqual(listIssues); + expect(state.boardItems).toEqual(issues); }); }); @@ -306,7 +306,7 @@ describe('Board Store Mutations', () => { state = { ...state, error: undefined, - issues: { + boardItems: { ...issue, }, }; @@ -320,7 +320,7 @@ describe('Board Store Mutations', () => { value, }); - expect(state.issues[issueId]).toEqual({ ...issue[issueId], id: '2' }); + expect(state.boardItems[issueId]).toEqual({ ...issue[issueId], id: '2' }); }); }); @@ -346,7 +346,7 @@ describe('Board Store Mutations', () => { }); describe('MOVE_ISSUE', () => { - it('updates issuesByListId, moving issue between lists', () => { + it('updates boardItemsByListId, moving issue between lists', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id], 'gid://gitlab/List/2': [], @@ -359,9 +359,9 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: listIssues, + boardItemsByListId: listIssues, boardLists: initialBoardListsState, - issues, + boardItems: issues, }; mutations.MOVE_ISSUE(state, { @@ -375,7 +375,7 @@ describe('Board Store Mutations', () => { 'gid://gitlab/List/2': [mockIssue2.id], }; - expect(state.issuesByListId).toEqual(updatedListIssues); + expect(state.boardItemsByListId).toEqual(updatedListIssues); }); }); @@ -387,19 +387,19 @@ describe('Board Store Mutations', () => { state = { ...state, - issues, + boardItems: issues, }; mutations.MOVE_ISSUE_SUCCESS(state, { issue: rawIssue, }); - expect(state.issues).toEqual({ 436: { ...mockIssue, id: 436 } }); + expect(state.boardItems).toEqual({ 436: { ...mockIssue, id: 436 } }); }); }); describe('MOVE_ISSUE_FAILURE', () => { - it('updates issuesByListId, reverting moving issue between lists, and sets error message', () => { + it('updates boardItemsByListId, reverting moving issue between lists, and sets error message', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id], 'gid://gitlab/List/2': [mockIssue2.id], @@ -407,7 +407,7 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: listIssues, + boardItemsByListId: listIssues, boardLists: initialBoardListsState, }; @@ -423,7 +423,7 @@ describe('Board Store Mutations', () => { 'gid://gitlab/List/2': [], }; - expect(state.issuesByListId).toEqual(updatedListIssues); + expect(state.boardItemsByListId).toEqual(updatedListIssues); expect(state.error).toEqual('An error occurred while moving the issue. Please try again.'); }); }); @@ -449,7 +449,7 @@ describe('Board Store Mutations', () => { }); describe('ADD_ISSUE_TO_LIST', () => { - it('adds issue to issues state and issue id in list in issuesByListId', () => { + it('adds issue to issues state and issue id in list in boardItemsByListId', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id], }; @@ -459,8 +459,8 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: listIssues, - issues, + boardItemsByListId: listIssues, + boardItems: issues, boardLists: initialBoardListsState, }; @@ -468,14 +468,14 @@ describe('Board Store Mutations', () => { mutations.ADD_ISSUE_TO_LIST(state, { list: mockLists[0], issue: mockIssue2 }); - expect(state.issuesByListId['gid://gitlab/List/1']).toContain(mockIssue2.id); - expect(state.issues[mockIssue2.id]).toEqual(mockIssue2); + expect(state.boardItemsByListId['gid://gitlab/List/1']).toContain(mockIssue2.id); + expect(state.boardItems[mockIssue2.id]).toEqual(mockIssue2); expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(2); }); }); describe('ADD_ISSUE_TO_LIST_FAILURE', () => { - it('removes issue id from list in issuesByListId and sets error message', () => { + it('removes issue id from list in boardItemsByListId and sets error message', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id], }; @@ -486,20 +486,20 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: listIssues, - issues, + boardItemsByListId: listIssues, + boardItems: issues, boardLists: initialBoardListsState, }; mutations.ADD_ISSUE_TO_LIST_FAILURE(state, { list: mockLists[0], issueId: mockIssue2.id }); - expect(state.issuesByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id); + expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id); expect(state.error).toBe('An error occurred while creating the issue. Please try again.'); }); }); describe('REMOVE_ISSUE_FROM_LIST', () => { - it('removes issue id from list in issuesByListId and deletes issue from state', () => { + it('removes issue id from list in boardItemsByListId and deletes issue from state', () => { const listIssues = { 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id], }; @@ -510,15 +510,15 @@ describe('Board Store Mutations', () => { state = { ...state, - issuesByListId: listIssues, - issues, + boardItemsByListId: listIssues, + boardItems: issues, boardLists: initialBoardListsState, }; mutations.ADD_ISSUE_TO_LIST_FAILURE(state, { list: mockLists[0], issueId: mockIssue2.id }); - expect(state.issuesByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id); - expect(state.issues).not.toContain(mockIssue2); + expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id); + expect(state.boardItems).not.toContain(mockIssue2); }); }); diff --git a/spec/frontend/fixtures/pipelines.rb b/spec/frontend/fixtures/pipelines.rb index b4b7f0e332f..2a538352abe 100644 --- a/spec/frontend/fixtures/pipelines.rb +++ b/spec/frontend/fixtures/pipelines.rb @@ -5,16 +5,22 @@ require 'spec_helper' RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') } - let(:commit) { create(:commit, project: project) } - let(:commit_without_author) { RepoHelpers.another_sample_commit } - let!(:user) { create(:user, developer_projects: [project], email: commit.author_email) } - let!(:pipeline) { create(:ci_pipeline, project: project, sha: commit.id, user: user) } + let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') } + + let_it_be(:commit_without_author) { RepoHelpers.another_sample_commit } let!(:pipeline_without_author) { create(:ci_pipeline, project: project, sha: commit_without_author.id) } - let!(:pipeline_without_commit) { create(:ci_pipeline, status: :success, project: project, sha: '0000') } + let!(:build_pipeline_without_author) { create(:ci_build, pipeline: pipeline_without_author, stage: 'test') } - render_views + let_it_be(:pipeline_without_commit) { create(:ci_pipeline, status: :success, project: project, sha: '0000') } + let!(:build_pipeline_without_commit) { create(:ci_build, pipeline: pipeline_without_commit, stage: 'test') } + + let(:commit) { create(:commit, project: project) } + let(:user) { create(:user, developer_projects: [project], email: commit.author_email) } + let!(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project, sha: commit.id, user: user) } + let!(:build_success) { create(:ci_build, pipeline: pipeline, stage: 'build') } + let!(:build_test) { create(:ci_build, pipeline: pipeline, stage: 'test') } + let!(:build_deploy_failed) { create(:ci_build, status: :failed, pipeline: pipeline, stage: 'deploy') } before(:all) do clean_frontend_fixtures('pipelines/') @@ -32,4 +38,14 @@ RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :co expect(response).to be_successful end + + it "pipelines/test_report.json" do + get :test_report, params: { + namespace_id: namespace, + project_id: project, + id: pipeline.id + }, format: :json + + expect(response).to be_successful + end end diff --git a/spec/frontend/fixtures/test_report.rb b/spec/frontend/fixtures/test_report.rb deleted file mode 100644 index 3d09078ba68..00000000000 --- a/spec/frontend/fixtures/test_report.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -RSpec.describe Projects::PipelinesController, "(JavaScript fixtures)", type: :controller do - include JavaScriptFixturesHelpers - - let(:namespace) { create(:namespace, name: "frontend-fixtures") } - let(:project) { create(:project, :repository, namespace: namespace, path: "pipelines-project") } - let(:commit) { create(:commit, project: project) } - let(:user) { create(:user, developer_projects: [project], email: commit.author_email) } - let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project, user: user) } - - render_views - - before do - sign_in(user) - end - - it "pipelines/test_report.json" do - get :test_report, params: { - namespace_id: project.namespace, - project_id: project, - id: pipeline.id - }, format: :json - - expect(response).to be_successful - end -end diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js index 2afdbb05107..337838c41b3 100644 --- a/spec/frontend/pipelines/mock_data.js +++ b/spec/frontend/pipelines/mock_data.js @@ -2,328 +2,6 @@ const PIPELINE_RUNNING = 'RUNNING'; const PIPELINE_CANCELED = 'CANCELED'; const PIPELINE_FAILED = 'FAILED'; -export const pipelineWithStages = { - id: 20333396, - user: { - id: 128633, - name: 'Rémy Coutable', - username: 'rymai', - state: 'active', - avatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - web_url: 'https://gitlab.com/rymai', - path: '/rymai', - }, - active: true, - coverage: '58.24', - source: 'push', - created_at: '2018-04-11T14:04:53.881Z', - updated_at: '2018-04-11T14:05:00.792Z', - path: '/gitlab-org/gitlab/pipelines/20333396', - flags: { - latest: true, - stuck: false, - auto_devops: false, - yaml_errors: false, - retryable: false, - cancelable: true, - failure_reason: false, - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico', - }, - duration: null, - finished_at: null, - stages: [ - { - name: 'build', - title: 'build: skipped', - status: { - icon: 'status_skipped', - text: 'skipped', - label: 'skipped', - group: 'skipped', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#build', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_skipped-a2eee568a5bffdb494050c7b62dde241de9189280836288ac8923d369f16222d.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#build', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=build', - }, - { - name: 'prepare', - title: 'prepare: passed', - status: { - icon: 'status_success', - text: 'passed', - label: 'passed', - group: 'success', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#prepare', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_success-26f59841becbef8c6fe414e9e74471d8bfd6a91b5855c19fe7f5923a40a7da47.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#prepare', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=prepare', - }, - { - name: 'test', - title: 'test: running', - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#test', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#test', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=test', - }, - { - name: 'post-test', - title: 'post-test: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#post-test', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#post-test', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-test', - }, - { - name: 'pages', - title: 'pages: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#pages', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#pages', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=pages', - }, - { - name: 'post-cleanup', - title: 'post-cleanup: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-cleanup', - }, - ], - artifacts: [ - { - name: 'gitlab:assets:compile', - expired: false, - expire_at: '2018-05-12T14:22:54.730Z', - path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/browse', - }, - { - name: 'rspec-mysql 12 28', - expired: false, - expire_at: '2018-05-12T14:22:45.136Z', - path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/browse', - }, - { - name: 'rspec-mysql 6 28', - expired: false, - expire_at: '2018-05-12T14:22:41.523Z', - path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/browse', - }, - { - name: 'rspec-pg geo 0 1', - expired: false, - expire_at: '2018-05-12T14:22:13.287Z', - path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/browse', - }, - { - name: 'rspec-mysql 0 28', - expired: false, - expire_at: '2018-05-12T14:22:06.834Z', - path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/browse', - }, - { - name: 'spinach-mysql 0 2', - expired: false, - expire_at: '2018-05-12T14:21:51.409Z', - path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/browse', - }, - { - name: 'karma', - expired: false, - expire_at: '2018-05-12T14:21:20.934Z', - path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/browse', - }, - { - name: 'spinach-pg 0 2', - expired: false, - expire_at: '2018-05-12T14:20:01.028Z', - path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/browse', - }, - { - name: 'spinach-pg 1 2', - expired: false, - expire_at: '2018-05-12T14:19:04.336Z', - path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/browse', - }, - { - name: 'sast', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/browse', - }, - { - name: 'code_quality', - expired: false, - expire_at: '2018-04-18T14:16:24.484Z', - path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/browse', - }, - { - name: 'cache gems', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/browse', - }, - { - name: 'dependency_scanning', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/browse', - }, - { - name: 'compile-assets', - expired: false, - expire_at: '2018-04-18T14:12:07.638Z', - path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/browse', - }, - { - name: 'setup-test-env', - expired: false, - expire_at: '2018-04-18T14:10:27.024Z', - path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/browse', - }, - { - name: 'retrieve-tests-metadata', - expired: false, - expire_at: '2018-05-12T14:06:35.926Z', - path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/browse', - }, - ], - manual_actions: [ - { - name: 'package-and-qa', - path: '/gitlab-org/gitlab/-/jobs/62411330/play', - playable: true, - }, - { - name: 'review-docs-deploy', - path: '/gitlab-org/gitlab/-/jobs/62411332/play', - playable: true, - }, - ], - }, - ref: { - name: 'master', - path: '/gitlab-org/gitlab/commits/master', - tag: false, - branch: true, - }, - commit: { - id: 'e6a2885c503825792cb8a84a8731295e361bd059', - short_id: 'e6a2885c', - title: "Merge branch 'ce-to-ee-2018-04-11' into 'master'", - created_at: '2018-04-11T14:04:39.000Z', - parent_ids: [ - '5d9b5118f6055f72cff1a82b88133609912f2c1d', - '6fdc6ee76a8062fe41b1a33f7c503334a6ebdc02', - ], - message: - "Merge branch 'ce-to-ee-2018-04-11' into 'master'\n\nCE upstream - 2018-04-11 12:26 UTC\n\nSee merge request gitlab-org/gitlab-ee!5326", - author_name: 'Rémy Coutable', - author_email: 'remy@rymai.me', - authored_date: '2018-04-11T14:04:39.000Z', - committer_name: 'Rémy Coutable', - committer_email: 'remy@rymai.me', - committed_date: '2018-04-11T14:04:39.000Z', - author: { - id: 128633, - name: 'Rémy Coutable', - username: 'rymai', - state: 'active', - avatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - web_url: 'https://gitlab.com/rymai', - path: '/rymai', - }, - author_gravatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - commit_url: - 'https://gitlab.com/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059', - commit_path: '/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059', - }, - cancel_path: '/gitlab-org/gitlab/pipelines/20333396/cancel', - triggered_by: null, - triggered: [], -}; - const threeWeeksAgo = new Date(); threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js index 78c0bcab03a..ccf3f0b6667 100644 --- a/spec/frontend/pipelines/pipelines_spec.js +++ b/spec/frontend/pipelines/pipelines_spec.js @@ -18,7 +18,7 @@ import Store from '~/pipelines/stores/pipelines_store'; import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue'; import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; -import { pipelineWithStages, stageReply, users, mockSearch, branches } from './mock_data'; +import { stageReply, users, mockSearch, branches } from './mock_data'; jest.mock('~/flash'); @@ -27,6 +27,9 @@ const mockProjectId = '21'; const mockPipelinesEndpoint = `/${mockProjectPath}/pipelines.json`; const mockPipelinesResponse = getJSONFixture('pipelines/pipelines.json'); const mockPipelinesIds = mockPipelinesResponse.pipelines.map(({ id }) => id); +const mockPipelineWithStages = mockPipelinesResponse.pipelines.find( + (p) => p.details.stages && p.details.stages.length, +); describe('Pipelines', () => { let wrapper; @@ -611,14 +614,15 @@ describe('Pipelines', () => { mock.onGet(mockPipelinesEndpoint, { scope: 'all', page: '1' }).reply( 200, { - pipelines: [pipelineWithStages], + pipelines: [mockPipelineWithStages], count: { all: '1' }, }, { 'POLL-INTERVAL': 100, }, ); - mock.onGet(pipelineWithStages.details.stages[0].dropdown_path).reply(200, stageReply); + + mock.onGet(mockPipelineWithStages.details.stages[0].dropdown_path).reply(200, stageReply); createComponent(); diff --git a/spec/frontend/pipelines/pipelines_table_row_spec.js b/spec/frontend/pipelines/pipelines_table_row_spec.js index 60318511be2..8edf891b443 100644 --- a/spec/frontend/pipelines/pipelines_table_row_spec.js +++ b/spec/frontend/pipelines/pipelines_table_row_spec.js @@ -155,7 +155,7 @@ describe('Pipelines Table Row', () => { it('should render an icon for each stage', () => { expect( wrapper.findAll( - '.table-section:nth-child(4) [data-testid="mini-pipeline-graph-dropdown-toggle"]', + '.table-section:nth-child(5) [data-testid="mini-pipeline-graph-dropdown-toggle"]', ).length, ).toEqual(pipeline.details.stages.length); }); @@ -182,9 +182,10 @@ describe('Pipelines Table Row', () => { expect(wrapper.find('.js-pipelines-retry-button').attributes('title')).toMatch('Retry'); expect(wrapper.find('.js-pipelines-cancel-button').exists()).toBe(true); expect(wrapper.find('.js-pipelines-cancel-button').attributes('title')).toMatch('Cancel'); - const dropdownMenu = wrapper.find('.dropdown-menu'); - expect(dropdownMenu.text()).toContain(scheduledJobAction.name); + const actionsMenu = wrapper.find('[data-testid="pipelines-manual-actions-dropdown"]'); + + expect(actionsMenu.text()).toContain(scheduledJobAction.name); }); it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => { diff --git a/spec/lib/bulk_imports/groups/pipelines/labels_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/labels_pipeline_spec.rb index f923a4a4721..3327a30f1d5 100644 --- a/spec/lib/bulk_imports/groups/pipelines/labels_pipeline_spec.rb +++ b/spec/lib/bulk_imports/groups/pipelines/labels_pipeline_spec.rb @@ -6,6 +6,7 @@ RSpec.describe BulkImports::Groups::Pipelines::LabelsPipeline do let(:user) { create(:user) } let(:group) { create(:group) } let(:cursor) { 'cursor' } + let(:timestamp) { Time.new(2020, 01, 01).utc } let(:entity) do create( :bulk_import_entity, @@ -20,21 +21,23 @@ RSpec.describe BulkImports::Groups::Pipelines::LabelsPipeline do subject { described_class.new(context) } - def extractor_data(title:, has_next_page:, cursor: nil) - data = [ - { - 'title' => title, - 'description' => 'desc', - 'color' => '#428BCA' - } - ] + def label_data(title) + { + 'title' => title, + 'description' => 'desc', + 'color' => '#428BCA', + 'created_at' => timestamp.to_s, + 'updated_at' => timestamp.to_s + } + end + def extractor_data(title:, has_next_page:, cursor: nil) page_info = { 'end_cursor' => cursor, 'has_next_page' => has_next_page } - BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info) + BulkImports::Pipeline::ExtractedData.new(data: [label_data(title)], page_info: page_info) end describe '#run' do @@ -55,6 +58,8 @@ RSpec.describe BulkImports::Groups::Pipelines::LabelsPipeline do expect(label.title).to eq('label2') expect(label.description).to eq('desc') expect(label.color).to eq('#428BCA') + expect(label.created_at).to eq(timestamp) + expect(label.updated_at).to eq(timestamp) end end @@ -92,19 +97,15 @@ RSpec.describe BulkImports::Groups::Pipelines::LabelsPipeline do describe '#load' do it 'creates the label' do - data = { - 'title' => 'label', - 'description' => 'description', - 'color' => '#FFFFFF' - } + data = label_data('label') expect { subject.load(context, data) }.to change(Label, :count).by(1) label = group.labels.first - expect(label.title).to eq(data['title']) - expect(label.description).to eq(data['description']) - expect(label.color).to eq(data['color']) + data.each do |key, value| + expect(label[key]).to eq(value) + end end end diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index 01aceec12c5..f5cb1987c5c 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -24,6 +24,26 @@ RSpec.describe Gitlab::CurrentSettings do end end + describe '.signup_disabled?' do + subject { described_class.signup_disabled? } + + context 'when signup is enabled' do + before do + create(:application_setting, signup_enabled: true) + end + + it { is_expected.to be_falsey } + end + + context 'when signup is disabled' do + before do + create(:application_setting, signup_enabled: false) + end + + it { is_expected.to be_truthy } + end + end + describe '#current_application_settings', :use_clean_rails_memory_store_caching do it 'allows keys to be called directly' do db_settings = create(:application_setting, diff --git a/spec/lib/marginalia_spec.rb b/spec/lib/marginalia_spec.rb index fa0cd214c7e..2ee27fbe20c 100644 --- a/spec/lib/marginalia_spec.rb +++ b/spec/lib/marginalia_spec.rb @@ -37,26 +37,9 @@ RSpec.describe 'Marginalia spec' do } end - context 'when the feature is enabled' do - before do - stub_feature(true) - end - - it 'generates a query that includes the component and value' do - component_map.each do |component, value| - expect(recorded.log.last).to include("#{component}:#{value}") - end - end - end - - context 'when the feature is disabled' do - before do - stub_feature(false) - end - - it 'excludes annotations in generated queries' do - expect(recorded.log.last).not_to include("/*") - expect(recorded.log.last).not_to include("*/") + it 'generates a query that includes the component and value' do + component_map.each do |component, value| + expect(recorded.log.last).to include("#{component}:#{value}") end end end @@ -90,59 +73,37 @@ RSpec.describe 'Marginalia spec' do } end - context 'when the feature is enabled' do - before do - stub_feature(true) + it 'generates a query that includes the component and value' do + component_map.each do |component, value| + expect(recorded.log.last).to include("#{component}:#{value}") end + end - it 'generates a query that includes the component and value' do - component_map.each do |component, value| - expect(recorded.log.last).to include("#{component}:#{value}") - end - end - - describe 'for ActionMailer delivery jobs' do - let(:delivery_job) { MarginaliaTestMailer.first_user.deliver_later } - - let(:recorded) do - ActiveRecord::QueryRecorder.new do - delivery_job.perform_now - end - end - - let(:component_map) do - { - "application" => "sidekiq", - "jid" => delivery_job.job_id, - "job_class" => delivery_job.arguments.first - } - end + describe 'for ActionMailer delivery jobs' do + let(:delivery_job) { MarginaliaTestMailer.first_user.deliver_later } - it 'generates a query that includes the component and value' do - component_map.each do |component, value| - expect(recorded.log.last).to include("#{component}:#{value}") - end + let(:recorded) do + ActiveRecord::QueryRecorder.new do + delivery_job.perform_now end end - end - context 'when the feature is disabled' do - before do - stub_feature(false) + let(:component_map) do + { + "application" => "sidekiq", + "jid" => delivery_job.job_id, + "job_class" => delivery_job.arguments.first + } end - it 'excludes annotations in generated queries' do - expect(recorded.log.last).not_to include("/*") - expect(recorded.log.last).not_to include("*/") + it 'generates a query that includes the component and value' do + component_map.each do |component, value| + expect(recorded.log.last).to include("#{component}:#{value}") + end end end end - def stub_feature(value) - stub_feature_flags(marginalia: value) - Gitlab::Marginalia.set_enabled_from_feature_flag - end - def make_request(correlation_id) request_env = Rack::MockRequest.env_for('/') diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index cbb20de75b0..06b6ba2088f 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -3082,32 +3082,83 @@ RSpec.describe MergeRequest, factory_default: :keep do end describe "#head_pipeline_active? " do - it do - is_expected - .to delegate_method(:active?) - .to(:head_pipeline) - .with_prefix - .with_arguments(allow_nil: true) + context 'when project lacks a head_pipeline relation' do + before do + subject.head_pipeline = nil + end + + it 'returns false' do + expect(subject.head_pipeline_active?).to be false + end + end + + context 'when project has a head_pipeline relation' do + let(:pipeline) { create(:ci_empty_pipeline) } + + before do + allow(subject).to receive(:head_pipeline) { pipeline } + end + + it 'accesses the value from the head_pipeline' do + expect(subject.head_pipeline) + .to receive(:active?) + + subject.head_pipeline_active? + end end end describe "#actual_head_pipeline_success? " do - it do - is_expected - .to delegate_method(:success?) - .to(:actual_head_pipeline) - .with_prefix - .with_arguments(allow_nil: true) + context 'when project lacks an actual_head_pipeline relation' do + before do + allow(subject).to receive(:actual_head_pipeline) { nil } + end + + it 'returns false' do + expect(subject.actual_head_pipeline_success?).to be false + end + end + + context 'when project has a actual_head_pipeline relation' do + let(:pipeline) { create(:ci_empty_pipeline) } + + before do + allow(subject).to receive(:actual_head_pipeline) { pipeline } + end + + it 'accesses the value from the actual_head_pipeline' do + expect(subject.actual_head_pipeline) + .to receive(:success?) + + subject.actual_head_pipeline_success? + end end end describe "#actual_head_pipeline_active? " do - it do - is_expected - .to delegate_method(:active?) - .to(:actual_head_pipeline) - .with_prefix - .with_arguments(allow_nil: true) + context 'when project lacks an actual_head_pipeline relation' do + before do + allow(subject).to receive(:actual_head_pipeline) { nil } + end + + it 'returns false' do + expect(subject.actual_head_pipeline_active?).to be false + end + end + + context 'when project has a actual_head_pipeline relation' do + let(:pipeline) { create(:ci_empty_pipeline) } + + before do + allow(subject).to receive(:actual_head_pipeline) { pipeline } + end + + it 'accesses the value from the actual_head_pipeline' do + expect(subject.actual_head_pipeline) + .to receive(:active?) + + subject.actual_head_pipeline_active? + end end end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 623767d19e0..c194a98fdd8 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -496,6 +496,16 @@ RSpec.describe Snippet do it 'returns array of blobs' do expect(snippet.blobs).to all(be_a(Blob)) end + + context 'when file does not exist' do + it 'removes nil values from the blobs array' do + allow(snippet).to receive(:list_files).and_return(%w(LICENSE non_existent_snippet_file)) + + blobs = snippet.blobs + expect(blobs.count).to eq 1 + expect(blobs.first.name).to eq 'LICENSE' + end + end end end diff --git a/spec/presenters/snippet_presenter_spec.rb b/spec/presenters/snippet_presenter_spec.rb index a1d987ed78f..b0387206bd9 100644 --- a/spec/presenters/snippet_presenter_spec.rb +++ b/spec/presenters/snippet_presenter_spec.rb @@ -159,7 +159,7 @@ RSpec.describe SnippetPresenter do let(:snippet) { create(:snippet, :repository, author: user) } it 'returns repository first blob' do - expect(subject).to eq snippet.blobs.first + expect(subject.name).to eq snippet.blobs.first.name end end end diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index 2316e702c3e..b5bf697e9e3 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -5,7 +5,9 @@ require 'spec_helper' RSpec.describe API::Lint do describe 'POST /ci/lint' do context 'when signup settings are disabled' do - Gitlab::CurrentSettings.signup_enabled = false + before do + Gitlab::CurrentSettings.signup_enabled = false + end context 'when unauthenticated' do it 'returns authentication error' do @@ -16,22 +18,25 @@ RSpec.describe API::Lint do end context 'when authenticated' do - it 'returns unauthorized error' do - post api('/ci/lint'), params: { content: 'content' } + let_it_be(:api_user) { create(:user) } + it 'returns authorized' do + post api('/ci/lint', api_user), params: { content: 'content' } - expect(response).to have_gitlab_http_status(:unauthorized) + expect(response).to have_gitlab_http_status(:ok) end end end context 'when signup settings are enabled' do - Gitlab::CurrentSettings.signup_enabled = true + before do + Gitlab::CurrentSettings.signup_enabled = true + end context 'when unauthenticated' do - it 'returns authentication error' do + it 'returns authorized success' do post api('/ci/lint'), params: { content: 'content' } - expect(response).to have_gitlab_http_status(:unauthorized) + expect(response).to have_gitlab_http_status(:ok) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 64c1479a412..2c745a0a70c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -249,9 +249,6 @@ RSpec.configure do |config| unstub_all_feature_flags end - # Enable Marginalia feature for all specs in the test suite. - Gitlab::Marginalia.enabled = true - # Stub these calls due to being expensive operations # It can be reenabled for specific tests via: # |