Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-05 00:10:20 +00:00
parent 9248363e3e
commit b28aa8bd7d
32 changed files with 225 additions and 125 deletions

View file

@ -11,7 +11,17 @@ function broadcastCount(newCount) {
} }
function updateUserMergeRequestCounts(newCount) { function updateUserMergeRequestCounts(newCount) {
const mergeRequestsCountEl = document.querySelector('.merge-requests-count'); const mergeRequestsCountEl = document.querySelector('.js-assigned-mr-count');
mergeRequestsCountEl.textContent = newCount.toLocaleString();
}
function updateReviewerMergeRequestCounts(newCount) {
const mergeRequestsCountEl = document.querySelector('.js-reviewer-mr-count');
mergeRequestsCountEl.textContent = newCount.toLocaleString();
}
function updateMergeRequestCounts(newCount) {
const mergeRequestsCountEl = document.querySelector('.js-merge-requests-count');
mergeRequestsCountEl.textContent = newCount.toLocaleString(); mergeRequestsCountEl.textContent = newCount.toLocaleString();
mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0); mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0);
} }
@ -22,10 +32,14 @@ function updateUserMergeRequestCounts(newCount) {
export function refreshUserMergeRequestCounts() { export function refreshUserMergeRequestCounts() {
return Api.userCounts() return Api.userCounts()
.then(({ data }) => { .then(({ data }) => {
const count = data.merge_requests; const assignedMergeRequests = data.assigned_merge_requests;
const reviewerMergeRequests = data.review_requested_merge_requests;
const fullCount = assignedMergeRequests + reviewerMergeRequests;
updateUserMergeRequestCounts(count); updateUserMergeRequestCounts(assignedMergeRequests);
broadcastCount(count); updateReviewerMergeRequestCounts(reviewerMergeRequests);
updateMergeRequestCounts(fullCount);
broadcastCount(fullCount);
}) })
.catch((ex) => { .catch((ex) => {
console.error(ex); // eslint-disable-line no-console console.error(ex); // eslint-disable-line no-console
@ -60,7 +74,7 @@ export function openUserCountsBroadcast() {
if (currentUserId) { if (currentUserId) {
channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`); channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`);
channel.onmessage = (ev) => { channel.onmessage = (ev) => {
updateUserMergeRequestCounts(ev.data); updateMergeRequestCounts(ev.data);
}; };
} }
} }

View file

@ -2,6 +2,7 @@
// NOTE! For the first iteration, we are simply copying the implementation of Assignees // NOTE! For the first iteration, we are simply copying the implementation of Assignees
// It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736 // It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736
import { deprecatedCreateFlash as Flash } from '~/flash'; import { deprecatedCreateFlash as Flash } from '~/flash';
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import eventHub from '~/sidebar/event_hub'; import eventHub from '~/sidebar/event_hub';
import Store from '~/sidebar/stores/sidebar_store'; import Store from '~/sidebar/stores/sidebar_store';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@ -80,8 +81,7 @@ export default {
.saveReviewers(this.field) .saveReviewers(this.field)
.then(() => { .then(() => {
this.loading = false; this.loading = false;
// Uncomment once this issue has been addressed > https://gitlab.com/gitlab-org/gitlab/-/issues/237922 refreshUserMergeRequestCounts();
// refreshUserMergeRequestCounts();
}) })
.catch(() => { .catch(() => {
this.loading = false; this.loading = false;

View file

@ -796,6 +796,14 @@
.navbar-gitlab { .navbar-gitlab {
li.dropdown { li.dropdown {
position: static; position: static;
&.user-counter {
margin-left: 8px !important;
> a {
padding: 0 4px !important;
}
}
} }
} }

View file

@ -172,7 +172,7 @@
} }
li { li {
.badge.badge-pill { .badge.badge-pill:not(.merge-request-badge) {
box-shadow: none; box-shadow: none;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
@ -438,7 +438,7 @@
.title-container, .title-container,
.navbar-nav { .navbar-nav {
.badge.badge-pill { .badge.badge-pill:not(.merge-request-badge) {
position: inherit; position: inherit;
font-weight: $gl-font-weight-normal; font-weight: $gl-font-weight-normal;
margin-left: -6px; margin-left: -6px;

View file

@ -11,6 +11,10 @@ module DashboardHelper
merge_requests_dashboard_path(assignee_username: current_user.username) merge_requests_dashboard_path(assignee_username: current_user.username)
end end
def reviewer_mrs_dashboard_path
merge_requests_dashboard_path(reviewer_username: current_user.username)
end
def dashboard_nav_links def dashboard_nav_links
@dashboard_nav_links ||= get_dashboard_nav_links @dashboard_nav_links ||= get_dashboard_nav_links
end end

View file

@ -159,6 +159,32 @@ module MergeRequestsHelper
issuable_path(issuable, { merge_request: { wip_event: wip_event } }) issuable_path(issuable, { merge_request: { wip_event: wip_event } })
end end
def user_merge_requests_counts
@user_merge_requests_counts ||= begin
assigned_count = assigned_issuables_count(:merge_requests)
review_requested_count = review_requested_merge_requests_count
total_count = assigned_count + review_requested_count
{
assigned: assigned_count,
review_requested: review_requested_count,
total: total_count
}
end
end
def merge_request_reviewers_enabled?
Feature.enabled?(:merge_request_reviewers, default_enabled: :yaml)
end
private
def review_requested_merge_requests_count
return 0 unless merge_request_reviewers_enabled?
current_user.review_requested_open_merge_requests_count
end
end end
MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper') MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper')

View file

@ -1564,6 +1564,12 @@ class User < ApplicationRecord
end end
end end
def review_requested_open_merge_requests_count(force: false)
Rails.cache.fetch(['users', id, 'review_requested_open_merge_requests_count'], force: force, expires_in: 20.minutes) do
MergeRequestsFinder.new(self, reviewer_id: id, state: 'opened', non_archived: true).execute.count
end
end
def assigned_open_issues_count(force: false) def assigned_open_issues_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do
IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
@ -1607,6 +1613,7 @@ class User < ApplicationRecord
def invalidate_merge_request_cache_counts def invalidate_merge_request_cache_counts
Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count']) Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count'])
Rails.cache.delete(['users', id, 'review_requested_open_merge_requests_count'])
end end
def invalidate_todos_done_count def invalidate_todos_done_count

View file

@ -135,10 +135,6 @@ class ProjectPolicy < BasePolicy
::Feature.enabled?(:build_service_proxy, @subject) ::Feature.enabled?(:build_service_proxy, @subject)
end end
condition(:project_bot_is_member) do
user.project_bot? & team_member?
end
with_scope :subject with_scope :subject
condition(:packages_disabled) { !@subject.packages_enabled } condition(:packages_disabled) { !@subject.packages_enabled }
@ -619,8 +615,6 @@ class ProjectPolicy < BasePolicy
enable :admin_resource_access_tokens enable :admin_resource_access_tokens
end end
rule { project_bot_is_member & ~blocked }.enable :bot_log_in
private private
def user_is_user? def user_is_user?

View file

@ -112,9 +112,11 @@ module MergeRequests
end end
def handle_reviewers_change(merge_request, old_reviewers) def handle_reviewers_change(merge_request, old_reviewers)
affected_reviewers = (old_reviewers + merge_request.reviewers) - (old_reviewers & merge_request.reviewers)
create_reviewer_note(merge_request, old_reviewers) create_reviewer_note(merge_request, old_reviewers)
notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers) notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers)
todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers) todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers)
invalidate_cache_counts(merge_request, users: affected_reviewers.compact)
end end
def create_branch_change_note(issuable, branch_type, old_branch, new_branch) def create_branch_change_note(issuable, branch_type, old_branch, new_branch)

View file

@ -47,17 +47,36 @@
%span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) } %span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) }
= number_with_delimiter(issues_count) = number_with_delimiter(issues_count)
- if header_link?(:merge_requests) - if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do - reviewers_enabled = merge_request_reviewers_enabled?
= link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _('Merge requests') }, = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter #{reviewers_enabled ? 'dropdown' : ''}" }) do
data: { qa_selector: 'merge_requests_shortcut_button', toggle: 'tooltip', placement: 'bottom', = link_to assigned_mrs_dashboard_path, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') },
data: { qa_selector: 'merge_requests_shortcut_button',
toggle: reviewers_enabled ? "dropdown" : "tooltip",
placement: 'bottom',
track_label: 'main_navigation', track_label: 'main_navigation',
track_event: 'click_merge_link', track_event: 'click_merge_link',
track_property: 'navigation', track_property: 'navigation',
container: 'body' } do container: 'body' } do
= sprite_icon('git-merge') = sprite_icon('git-merge')
- merge_requests_count = assigned_issuables_count(:merge_requests) %span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) }
%span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count == 0) } = number_with_delimiter(user_merge_requests_counts[:total])
= number_with_delimiter(merge_requests_count) - if reviewers_enabled
= sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!')
- if reviewers_enabled
.dropdown-menu.dropdown-menu-right
%ul
%li.dropdown-header
= _('Merge requests')
%li
= link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do
= _('Assigned to you')
%span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" }
= user_merge_requests_counts[:assigned]
%li
= link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do
= _('Review requests for you')
%span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" }
= user_merge_requests_counts[:review_requested]
- if header_link?(:todos) - if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
= link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos', = link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos',

