diff options
38 files changed, 421 insertions, 143 deletions
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 6be3a62ff00..0d10c896a47 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -166,6 +166,7 @@ export default () => { humanTotalTimeSpent, weight, epic, + assignees, } = convertObjectPropsToCamelCase(data); newIssue.setFetchingState('subscriptions', false); @@ -179,6 +180,7 @@ export default () => { subscribed, weight, epic, + assignees, }); }) .catch(() => { diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 60c2059a876..a28e17f7a56 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */ +/* eslint-disable func-names, no-else-return, consistent-return, one-var, no-return-assign */ import $ from 'jquery'; @@ -51,7 +51,7 @@ export default class ImageFile { } // eslint-disable-next-line class-methods-use-this initDraggable($el, padding, callback) { - var dragging = false; + let dragging = false; const $body = $('body'); const $offsetEl = $el.parent(); const dragStart = function() { @@ -88,14 +88,12 @@ export default class ImageFile { } static prepareFrames(view) { - var maxHeight, maxWidth; - maxWidth = 0; - maxHeight = 0; + let maxWidth = 0; + let maxHeight = 0; $('.frame', view) .each((index, frame) => { - var height, width; - width = $(frame).width(); - height = $(frame).height(); + const width = $(frame).width(); + const height = $(frame).height(); maxWidth = width > maxWidth ? width : maxWidth; return (maxHeight = height > maxHeight ? height : maxHeight); }) @@ -110,8 +108,7 @@ export default class ImageFile { 'two-up': function() { return $('.two-up.view .wrap', this.file).each((index, wrap) => { $('img', wrap).each(function() { - var currentWidth; - currentWidth = $(this).width(); + const currentWidth = $(this).width(); if (currentWidth > availWidth / 2) { return $(this).width(availWidth / 2); } @@ -124,16 +121,14 @@ export default class ImageFile { }); }, swipe() { - var maxHeight, maxWidth; - maxWidth = 0; - maxHeight = 0; + let maxWidth = 0; + let maxHeight = 0; return $('.swipe.view', this.file).each((index, view) => { - var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding; const ref = ImageFile.prepareFrames(view); [maxWidth, maxHeight] = ref; - $swipeFrame = $('.swipe-frame', view); - $swipeWrap = $('.swipe-wrap', view); - $swipeBar = $('.swipe-bar', view); + const $swipeFrame = $('.swipe-frame', view); + const $swipeWrap = $('.swipe-wrap', view); + const $swipeBar = $('.swipe-bar', view); $swipeFrame.css({ width: maxWidth + 16, @@ -148,7 +143,7 @@ export default class ImageFile { left: 1, }); - wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); + const wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); this.initDraggable($swipeBar, wrapPadding, (e, left) => { if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) { @@ -159,19 +154,17 @@ export default class ImageFile { }); }, 'onion-skin': function() { - var dragTrackWidth, maxHeight, maxWidth; + let maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; - dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); + const dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); return $('.onion-skin.view', this.file).each((index, view) => { - var $frame, $track, $dragger, $frameAdded, framePadding; - const ref = ImageFile.prepareFrames(view); [maxWidth, maxHeight] = ref; - $frame = $('.onion-skin-frame', view); - $frameAdded = $('.frame.added', view); - $track = $('.drag-track', view); - $dragger = $('.dragger', $track); + const $frame = $('.onion-skin-frame', view); + const $frameAdded = $('.frame.added', view); + const $track = $('.drag-track', view); + const $dragger = $('.dragger', $track); $frame.css({ width: maxWidth + 16, @@ -186,10 +179,10 @@ export default class ImageFile { }); $frameAdded.css('opacity', 1); - framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); + const framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); this.initDraggable($dragger, framePadding, (e, left) => { - var opacity = left / dragTrackWidth; + const opacity = left / dragTrackWidth; if (opacity >= 0 && opacity <= 1) { $dragger.css('left', left); diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue index 86f5559af8f..eb8945c1a57 100644 --- a/app/assets/javascripts/monitoring/components/embed.vue +++ b/app/assets/javascripts/monitoring/components/embed.vue @@ -97,7 +97,6 @@ export default { v-for="(graphData, graphIndex) in charts" :key="`panel-type-${graphIndex}`" class="w-100" - clipboard-text="" :graph-data="graphData" :group-id="dashboardUrl" /> diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index d0dce4f5116..ec6a41d0540 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -36,7 +36,8 @@ export default { props: { clipboardText: { type: String, - required: true, + required: false, + default: '', }, graphData: { type: Object, @@ -152,6 +153,7 @@ export default { {{ __('Download CSV') }} </gl-dropdown-item> <gl-dropdown-item + v-if="clipboardText" v-track-event="generateLinkToChartOptions(clipboardText)" class="js-chart-link" :data-clipboard-text="clipboardText" diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue index 53f509185a8..8636984c6af 100644 --- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue +++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue @@ -12,6 +12,9 @@ export default { }, mixins: [Issuable, issuableStateMixin], computed: { + projectArchivedWarning() { + return __('This project is archived and cannot be commented on.'); + }, lockedIssueWarning() { return sprintf( __('This %{issuableDisplayName} is locked. Only project members can comment.'), @@ -26,9 +29,15 @@ export default { <div class="disabled-comment text-center"> <span class="issuable-note-warning inline"> <icon :size="16" name="lock" class="icon" /> - <span> - {{ lockedIssueWarning }} + <span v-if="isProjectArchived"> + {{ projectArchivedWarning }} + <gl-link :href="archivedProjectDocsPath" target="_blank" class="learn-more"> + {{ __('Learn more') }} + </gl-link> + </span> + <span v-else> + {{ lockedIssueWarning }} <gl-link :href="lockedIssueDocsPath" target="_blank" class="learn-more"> {{ __('Learn more') }} </gl-link> diff --git a/app/assets/javascripts/notes/mixins/issuable_state.js b/app/assets/javascripts/notes/mixins/issuable_state.js index d97d9f6850a..0ca8c8c98a3 100644 --- a/app/assets/javascripts/notes/mixins/issuable_state.js +++ b/app/assets/javascripts/notes/mixins/issuable_state.js @@ -3,6 +3,12 @@ import { mapGetters } from 'vuex'; export default { computed: { ...mapGetters(['getNoteableDataByProp']), + isProjectArchived() { + return this.getNoteableDataByProp('is_project_archived'); + }, + archivedProjectDocsPath() { + return this.getNoteableDataByProp('archived_project_docs_path'); + }, lockedIssueDocsPath() { return this.getNoteableDataByProp('locked_discussion_docs_path'); }, diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue index aaac812f213..9a60172db2e 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue @@ -1,7 +1,6 @@ <script> import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; -import { joinPaths } from '~/lib/utils/url_utility'; import AssigneeAvatar from './assignee_avatar.vue'; export default { @@ -60,7 +59,7 @@ export default { }; }, assigneeUrl() { - return joinPaths(`${this.rootPath}`, `${this.user.username}`); + return this.user.web_url; }, }, }; diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index ba8d2d18695..0df201ab506 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -53,7 +53,7 @@ class AutocompleteController < ApplicationController private def target_branch_params - params.permit(:group_id, :project_id) + params.permit(:group_id, :project_id).select { |_, v| v.present? } end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 0b1d17a9e12..ce263566ea6 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -502,6 +502,10 @@ module Ci builds.skipped.after_stage(stage_idx).find_each(&:process) end + def child? + false + end + def latest? return false unless git_ref && commit.present? diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index a3d0298a495..98c0c703584 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -64,4 +64,12 @@ class IssueEntity < IssuableEntity expose :locked_discussion_docs_path, if: -> (issue) { issue.discussion_locked? } do |issue| help_page_path('user/discussions/index.md', anchor: 'lock-discussions') end + + expose :is_project_archived do |issue| + issue.project.archived? + end + + expose :archived_project_docs_path, if: -> (issue) { issue.project.archived? } do |issue| + help_page_path('user/project/settings/index.md', anchor: 'archiving-a-project') + end end diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb index 9504fdd8eac..8e7456ce059 100644 --- a/app/serializers/merge_request_noteable_entity.rb +++ b/app/serializers/merge_request_noteable_entity.rb @@ -42,6 +42,18 @@ class MergeRequestNoteableEntity < IssuableEntity end end + expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request| + help_page_path('user/discussions/index.md', anchor: 'lock-discussions') + end + + expose :is_project_archived do |merge_request| + merge_request.project.archived? + end + + expose :archived_project_docs_path, if: -> (merge_request) { merge_request.project.archived? } do |merge_request| + help_page_path('user/project/settings/index.md', anchor: 'archiving-a-project') + end + private delegate :current_user, to: :request diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index f9cc118a252..160c3b4d06d 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -19,7 +19,8 @@ = group_icon(@group, class: "avatar s60") %li %span.light= _('Name:') - %strong= @group.name + %strong + = link_to @group.name, group_path(@group) %li %span.light= _('Path:') %strong diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 4eec81c9125..ce6ae765de9 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -20,6 +20,11 @@ .well-segment.qa-pipeline-badges .icon-container = sprite_icon('flag') + - if @pipeline.child? + %span.js-pipeline-child.badge.badge-primary.has-tooltip{ title: s_("Pipelines|This is a child pipeline within the parent pipeline") } + = s_('Pipelines|Child pipeline') + = surround '(', ')' do + = link_to s_('Pipelines|parent'), pipeline_path(@pipeline.triggered_by_pipeline), class: 'text-white text-underline' - if @pipeline.latest? %span.js-pipeline-url-latest.badge.badge-success.has-tooltip{ title: _("Latest pipeline for the most recent commit on this branch") } latest diff --git a/changelogs/unreleased/24190-archived-project-warning-message.yml b/changelogs/unreleased/24190-archived-project-warning-message.yml new file mode 100644 index 00000000000..bfe1976451e --- /dev/null +++ b/changelogs/unreleased/24190-archived-project-warning-message.yml @@ -0,0 +1,5 @@ +--- +title: Show correct warning on issue when project is archived +merge_request: 20078 +author: +type: fixed diff --git a/changelogs/unreleased/commit-box-child-label.yml b/changelogs/unreleased/commit-box-child-label.yml new file mode 100644 index 00000000000..99cfcae2016 --- /dev/null +++ b/changelogs/unreleased/commit-box-child-label.yml @@ -0,0 +1,5 @@ +--- +title: Add child label to commit box +merge_request: 21323 +author: +type: added diff --git a/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml b/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml new file mode 100644 index 00000000000..b91e5cfdde7 --- /dev/null +++ b/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml @@ -0,0 +1,5 @@ +--- +title: Fix assignee url in issue board sidebar +merge_request: 20992 +author: Lee Tickett +type: fixed diff --git a/changelogs/unreleased/winh-var-image_file.yml b/changelogs/unreleased/winh-var-image_file.yml new file mode 100644 index 00000000000..7ca37d304fe --- /dev/null +++ b/changelogs/unreleased/winh-var-image_file.yml @@ -0,0 +1,5 @@ +--- +title: Remove var from app/assets/javascripts/commit/image_file.js +merge_request: 21649 +author: Abubakar Hassan +type: other diff --git a/changelogs/unreleased/xanf-add-group-link.yml b/changelogs/unreleased/xanf-add-group-link.yml new file mode 100644 index 00000000000..5cb57c5d544 --- /dev/null +++ b/changelogs/unreleased/xanf-add-group-link.yml @@ -0,0 +1,5 @@ +--- +title: link to group on group admin page +merge_request: 21709 +author: +type: added diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 466e4e43bfc..525a8f7a94c 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -91,7 +91,7 @@ GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform IP based firewall can be configured by looking up all [IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges). -[Static endpoints](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/5071) are being considered. +[Static endpoints](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/97) are being considered. ## Shared Runners diff --git a/doc/user/project/repository/git_blame.md b/doc/user/project/repository/git_blame.md new file mode 100644 index 00000000000..454b3f86df9 --- /dev/null +++ b/doc/user/project/repository/git_blame.md @@ -0,0 +1,50 @@ +--- +type: reference, howto +description: "Documentation on Git file blame." +--- + +# Git file blame + +> [Introduced](https://git.sphere.ly/staff/publicgitlab/commit/39c657930625ddc3ac8a921f01ffc83acadce68f) in GitLab 2.5 + +[Git blame](https://git-scm.com/docs/git-blame) provides more information +about every line in a file, including the last modified time, author, and +commit hash. + +You can find the **Blame** button with each file in a project. + +![File blame button](img/file_blame_button_v12_6.png "Blame button") + +When you select the **Blame** button, you'll see a screen with the +noted information: + +![Git blame output](img/file_blame_output_v12_6.png "Blame button output") + +If you hover over a commit in the UI, you'll see a precise date and time +for that commit. + +## Associated `git` command + +If you're running `git` from the command line, the equivalent command is +`git blame <filename>`. For example, if you want to find `blame` information +about a `README.md` file in the local directory, run the following command: + +```bash +git blame README.md +``` + +You'll see output similar to the following, which includes the commit time +in UTC format: + +```bash +62e2353a (Achilleas Pipinellis 2019-07-11 14:52:18 +0300 1) [![build status](https://gitlab.com/gitlab-org/gitlab-docs/badges/master/build.svg)](https://gitlab.com/gitlab-com/gitlab-docs/commits/master) +fb0fc7d6 (Achilleas Pipinellis 2016-11-07 22:21:22 +0100 2) +^764ca75 (Connor Shea 2016-10-05 23:40:24 -0600 3) # GitLab Documentation +^764ca75 (Connor Shea 2016-10-05 23:40:24 -0600 4) +0e62ed6d (Mike Jang 2019-11-26 21:44:53 +0000 5) This project hosts the repository used to generate the GitLab +0e62ed6d (Mike Jang 2019-11-26 21:44:53 +0000 6) documentation website and deployed to https://docs.gitlab.com. It uses the +``` + +## File blame through the API + +You can also get this information over the [Git file blame REST API](../../../api/repository_files.md#get-file-blame-from-repository). diff --git a/doc/user/project/repository/git_history.md b/doc/user/project/repository/git_history.md new file mode 100644 index 00000000000..9cd3d0d4ed0 --- /dev/null +++ b/doc/user/project/repository/git_history.md @@ -0,0 +1,67 @@ +--- +type: reference, howto +description: "Documentation on Git file history." +--- + +# Git file history + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/blob/9ba1224867665844b117fa037e1465bb706b3685/app/controllers/commits_controller.rb) in GitLab 0.8.0 + +Git file History provides information about the commit history associated +with a file. + +You can find the **History** button with each file in a project. + +![File history button](img/file_history_button_v12_6.png "History button") + +When you select the **History** button, you'll see a screen with the +noted information: + +![Git log output](img/file_history_output_v12_6.png "History button output") + +If you hover over a commit in the UI, you'll see a precise date and time +that commit was last modified. + +## Associated `git` command + +If you're running `git` from the command line, the equivalent command +is `git log <filename>`. For example, if you want to find `history` +information about a `README.md` file in the local directory, run the +following command: + +```bash +git log README.md +``` + +You'll see output similar to the following, which includes the commit +time in UTC format: + +```bash +commit 0e62ed6d9f39fa9bedf7efc6edd628b137fa781a +Author: Mike Jang <mjang@gitlab.com> +Date: Tue Nov 26 21:44:53 2019 +0000 + + Deemphasize GDK as a doc build tool + +commit 418879420b1e3a4662067bd07b64bb6988654697 +Author: Marcin Sedlak-Jakubowski <msedlakjakubowski@gitlab.com> +Date: Mon Nov 4 19:58:27 2019 +0100 + + Fix typo + +commit 21cc1fef11349417ed515557748369cfb235fc81 +Author: Jacques Erasmus <jerasmus@gitlab.com> +Date: Mon Oct 14 22:13:40 2019 +0000 + + Add support for modern JS + + Added rollup to the project + +commit 2f5e895aebfa5678e51db303b97de56c51e3cebe +Author: Achilleas Pipinellis <axil@gitlab.com> +Date: Fri Sep 13 14:03:01 2019 +0000 + + Remove gitlab-foss Git URLs as we don't need them anymore + + [ci skip] +``` diff --git a/doc/user/project/repository/img/file_blame_button_v12_6.png b/doc/user/project/repository/img/file_blame_button_v12_6.png Binary files differnew file mode 100644 index 00000000000..b5a18e6726f --- /dev/null +++ b/doc/user/project/repository/img/file_blame_button_v12_6.png diff --git a/doc/user/project/repository/img/file_blame_output_v12_6.png b/doc/user/project/repository/img/file_blame_output_v12_6.png Binary files differnew file mode 100644 index 00000000000..4aca40353d5 --- /dev/null +++ b/doc/user/project/repository/img/file_blame_output_v12_6.png diff --git a/doc/user/project/repository/img/file_history_button_v12_6.png b/doc/user/project/repository/img/file_history_button_v12_6.png Binary files differnew file mode 100644 index 00000000000..b5a18e6726f --- /dev/null +++ b/doc/user/project/repository/img/file_history_button_v12_6.png diff --git a/doc/user/project/repository/img/file_history_output_v12_6.png b/doc/user/project/repository/img/file_history_output_v12_6.png Binary files differnew file mode 100644 index 00000000000..9e9855203af --- /dev/null +++ b/doc/user/project/repository/img/file_history_output_v12_6.png diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index cb7fe63db6f..fc422bb5aba 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -48,6 +48,8 @@ it's easier to do so [via GitLab UI](web_editor.md): - [File templates](web_editor.md#template-dropdowns) - [Create a directory](web_editor.md#create-a-directory) - [Start a merge request](web_editor.md#tips) +- [Find file history](git_history.md) +- [Identify changes by line (Git blame)](git_blame.md) **From the command line:** diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 162dda4d044..ecf5ca2d49a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12719,6 +12719,9 @@ msgstr "" msgid "Pipelines|CI Lint" msgstr "" +msgid "Pipelines|Child pipeline" +msgstr "" + msgid "Pipelines|Clear Runner Caches" msgstr "" @@ -12752,9 +12755,15 @@ msgstr "" msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team." msgstr "" +msgid "Pipelines|This is a child pipeline within the parent pipeline" +msgstr "" + msgid "Pipelines|This project is not currently set up to run pipelines." msgstr "" +msgid "Pipelines|parent" +msgstr "" + msgid "Pipeline|Commit" msgstr "" @@ -18265,6 +18274,9 @@ msgstr "" msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again." msgstr "" +msgid "This project is archived and cannot be commented on." +msgstr "" + msgid "This project path either does not exist or is private." msgstr "" diff --git a/package.json b/package.json index 059b6b4445f..b0b1207311a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@gitlab/svgs": "^1.85.0", "@gitlab/ui": "8.2.0", "@gitlab/visual-review-tools": "1.2.0", - "@sentry/browser": "^5.7.1", + "@sentry/browser": "^5.10.2", "@sourcegraph/code-host-integration": "^0.0.14", "apollo-cache-inmemory": "^1.6.3", "apollo-client": "^2.6.4", diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb index dc7fa9f3859..bab6b1ac5fc 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb @@ -3,19 +3,18 @@ module QA context 'Plan' do describe 'Close issue' do - let(:issue_title) { 'issue title' } - let(:commit_message) { 'Closes' } + let(:issue) do + Resource::Issue.fabricate_via_api! do |issue| + issue.title = 'Issue to be closed via pushing a commit' + end + end + + let(:project) { issue.project } + let(:issue_id) { issue.api_response[:iid] } before do Flow::Login.sign_in - issue = Resource::Issue.fabricate_via_api! do |issue| - issue.title = issue_title - end - - @project = issue.project - @issue_id = issue.api_response[:iid] - # Initial commit should be pushed because # the very first commit to the project doesn't close the issue # https://gitlab.com/gitlab-org/gitlab-foss/issues/38965 @@ -23,24 +22,15 @@ module QA end it 'user closes an issue by pushing commit' do - push_commit("#{commit_message} ##{@issue_id}", false) - - @project.visit! - Page::Project::Show.perform do |show| - show.click_commit(commit_message) - end - commit_sha = Page::Project::Commit::Show.perform(&:commit_sha) + push_commit("Closes ##{issue_id}", false) - Page::Project::Menu.perform(&:click_issues) - Page::Project::Issue::Index.perform do |index| - index.click_closed_issues_link - index.click_issue_link(issue_title) - end + issue.visit! Page::Project::Issue::Show.perform do |show| - show.select_all_activities_filter - expect(show).to have_element(:reopen_issue_button) - expect(show).to have_content("closed via commit #{commit_sha}") + reopen_issue_button_visible = show.wait(reload: true) do + show.has_element?(:reopen_issue_button, wait: 1.0) + end + expect(reopen_issue_button_visible).to be_truthy end end @@ -49,7 +39,7 @@ module QA push.commit_message = commit_message push.new_branch = new_branch push.file_content = commit_message - push.project = @project + push.project = project end end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 56c27b4e5eb..4227a4453a3 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -391,13 +391,24 @@ describe AutocompleteController do end context 'user with an accessible merge request but no scope' do - it 'returns an error' do - sign_in(user) + where( + params: [ + {}, + { group_id: ' ' }, + { project_id: ' ' }, + { group_id: ' ', project_id: ' ' } + ] + ) + + with_them do + it 'returns an error' do + sign_in(user) - get :merge_request_target_branches + get :merge_request_target_branches, params: params - expect(response).to have_gitlab_http_status(400) - expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' }) + expect(response).to have_gitlab_http_status(400) + expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' }) + end end end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 34356a2ee90..257e5cb8bf0 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -94,6 +94,14 @@ describe 'Admin Groups' do expect(page).to have_content("Group: #{group.name}") expect(page).to have_content("ID: #{group.id}") end + + it 'has a link to the group' do + group = create(:group, :private) + + visit admin_group_path(group) + + expect(page).to have_link(group.name, href: group_path(group)) + end end describe 'group edit' do diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json index d37f5b864d7..c0eb320e67f 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json +++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json @@ -1,6 +1,9 @@ { "type": "object", - "properties" : { + "required": ["id", "iid", "title", "description", "merge_params", "state", "source_branch", "target_branch", + "diff_head_sha", "create_note_path", "preview_note_path", "can_receive_suggestion", "create_issue_to_resolve_discussions_path", + "new_blob_path", "current_user", "is_project_archived"], + "properties": { "id": { "type": "integer" }, "iid": { "type": "integer" }, "title": { "type": "string" }, @@ -26,7 +29,10 @@ "can_update": { "type": "boolean" } }, "additionalProperties": false - } + }, + "is_project_archived": { "type": "boolean" }, + "locked_discussion_docs_path": { "type": "string" }, + "archived_project_docs_path": { "type": "string" } }, "additionalProperties": false } diff --git a/spec/frontend/monitoring/panel_type_spec.js b/spec/frontend/monitoring/panel_type_spec.js index 7adecc56d18..c869d77673e 100644 --- a/spec/frontend/monitoring/panel_type_spec.js +++ b/spec/frontend/monitoring/panel_type_spec.js @@ -20,6 +20,16 @@ describe('Panel Type component', () => { const dashboardWidth = 100; const exampleText = 'example_text'; + const createWrapper = props => + shallowMount(PanelType, { + propsData: { + ...props, + }, + store, + sync: false, + attachToDocument: true, + }); + beforeEach(() => { setTestTimeout(1000); axiosMock = new AxiosMockAdapter(axios); @@ -36,14 +46,9 @@ describe('Panel Type component', () => { graphDataNoResult.metrics[0].result = []; beforeEach(() => { - panelType = shallowMount(PanelType, { - propsData: { - clipboardText: 'dashboard_link', - dashboardWidth, - graphData: graphDataNoResult, - }, - sync: false, - attachToDocument: true, + panelType = createWrapper({ + dashboardWidth, + graphData: graphDataNoResult, }); }); @@ -68,41 +73,30 @@ describe('Panel Type component', () => { }); }); - describe('when Graph data is available', () => { - const propsData = { - clipboardText: exampleText, - dashboardWidth, - graphData: graphDataPrometheusQueryRange, - }; - - beforeEach(done => { + describe('when graph data is available', () => { + beforeEach(() => { store = createStore(); - panelType = shallowMount(PanelType, { - propsData, - store, - sync: false, - attachToDocument: true, + panelType = createWrapper({ + dashboardWidth, + graphData: graphDataPrometheusQueryRange, }); - panelType.vm.$nextTick(done); }); afterEach(() => { panelType.destroy(); }); + it('sets no clipboard copy link on dropdown by default', () => { + const link = () => panelType.find('.js-chart-link'); + expect(link().exists()).toBe(false); + }); + describe('Time Series Chart panel type', () => { it('is rendered', () => { expect(panelType.find(TimeSeriesChart).isVueInstance()).toBe(true); expect(panelType.find(TimeSeriesChart).exists()).toBe(true); }); - it('sets clipboard text on the dropdown', () => { - const link = () => panelType.find('.js-chart-link'); - const clipboardText = () => link().element.dataset.clipboardText; - - expect(clipboardText()).toBe(exampleText); - }); - it('includes a default group id', () => { expect(panelType.vm.groupId).toBe('panel-type-chart'); }); @@ -123,6 +117,30 @@ describe('Panel Type component', () => { }); }); + describe('when cliboard data is available', () => { + const clipboardText = 'A value to copy.'; + + beforeEach(() => { + store = createStore(); + panelType = createWrapper({ + clipboardText, + dashboardWidth, + graphData: graphDataPrometheusQueryRange, + }); + }); + + afterEach(() => { + panelType.destroy(); + }); + + it('sets clipboard text on the dropdown', () => { + const link = () => panelType.find('.js-chart-link'); + + expect(link().exists()).toBe(true); + expect(link().element.dataset.clipboardText).toBe(clipboardText); + }); + }); + describe('when downloading metrics data as CSV', () => { beforeEach(done => { graphDataPrometheusQueryRange.y_label = 'metric'; diff --git a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js index d800649bc1d..9b2e2e38366 100644 --- a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js +++ b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js @@ -1,12 +1,11 @@ import { shallowMount } from '@vue/test-utils'; import { TEST_HOST } from 'helpers/test_constants'; -import { joinPaths } from '~/lib/utils/url_utility'; import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue'; import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue'; import userDataMock from '../../user_data_mock'; const TOOLTIP_PLACEMENT = 'bottom'; -const { name: USER_NAME, username: USER_USERNAME } = userDataMock(); +const { name: USER_NAME } = userDataMock(); const TEST_ISSUABLE_TYPE = 'merge_request'; describe('AssigneeAvatarLink component', () => { @@ -38,9 +37,8 @@ describe('AssigneeAvatarLink component', () => { it('has the root url present in the assigneeUrl method', () => { createComponent(); - const assigneeUrl = joinPaths(TEST_HOST, USER_USERNAME); - expect(wrapper.attributes().href).toEqual(assigneeUrl); + expect(wrapper.attributes().href).toEqual(userDataMock().web_url); }); it('renders assignee avatar', () => { diff --git a/spec/frontend/sidebar/user_data_mock.js b/spec/frontend/sidebar/user_data_mock.js index 8ad70bb3499..df90a65f6f9 100644 --- a/spec/frontend/sidebar/user_data_mock.js +++ b/spec/frontend/sidebar/user_data_mock.js @@ -1,9 +1,11 @@ +import { TEST_HOST } from 'helpers/test_constants'; + export default () => ({ - avatar_url: 'mock_path', + avatar_url: `${TEST_HOST}/avatar/root.png`, id: 1, name: 'Root', state: 'active', username: 'root', - web_url: '', + web_url: `${TEST_HOST}/root`, can_merge: true, }); diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index 224ed0b402f..a1868b2631b 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -92,4 +92,36 @@ describe IssueEntity do end end end + + context 'when issuable in active or archived project' do + before do + project.add_developer(user) + end + + context 'when project is active' do + it 'returns archived false' do + expect(subject[:is_project_archived]).to eq(false) + end + + it 'returns nil for archived project doc' do + response = described_class.new(resource, request: request).as_json + + expect(response[:archived_project_docs_path]).to be nil + end + end + + context 'when project is archived' do + before do + project.update(archived: true) + end + + it 'returns archived true' do + expect(subject[:is_project_archived]).to eq(true) + end + + it 'returns archived project doc' do + expect(subject[:archived_project_docs_path]).to eq('/help/user/project/settings/index.md#archiving-a-project') + end + end + end end diff --git a/spec/serializers/merge_request_serializer_spec.rb b/spec/serializers/merge_request_serializer_spec.rb index a99f11168c0..9297df31842 100644 --- a/spec/serializers/merge_request_serializer_spec.rb +++ b/spec/serializers/merge_request_serializer_spec.rb @@ -48,7 +48,24 @@ describe MergeRequestSerializer do let(:serializer) { 'noteable' } it 'matches noteable merge request json schema' do - expect(json_entity).to match_schema('entities/merge_request_noteable', strict: true) + expect(json_entity).to match_schema('entities/merge_request_noteable') + end + + context 'when merge_request is locked' do + let(:resource) { create(:merge_request, :locked, description: "Description") } + + it 'matches noteable merge request json schema' do + expect(json_entity).to match_schema('entities/merge_request_noteable') + end + end + + context 'when project is archived' do + let(:project) { create(:project, :archived, :repository) } + let(:resource) { create(:merge_request, source_project: project, target_project: project, description: "Description") } + + it 'matches noteable merge request json schema' do + expect(json_entity).to match_schema('entities/merge_request_noteable') + end end end diff --git a/yarn.lock b/yarn.lock index 5b6038b6e04..be6b64930f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -918,56 +918,56 @@ consola "^2.3.0" node-fetch "^2.3.0" -"@sentry/browser@^5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.7.1.tgz#1f8435e2a325d7a09f830065ebce40a2b3c708a4" - integrity sha512-K0x1XhsHS8PPdtlVOLrKZyYvi5Vexs9WApdd214bO6KaGF296gJvH1mG8XOY0+7aA5i2A7T3ttcaJNDYS49lzw== - dependencies: - "@sentry/core" "5.7.1" - "@sentry/types" "5.7.1" - "@sentry/utils" "5.7.1" +"@sentry/browser@^5.10.2": + version "5.10.2" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.10.2.tgz#0bbb05505c58ea998c833cffec3f922fe4b4fa58" + integrity sha512-r3eyBu2ln7odvWtXARCZPzpuGrKsD6U9F3gKTu4xdFkA0swSLUvS7AC2FUksj/1BE23y+eB/zzPT+RYJ58tidA== + dependencies: + "@sentry/core" "5.10.2" + "@sentry/types" "5.10.0" + "@sentry/utils" "5.10.2" tslib "^1.9.3" -"@sentry/core@5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.1.tgz#3eb2b7662cac68245931ee939ec809bf7a639d0e" - integrity sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg== +"@sentry/core@5.10.2": + version "5.10.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.10.2.tgz#1cb64489e6f8363c3249415b49d3f1289814825f" + integrity sha512-sKVeFH3v8K8xw2vM5MKMnnyAAwih+JSE3pbNL0CcCCA+/SwX+3jeAo2BhgXev2SAR/TjWW+wmeC9TdIW7KyYbg== dependencies: - "@sentry/hub" "5.7.1" - "@sentry/minimal" "5.7.1" - "@sentry/types" "5.7.1" - "@sentry/utils" "5.7.1" + "@sentry/hub" "5.10.2" + "@sentry/minimal" "5.10.2" + "@sentry/types" "5.10.0" + "@sentry/utils" "5.10.2" tslib "^1.9.3" -"@sentry/hub@5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.1.tgz#a52acd9fead7f3779d96e9965c6978aecc8b9cad" - integrity sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig== +"@sentry/hub@5.10.2": + version "5.10.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.10.2.tgz#25d9f36b8f7c5cb65cf486737fa61dc9bf69b7e3" + integrity sha512-hSlZIiu3hcR/I5yEhlpN9C0nip+U7hiRzRzUQaBiHO4YG4TC58NqnOPR89D/ekiuHIXzFpjW9OQmqtAMRoSUYA== dependencies: - "@sentry/types" "5.7.1" - "@sentry/utils" "5.7.1" + "@sentry/types" "5.10.0" + "@sentry/utils" "5.10.2" tslib "^1.9.3" -"@sentry/minimal@5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.1.tgz#56afc537737586929e25349765e37a367958c1e1" - integrity sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g== +"@sentry/minimal@5.10.2": + version "5.10.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.10.2.tgz#267c2f3aa6877a0fe7a86971942e83f3ee616580" + integrity sha512-GalixiM9sckYfompH5HHTp9XT2BcjawBkcl1DMEKUBEi37+kUq0bivOBmnN1G/I4/wWOUdnAI/kagDWaWpbZPg== dependencies: - "@sentry/hub" "5.7.1" - "@sentry/types" "5.7.1" + "@sentry/hub" "5.10.2" + "@sentry/types" "5.10.0" tslib "^1.9.3" -"@sentry/types@5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.1.tgz#4c4c1d4d891b6b8c2c3c7b367d306a8b1350f090" - integrity sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ== +"@sentry/types@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.10.0.tgz#4f0ba31b6e4d5371112c38279f11f66c73b43746" + integrity sha512-TW20GzkCWsP6uAxR2JIpIkiitCKyIOfkyDsKBeLqYj4SaZjfvBPnzgNCcYR0L0UsP1/Es6oHooZfIGSkp6GGxQ== -"@sentry/utils@5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.1.tgz#cf37ad55f78e317665cd8680f202d307fa77f1d0" - integrity sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q== +"@sentry/utils@5.10.2": + version "5.10.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.10.2.tgz#261f575079d30aaf604e59f5f4de0aa21db22252" + integrity sha512-UcbbaFpYrGSV448lQ16Cr+W/MPuKUflQQUdrMCt5vgaf5+M7kpozlcji4GGGZUCXIA7oRP93ABoXj55s1OM9zw== dependencies: - "@sentry/types" "5.7.1" + "@sentry/types" "5.10.0" tslib "^1.9.3" "@sourcegraph/code-host-integration@^0.0.14": |