View file

@ -0,0 +1,5 @@
---
title: Fix project access token regression
merge_request: 50800
author:
type: fixed

View file

@ -1,5 +0,0 @@
---
title: Fix empty pipeline analytics charts when time_zone is non-UTC
merge_request: 50760
author:
type: fixed

View file

@ -459,11 +459,11 @@ Cluster.
| `alert` | Hash | yes | Alerts detail. Currently same format as [3rd party alert](../operations/incident_management/alert_integrations.md#customize-the-alert-payload-outside-of-gitlab). | | `alert` | Hash | yes | Alerts detail. Currently same format as [3rd party alert](../operations/incident_management/alert_integrations.md#customize-the-alert-payload-outside-of-gitlab). |
```plaintext ```plaintext
POST internal/kubernetes/modules/cilium/network_alert POST internal/kubernetes/modules/cilium_alert
``` ```
Example Request: Example Request:
```shell ```shell
curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium/network_alert" curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium_alert"
``` ```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -42,18 +42,21 @@ Otherwise, you can:
1. Navigate to the **License** tab, and click **Upload New License**. 1. Navigate to the **License** tab, and click **Upload New License**.
![License Admin Area](img/license_admin_area.png) - *If you've received a `.gitlab-license` file:*
1. Download the license file to your local machine.
1. Select **Upload `.gitlab-license` file**.
1. Select **Choose File** and select the license file.
In this example the license file is named `GitLab.gitlab-license`.
1. Check the **Subscription Agreement** checkbox.
1. Select **Upload License**.
- *If you've received a `.gitlab-license` file,* you should have already downloaded ![Upload license](img/license_upload_v13_8.png)
it in your local machine. You can then upload it directly by choosing the
license file and clicking the **Upload license** button. In the image below,
the selected license file is named `GitLab.gitlab-license`.
![Upload license](img/license_upload.png) - *If you've received your license as plain text:*
1. Select **Enter license key**.
- *If you've received your license as plain text,* select the 1. Copy the license and paste it into the **License key** field.
**Enter license key** option, copy the license, paste it into the **License key** 1. Check the **Subscription Agreement** checkbox.
field, and click **Upload license**. 1. Select **Upload License**.
## Add your license at install time ## Add your license at install time

View file

@ -47,6 +47,9 @@ To enforce confirmation of the email address used for new sign ups:
1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**. 1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**.
1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**. 1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**.
In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/273258), if an administrator disables this setting, the users in pending approval state are
automatically approved in a background job.
## User cap **(CORE ONLY)** ## User cap **(CORE ONLY)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6. > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6.

View file

@ -102,7 +102,7 @@ To authenticate to the Package Registry, you must use one of the following:
- It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow). - It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers. Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers.
- A [CI job token](#authenticate-with-a-ci-job-token). - A [CI job token](#authenticate-with-a-ci-job-token).
- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case. - Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case.
### Authenticate with a personal access token or deploy token ### Authenticate with a personal access token or deploy token
@ -201,7 +201,7 @@ Then, you can run `npm publish` either locally or by using GitLab CI/CD.
## Package naming convention ## Package naming convention
Your NPM package name must be in the format of `@scope:package-name`. Your NPM package name must be in the format of `@scope/package-name`.
- The `@scope` is the root namespace of the GitLab project. It must match exactly, including the case. - The `@scope` is the root namespace of the GitLab project. It must match exactly, including the case.
- The `package-name` can be whatever you want. - The `package-name` can be whatever you want.
@ -241,7 +241,7 @@ Prerequisites:
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry. - [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages). - Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case. - Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case.
To upload an NPM package to your project, run this command: To upload an NPM package to your project, run this command:
@ -461,7 +461,7 @@ If you get this error, ensure that:
### `npm publish` returns `npm ERR! 400 Bad Request` ### `npm publish` returns `npm ERR! 400 Bad Request`
If you get this error, your package name may not meet the If you get this error, your package name may not meet the
[@scope:package-name package naming convention](#package-naming-convention). [@scope/package-name package naming convention](#package-naming-convention).
Ensure the name meets the convention exactly, including the case. Ensure the name meets the convention exactly, including the case.
Then try to publish again. Then try to publish again.

View file

@ -235,9 +235,12 @@ password = ${env.CI_JOB_TOKEN}
When publishing packages, note that: When publishing packages, note that:
- The maximum allowed size is 50 MB. - You must [authenticate with the Package Registry](#authenticate-with-the-package-registry).
- Your [version string must be valid](#ensure-your-version-string-is-valid).
- The maximum allowed package size is 5 GB.
- You can't upload the same version of a package multiple times. If you try, - You can't upload the same version of a package multiple times. If you try,
you receive the error `Validation failed: File name has already been taken`. you receive the error `Validation failed: File name has already been taken`.
- You cannot publish PyPI packages to a group, only to a project.
### Ensure your version string is valid ### Ensure your version string is valid

View file

@ -12,7 +12,9 @@ module API
unauthorized! unless current_user unauthorized! unless current_user
{ {
merge_requests: current_user.assigned_open_merge_requests_count merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count
} }
end end
end end

View file

@ -198,7 +198,9 @@ module Gitlab
return unless valid_scoped_token?(token, all_available_scopes) return unless valid_scoped_token?(token, all_available_scopes)
if token.user.can?(:log_in) || token.user.can?(:bot_log_in, project) return if project && token.user.project_bot? && !project.bots.include?(token.user)
if token.user.can?(:log_in) || token.user.project_bot?
Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes)) Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
end end
end end
@ -283,7 +285,7 @@ module Gitlab
return unless build.project.builds_enabled? return unless build.project.builds_enabled?
if build.user if build.user
return unless build.user.can?(:log_in) || build.user.can?(:bot_log_in, build.project) return unless build.user.can?(:log_in) || (build.user.project_bot? && build.project.bots&.include?(build.user))
# If user is assigned to build, use restricted credentials of user # If user is assigned to build, use restricted credentials of user
Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities) Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)

View file

@ -31,10 +31,9 @@ module Gitlab
current = @from current = @from
while current <= @to while current <= @to
label = current.strftime(@format) @labels << current.strftime(@format)
@labels << label @total << (totals_count[current] || 0)
@total << (totals_count[label] || 0) @success << (success_count[current] || 0)
@success << (success_count[label] || 0)
current += interval_step current += interval_step
end end
@ -46,7 +45,6 @@ module Gitlab
query query
.group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)") .group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)")
.count(:created_at) .count(:created_at)
.transform_keys { |date| date.strftime(@format) }
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord

View file

@ -3943,6 +3943,9 @@ msgstr ""
msgid "Assigned to me" msgid "Assigned to me"
msgstr "" msgstr ""
msgid "Assigned to you"
msgstr ""
msgid "Assignee" msgid "Assignee"
msgid_plural "%d Assignees" msgid_plural "%d Assignees"
msgstr[0] "" msgstr[0] ""
@ -24032,6 +24035,9 @@ msgstr ""
msgid "Review requested from %{name}" msgid "Review requested from %{name}"
msgstr "" msgstr ""
msgid "Review requests for you"
msgstr ""
msgid "Review the changes locally" msgid "Review the changes locally"
msgstr "" msgstr ""

View file

@ -110,6 +110,12 @@ RSpec.describe 'Dashboard Merge Requests' do
visit merge_requests_dashboard_path(assignee_username: current_user.username) visit merge_requests_dashboard_path(assignee_username: current_user.username)
end end
it 'includes assigned and reviewers in badge' do
expect(find('.merge-requests-count')).to have_content('3')
expect(find('.js-assigned-mr-count')).to have_content('2')
expect(find('.js-reviewer-mr-count')).to have_content('1')
end
it 'shows assigned merge requests' do it 'shows assigned merge requests' do
expect(page).to have_content(assigned_merge_request.title) expect(page).to have_content(assigned_merge_request.title)
expect(page).to have_content(assigned_merge_request_from_fork.title) expect(page).to have_content(assigned_merge_request_from_fork.title)

View file

@ -8,7 +8,7 @@ import Api from '~/api';
jest.mock('~/api'); jest.mock('~/api');
const TEST_COUNT = 1000; const TEST_COUNT = 1000;
const MR_COUNT_CLASS = 'merge-requests-count'; const MR_COUNT_CLASS = 'js-merge-requests-count';
describe('User Merge Requests', () => { describe('User Merge Requests', () => {
let channelMock; let channelMock;
@ -24,7 +24,9 @@ describe('User Merge Requests', () => {
newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock); newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock);
global.BroadcastChannel = newBroadcastChannelMock; global.BroadcastChannel = newBroadcastChannelMock;
setFixtures(`<div class="${MR_COUNT_CLASS}">0</div>`); setFixtures(
`<div><div class="${MR_COUNT_CLASS}">0</div><div class="js-assigned-mr-count"></div><div class="js-reviewer-mr-count"></div></div>`,
);
}); });
const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent; const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent;
@ -33,7 +35,10 @@ describe('User Merge Requests', () => {
beforeEach(() => { beforeEach(() => {
Api.userCounts.mockReturnValue( Api.userCounts.mockReturnValue(
Promise.resolve({ Promise.resolve({
data: { merge_requests: TEST_COUNT }, data: {
assigned_merge_requests: TEST_COUNT,
review_requested_merge_requests: TEST_COUNT,
},
}), }),
); );
}); });
@ -46,7 +51,7 @@ describe('User Merge Requests', () => {
}); });
it('updates the top count of merge requests', () => { it('updates the top count of merge requests', () => {
expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString()); expect(findMRCountText()).toEqual(Number(TEST_COUNT + TEST_COUNT).toLocaleString());
}); });
it('calls the API', () => { it('calls the API', () => {
@ -54,7 +59,7 @@ describe('User Merge Requests', () => {
}); });
it('posts count to BroadcastChannel', () => { it('posts count to BroadcastChannel', () => {
expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT); expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT + TEST_COUNT);
}); });
}); });

View file

@ -89,4 +89,10 @@ RSpec.describe DashboardHelper do
it { is_expected.to eq(false) } it { is_expected.to eq(false) }
end end
describe '#reviewer_mrs_dashboard_path' do
subject { helper.reviewer_mrs_dashboard_path }
it { is_expected.to eq(merge_requests_dashboard_path(reviewer_username: user.username)) }
end
end end

View file

@ -67,4 +67,37 @@ RSpec.describe MergeRequestsHelper do
end end
end end
end end
describe '#user_merge_requests_counts' do
let(:user) do
double(
assigned_open_merge_requests_count: 1,
review_requested_open_merge_requests_count: 2
)
end
subject { helper.user_merge_requests_counts }
before do
allow(helper).to receive(:current_user).and_return(user)
end
it "returns assigned, review requested and total merge request counts" do
expect(subject).to eq(
assigned: user.assigned_open_merge_requests_count,
review_requested: user.review_requested_open_merge_requests_count,
total: user.assigned_open_merge_requests_count + user.review_requested_open_merge_requests_count
)
end
context 'when merge_request_reviewers is disabled' do
before do
stub_feature_flags(merge_request_reviewers: false)
end
it 'returns review_requested as 0' do
expect(subject[:review_requested]).to eq(0)
end
end
end
end end

View file

@ -47,10 +47,6 @@ RSpec.describe Gitlab::Ci::Charts do
subject { chart.to } subject { chart.to }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'includes the whole current day' do it 'includes the whole current day' do
is_expected.to eq(Date.today.end_of_day) is_expected.to eq(Date.today.end_of_day)
end end
@ -62,37 +58,6 @@ RSpec.describe Gitlab::Ci::Charts do
it 'uses %d %B as labels format' do it 'uses %d %B as labels format' do
expect(chart.labels).to include(chart.from.strftime('%d %B')) expect(chart.labels).to include(chart.from.strftime('%d %B'))
end end
it 'returns count of pipelines run each day in the current week' do
expect(chart.total).to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1)
end
end
context 'weekchart_non_utc' do
today = Date.today
end_of_today = Time.use_zone(Time.find_zone('Asia/Dubai')) { today.end_of_day }
let(:project) { create(:project) }
let(:chart) do
allow(Date).to receive(:today).and_return(today)
allow(today).to receive(:end_of_day).and_return(end_of_today)
Gitlab::Ci::Charts::WeekChart.new(project)
end
subject { chart.total }
before do
create(:ci_empty_pipeline, project: project, duration: 120)
end
it 'uses a non-utc time zone for range times' do
expect(chart.to.zone).to eq(end_of_today.zone)
expect(chart.from.zone).to eq(end_of_today.zone)
end
it 'returns count of pipelines run each day in the current week' do
is_expected.to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1)
end
end end
context 'pipeline_times' do context 'pipeline_times' do

View file

@ -4084,6 +4084,7 @@ RSpec.describe User do
cache_mock = double cache_mock = double
expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count']) expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count'])
expect(cache_mock).to receive(:delete).with(['users', user.id, 'review_requested_open_merge_requests_count'])
allow(Rails).to receive(:cache).and_return(cache_mock) allow(Rails).to receive(:cache).and_return(cache_mock)
@ -4163,6 +4164,20 @@ RSpec.describe User do
end end
end end
describe '#review_requested_open_merge_requests_count' do
it 'returns number of open merge requests from non-archived projects' do
user = create(:user)
project = create(:project, :public)
archived_project = create(:project, :public, :archived)
create(:merge_request, source_project: project, author: user, reviewers: [user])
create(:merge_request, :closed, source_project: project, author: user, reviewers: [user])
create(:merge_request, source_project: archived_project, author: user, reviewers: [user])
expect(user.review_requested_open_merge_requests_count(force: true)).to eq 1
end
end
describe '#assigned_open_issues_count' do describe '#assigned_open_issues_count' do
it 'returns number of open issues from non-archived projects' do it 'returns number of open issues from non-archived projects' do
user = create(:user) user = create(:user)

View file

@ -401,40 +401,6 @@ RSpec.describe ProjectPolicy do
end end
end end
describe 'bot_log_in' do
let(:bot_user) { create(:user, :project_bot) }
let(:project) { private_project }
context 'when bot is in project and is not blocked' do
before do
project.add_maintainer(bot_user)
end
it 'is a valid project bot' do
expect(bot_user.can?(:bot_log_in, project)).to be_truthy
end
end
context 'when project bot is invalid' do
context 'when bot is not in project' do
it 'is not a valid project bot' do
expect(bot_user.can?(:bot_log_in, project)).to be_falsy
end
end
context 'when bot user is blocked' do
before do
project.add_maintainer(bot_user)
bot_user.block!
end
it 'is not a valid project bot' do
expect(bot_user.can?(:bot_log_in, project)).to be_falsy
end
end
end
end
context 'support bot' do context 'support bot' do
let(:current_user) { User.support_bot } let(:current_user) { User.support_bot }

View file

@ -521,6 +521,19 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
should_email(user2) should_email(user2)
should_email(user3) should_email(user3)
end end
it 'updates open merge request counter for reviewers', :use_clean_rails_memory_store_caching do
merge_request.reviewers = [user3]
# Cache them to ensure the cache gets invalidated on update
expect(user2.review_requested_open_merge_requests_count).to eq(0)
expect(user3.review_requested_open_merge_requests_count).to eq(1)
update_merge_request(reviewer_ids: [user2.id])
expect(user2.review_requested_open_merge_requests_count).to eq(1)
expect(user3.review_requested_open_merge_requests_count).to eq(0)
end
end end
context 'when the milestone is removed' do context 'when the milestone is removed' do