Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-06 00:09:06 +00:00
parent da07b341fd
commit 962af1ccc6
25 changed files with 397 additions and 70 deletions

View file

@ -808,10 +808,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
- ee/spec/workers/set_user_status_based_on_user_cap_setting_worker_spec.rb
- spec/controllers/confirmations_controller_spec.rb
- spec/controllers/dashboard/projects_controller_spec.rb
- spec/controllers/groups/dependency_proxy_auth_controller_spec.rb
- spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
- spec/controllers/groups/registry/repositories_controller_spec.rb
- spec/controllers/groups/variables_controller_spec.rb
- spec/controllers/invites_controller_spec.rb
- spec/controllers/profiles/emails_controller_spec.rb
- spec/controllers/projects/alerting/notifications_controller_spec.rb

View file

@ -22,16 +22,13 @@ export default {
GlAlert,
},
mixins: [glFeatureFlagMixin()],
inject: ['canAdminList'],
props: {
lists: {
type: Array,
required: false,
default: () => [],
},
canAdminList: {
type: Boolean,
required: true,
},
disabled: {
type: Boolean,
required: true,
@ -99,7 +96,7 @@ export default {
</script>
<template>
<div>
<div v-cloak data-qa-selector="boards_list">
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
{{ error }}
</gl-alert>

View file

@ -22,13 +22,7 @@ export default {
import('ee_component/boards/components/board_settings_list_types.vue'),
},
mixins: [glFeatureFlagMixin()],
props: {
canAdminList: {
type: Boolean,
required: false,
default: false,
},
},
inject: ['canAdminList'],
data() {
return {
ListType,

View file

@ -81,6 +81,7 @@ export default () => {
rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null,
canUpdate: parseBoolean($boardApp.dataset.canUpdate),
canAdminList: parseBoolean($boardApp.dataset.canAdminList),
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,

View file

@ -228,7 +228,7 @@
padding: 0 2px;
svg {
margin-bottom: 1px;
margin-bottom: 3px;
height: $default-icon-size;
width: $default-icon-size;
border-radius: 50%;

View file

@ -14,7 +14,8 @@ module BoardsHelper
root_path: root_path,
full_path: full_path,
bulk_update_path: @bulk_issues_path,
can_update: (!!can?(current_user, :admin_issue, board)).to_s,
can_update: can_update?.to_s,
can_admin_list: can_admin_list?.to_s,
time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s,
recent_boards_endpoint: recent_boards_path,
parent: current_board_parent.model_name.param_key,
@ -88,6 +89,14 @@ module BoardsHelper
@current_board_parent ||= @group || @project
end
def can_update?
can?(current_user, :admin_issue, board)
end
def can_admin_list?
can?(current_user, :admin_issue_board_list, current_board_parent)
end
def can_admin_issue?
can?(current_user, :admin_issue, current_board_parent)
end

View file

@ -3,7 +3,7 @@
= f.label :create_chat_team do
%span.gl-display-flex
= custom_icon('icon_mattermost')
%span.gl-ml-2 Mattermost
%span.gl-ml-2= _('Mattermost')
.col-sm-12
.form-check.js-toggle-container
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: false }, true, false)
@ -11,7 +11,7 @@
= _('Create a Mattermost team for this group')
%br
%small.light.js-toggle-content
Mattermost URL:
= _('Mattermost URL:')
= Settings.mattermost.host
%span> /
%span{ "data-bind-out" => "create_chat_team" }

View file

@ -1,15 +1,15 @@
.form-group.row
.col-sm-2.col-form-label.pt-0
= f.label :lfs_enabled, 'Large File Storage'
= f.label :lfs_enabled, _('Large File Storage')
.col-sm-10
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
= f.label :lfs_enabled, class: 'form-check-label' do
%strong
Allow projects within this group to use Git LFS
= _('Allow projects within this group to use Git LFS')
= link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index')
%br/
%span This setting can be overridden in each project.
%span= _('This setting can be overridden in each project.')
.form-group.row
.col-sm-2.col-form-label
= f.label s_('ProjectCreationLevel|Allowed to create projects')
@ -24,16 +24,16 @@
.form-group.row
.col-sm-2.col-form-label.pt-0
= f.label :require_two_factor_authentication, 'Two-factor authentication'
= f.label :require_two_factor_authentication, _('Two-factor authentication')
.col-sm-10
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.label :require_two_factor_authentication, class: 'form-check-label' do
%strong
Require all users in this group to set up Two-factor authentication
= _("Require all users in this group to setup Two-factor authentication")
= link_to sprite_icon('question-o'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
.form-group.row
.offset-sm-2.col-sm-10
.form-check
= f.text_field :two_factor_grace_period, class: 'form-control'
.form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
.form-text.text-muted= _("Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication")

View file

@ -1,8 +1,5 @@
- board = local_assigns.fetch(:board, nil)
- group = local_assigns.fetch(:group, false)
-# TODO: Move group_id and can_admin_list to the board store
See: https://gitlab.com/gitlab-org/gitlab/-/issues/213082
- can_admin_list = can?(current_user, :admin_issue_board_list, current_board_parent) == true
- @no_breadcrumb_container = true
- @no_container = true
- @content_class = "issue-boards-content js-focus-mode-board"
@ -15,11 +12,6 @@
= render 'shared/issuable/search_bar', type: :boards, board: board
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
%board-content{ "v-cloak" => "true",
"ref" => "board_content",
":lists" => "state.lists",
":can-admin-list" => can_admin_list,
":disabled" => "disabled",
data: { qa_selector: "boards_list" } }
%board-content{ ":lists" => "state.lists", ":disabled" => "disabled" }
= render "shared/boards/components/sidebar", group: group
%board-settings-sidebar{ ":can-admin-list" => can_admin_list }
%board-settings-sidebar

View file

@ -0,0 +1,5 @@
---
title: Externalise strings in groups/_group_admin_settings.html.haml
merge_request: 58331
author: nuwe1
type: other

View file

@ -0,0 +1,5 @@
---
title: Externalize strings in groups/_create_chat_team.html.haml
merge_request: 58328
author: nuwe1
type: other

View file

@ -0,0 +1,5 @@
---
title: Fix EmptyLineAfterFinalLetItBe Rubocop offenses for groups controller
merge_request: 58174
author: Huzaifa Iftikhar @huzaifaiftikhar
type: fixed

View file

@ -941,3 +941,226 @@ result as you did at the start. For example:
```
Note that `enforced="true"` means that authentication is being enforced.
## Pack-objects cache **(FREE SELF)**
> - [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/372) in GitLab 13.11.
> - It's enabled on GitLab.com.
> - It's recommended for production use.
[Gitaly](index.md), the service that provides storage for Git
repositories, can be configured to cache a short rolling window of Git
fetch responses. This can reduce server load when your server receives
lots of CI fetch traffic.
### Overview
The pack-objects cache wraps `git pack-objects`, an internal part of
Git that gets invoked indirectly via the PostUploadPack and
SSHUploadPack Gitaly RPCs. These are the RPCs that Gitaly runs when a
user does a Git fetch via HTTP or SSH, respectively. When the cache is
enabled, anything that uses PostUploadPack or SSHUploadPack can
benefit from it. It is orthogonal to:
- The transport (HTTP or SSH).
- Git protocol version (v0 or v2).
- The type of fetch (full clones, incremental fetches, shallow clones,
partial clones, and so on).
The strength of this cache is its ability to deduplicate concurrent
identical fetches. It:
- Can benefit GitLab instances where your users run CI/CD pipelines with many concurrent jobs.
There should be a noticeable reduction in server CPU utilization.
- Does not benefit unique fetches at all. For example, if you run a spot check by cloning a
repository to your local computer, you are unlikely to see a benefit from this cache because
your fetch is probably unique.
The pack-objects cache is a local cache. It:
- Stores its metadata in the memory of the Gitaly process it is enabled in.
- Stores the actual Git data it is caching in files on local storage.
Using local files has the benefit that the operating system may
automatically keep parts of the pack-objects cache files in RAM,
making it faster.
Because the pack-objects cache can lead to a significant increase in
disk write IO, it is off by default.
### Configure the cache
These are the configuration settings for the pack-objects cache. Each
setting is discussed in greater detail below.
|Setting|Default|Description|
|:---|:---|:---|
|`enabled`|`false`|Turns on the cache. When off, Gitaly runs a dedicated `git pack-objects` process for each request. |
|`dir`|`<PATH TO FIRST STORAGE>/+gitaly/PackObjectsCache`|Local directory where cache files get stored.|
|`max_age`|`5m` (5 minutes)|Cache entries older than this get evicted and removed from disk.|
In `/etc/gitlab/gitlab.rb`, set:
```ruby
gitaly['pack_objects_cache_enabled'] = true
## gitaly['pack_objects_cache_dir'] = '/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache'
## gitaly['pack_objects_cache_max_age'] = '5m'
```
#### `enabled` defaults to `false`
The cache is disabled by default. This is because in some cases, it
can create an [extreme
increase](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/4010#note_534564684)
in the number of bytes written to disk. On GitLab.com, we have verified
that our repository storage disks can handle this extra workload, but
we felt we cannot assume this is true everywhere.
#### Cache storage directory `dir`
The cache needs a directory to store its files in. This directory
should be:
- In a filesystem with enough space. If the cache filesystem runs out of space, all
fetches start failing.
- On a disk with enough IO bandwidth. If the cache disk runs out of IO bandwidth, all
fetches, and probably the entire server, slows down.
By default, the cache storage directory is set to a subdirectory of the first Gitaly storage
defined in the configuration file.
Multiple Gitaly processes can use the same directory for cache storage. Each Gitaly process
uses a unique random string as part of the cache filenames it creates. This means:
- They do not collide.
- They do not reuse another process's files.
While the default directory puts the cache files in the same
filesystem as your repository data, this is not requirement. You can
put the cache files on a different filesystem if that works better for
your infrastructure.
The amount of IO bandwidth required from the disk depends on:
- The size and shape of the repositories on your Gitaly server.
- The kind of traffic your users generate.
You can use the `gitaly_pack_objects_generated_bytes_total` metric as a pessimistic estimate,
pretending your cache hit ratio is 0%.
The amount of space required depends on:
- The bytes per second that your users pull from the cache.
- The size of the `max_age` cache eviction window.
If your users pull 100 MB/s and you use a 5 minute window, then on average you have
`5*60*100MB = 30GB` of data in your cache directory. This is an expected average, not
a guarantee. Peak size may exceed this average.
#### Cache eviction window `max_age`
The `max_age` configuration setting lets you control the chance of a
cache hit and the average amount of storage used by cache files.
Entries older than `max_age` get evicted from the in-memory metadata
store, and deleted from disk.
Note that eviction does not interfere with ongoing requests, so it is OK
for `max_age` to be less than the time it takes to do a fetch over a
slow connection. This is because Unix filesystems do not truly delete
a file until all processes that are reading the deleted file have
closed it.
### Observe the cache
The cache can be observed in logs and using metrics.
#### Logs
|Message|Fields|Description|
|:---|:---|:---|
|`generated bytes`|`bytes`, `cache_key`|Logged when an entry was added to the cache|
|`served bytes`|`bytes`, `cache_key`|Logged when an entry was read from the cache|
In the case of a:
- Cache miss, Gitaly logs both a `generated bytes` and a `served bytes` message.
- Cache hit, Gitaly logs only a `served bytes` message.
Example:
```json
{
"bytes":26186490,
"cache_key":"1b586a2698ca93c2529962e85cda5eea8f0f2b0036592615718898368b462e19",
"correlation_id":"01F1MY8JXC3FZN14JBG1H42G9F",
"grpc.meta.deadline_type":"none",
"grpc.method":"PackObjectsHook",
"grpc.request.fullMethod":"/gitaly.HookService/PackObjectsHook",
"grpc.request.glProjectPath":"root/gitlab-workhorse",
"grpc.request.glRepository":"project-2",
"grpc.request.repoPath":"@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git",
"grpc.request.repoStorage":"default",
"grpc.request.topLevelGroup":"@hashed",
"grpc.service":"gitaly.HookService",
"grpc.start_time":"2021-03-25T14:57:52.747Z",
"level":"info",
"msg":"generated bytes",
"peer.address":"@",
"pid":20961,
"span.kind":"server",
"system":"grpc",
"time":"2021-03-25T14:57:53.543Z"
}
{
"bytes":26186490,
"cache_key":"1b586a2698ca93c2529962e85cda5eea8f0f2b0036592615718898368b462e19",
"correlation_id":"01F1MY8JXC3FZN14JBG1H42G9F",
"grpc.meta.deadline_type":"none",
"grpc.method":"PackObjectsHook",
"grpc.request.fullMethod":"/gitaly.HookService/PackObjectsHook",
"grpc.request.glProjectPath":"root/gitlab-workhorse",
"grpc.request.glRepository":"project-2",
"grpc.request.repoPath":"@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git",
"grpc.request.repoStorage":"default",
"grpc.request.topLevelGroup":"@hashed",
"grpc.service":"gitaly.HookService",
"grpc.start_time":"2021-03-25T14:57:52.747Z",
"level":"info",
"msg":"served bytes",
"peer.address":"@",
"pid":20961,
"span.kind":"server",
"system":"grpc",
"time":"2021-03-25T14:57:53.543Z"
}
```
#### Metrics
The following cache metrics are available.
|Metric|Type|Labels|Description|
|:---|:---|:---|:---|
|`gitaly_pack_objects_cache_enabled`|gauge|`dir`,`max_age`|Set to `1` when the cache is enabled via the Gitaly config file|
|`gitaly_pack_objects_cache_lookups_total`|counter|`result`|Hit/miss counter for cache lookups|
|`gitaly_pack_objects_generated_bytes_total`|counter||Number of bytes written into the cache|
|`gitaly_pack_objects_served_bytes_total`|counter||Number of bytes read from the cache|
|`gitaly_streamcache_filestore_disk_usage_bytes`|gauge|`dir`|Total size of cache files|
|`gitaly_streamcache_index_entries`|gauge|`dir`|Number of entries in the cache|
Some of these metrics start with `gitaly_streamcache`
because they are generated by the "streamcache" internal library
package in Gitaly.
Example:
```plaintext
gitaly_pack_objects_cache_enabled{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache",max_age="300"} 1
gitaly_pack_objects_cache_lookups_total{result="hit"} 2
gitaly_pack_objects_cache_lookups_total{result="miss"} 1
gitaly_pack_objects_generated_bytes_total 2.618649e+07
gitaly_pack_objects_served_bytes_total 7.855947e+07
gitaly_streamcache_filestore_disk_usage_bytes{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 2.6200152e+07
gitaly_streamcache_filestore_removed_total{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1
gitaly_streamcache_index_entries{dir="/var/opt/gitlab/git-data/repositories/+gitaly/PackObjectsCache"} 1
```

View file

@ -91,6 +91,9 @@ The following table describes details of your subscription:
> - [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/292086) in GitLab 13.8 to include public
email address.
To view a list of seats being used, go to **Settings > Billing**.
Under **Seats currently in use**, select **See usage**.
The **Seat usage** page lists all users occupying seats. Details for each user include:
- Full name
@ -117,6 +120,14 @@ For example:
| Amir | `ami` | Yes |
| Amir | `amr` | No |
## Subscription expiry
When your subscription expires, you can continue to use paid features of GitLab for 14 days.
On the 15th day, paid features are no longer available. You can
continue to use free features.
To resume paid feature functionality, purchase a new subscription.
## Renew your GitLab SaaS subscription
To renew your subscription:
@ -145,23 +156,42 @@ It's important to regularly review your user accounts, because:
attempt to renew a subscription for too few users.
- Stale user accounts can be a security risk. A regular review helps reduce this risk.
#### Users over License
#### Seats owed
A GitLab subscription is valid for a specific number of users. For details, see
[Choose the number of users](#choose-the-number-of-users).
If the number of [billable users](#view-your-gitlab-saas-subscription) exceeds the number included in the subscription, known
as the number of _users over license_, you must pay for the excess number of
users either before renewal, or at the time of renewal. This is also known the
_true up_ process.
as the number of _seats owed_, you must pay for the excess number of users before renewal.
##### Seats owed example
You purchase a subscription for 10 users.
| Event | Billable members | Maximum users |
|:---------------------------------------------------|:-----------------|:--------------|
| Ten users occupy all 10 seats. | 10 | 10 |
| Two new users join. | 12 | 12 |
| Three users leave and their accounts are removed. | 9 | 12 |
Seats owed = 12 - 10 (Maximum users - users in subscription)
### Renew or change a GitLab SaaS subscription
You can adjust the number of users before renewing your GitLab SaaS subscription.
Starting 30 days before a subscription expires, GitLab notifies group owners
of the date of expiry with a banner in the GitLab user interface.
- To renew for more users than are currently included in your GitLab SaaS plan, [add users to your subscription](#add-users-to-your-subscription).
- To renew for fewer users than are currently included in your GitLab SaaS plan,
[remove](#remove-billable-user) the user accounts you no longer need.
We recommend following these steps during renewal:
1. Prune any [inactive or unwanted users](#remove-billable-user).
1. Determine if you have a need for user growth in the upcoming subscription.
1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and beneath your existing subscription, select the **Renew** button.
1. Review your renewal details and complete the payment process.
1. Select **Confirm purchase**.
Your updated subscription is applied to your namespace on the renewal period start date.
An invoice is generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance.
For details on upgrading your subscription tier, see
[Upgrade your GitLab SaaS subscription tier](#upgrade-your-gitlab-saas-subscription-tier).
@ -235,14 +265,6 @@ To remove a billable user:
1. In the row for the user you want to remove, on the right side, select the ellipsis and **Remove user**.
1. Re-type the username and select **Remove user**.
## Subscription expiry
When your subscription expires, you can continue to use GitLab for 14 days.
On the 15th day, paid features are no longer available. You can
continue to use free features.
To resume paid feature functionality, purchase a new subscription.
## CI pipeline minutes
CI pipeline minutes are the execution time for your

View file

@ -150,9 +150,9 @@ You purchase a license for 10 users.
| Event | Billable members | Maximum users |
|:---------------------------------------------------|:-----------------|:--------------|
| Ten people (users) occupy all 10 seats. | 10 | 10 |
| Two new people join. | 12 | 12 |
| Three people leave and their accounts are removed. | 9 | 12 |
| Ten users occupy all 10 seats. | 10 | 10 |
| Two new users join. | 12 | 12 |
| Three users leave and their accounts are removed. | 9 | 12 |
Users over license = 12 - 10 (Maximum users - users in license)

View file

@ -109,7 +109,7 @@ There are two types of jobs:
Here's our current suggestion for configuring your fuzz target's timeout:
- Set `COVFUZZ_BRANCH` to the branch where you want to run long-running (asynchronous) fuzzing
jobs. This is `master` by default.
jobs. This is normally the default branch.
- Use regression or short-running fuzzing jobs for other branches or merge requests.
This suggestion helps find new bugs on the development branch and catch old bugs in merge requests
@ -122,8 +122,8 @@ any option available in the underlying fuzzing engine.
### Available CI/CD variables
| CI/CD variable | Description |
|-----------------------|--------------------------------------------------------------------|
| `COVFUZZ_BRANCH` | The branch for long-running fuzzing jobs. The default is `master`. |
|-----------------------|--------------------------------------------------------------------------------|
| `COVFUZZ_BRANCH` | The branch for long-running fuzzing jobs. This is normally the default branch. |
| `COVFUZZ_SEED_CORPUS` | Path to a seed corpus directory. The default is empty. |
| `COVFUZZ_URL_PREFIX` | Path to the `gitlab-cov-fuzz` repository cloned for use with an offline environment. You should only change this when using an offline environment. The default value is `https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw`. |

View file

@ -18969,6 +18969,12 @@ msgstr ""
msgid "Match not found; try refining your search query."
msgstr ""
msgid "Mattermost"
msgstr ""
msgid "Mattermost URL:"
msgstr ""
msgid "MattermostService|Add to Mattermost"
msgstr ""
@ -26246,6 +26252,9 @@ msgstr ""
msgid "Require admin approval for new sign-ups"
msgstr ""
msgid "Require all users in this group to setup Two-factor authentication"
msgstr ""
msgid "Require all users in this group to setup two-factor authentication"
msgstr ""

View file

@ -32,7 +32,7 @@ module QA
element :labels_edit_button
end
view 'app/views/shared/boards/_show.html.haml' do
view 'app/assets/javascripts/boards/components/board_content.vue' do
element :boards_list
end

View file

@ -31,6 +31,7 @@ RSpec.describe Groups::DependencyProxyAuthController do
context 'with valid JWT' do
let_it_be(:user) { create(:user) }
let(:jwt) { build_jwt(user) }
let(:token_header) { "Bearer #{jwt.encoded}" }
@ -65,6 +66,7 @@ RSpec.describe Groups::DependencyProxyAuthController do
context 'expired token' do
let_it_be(:user) { create(:user) }
let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
let(:token_header) { "Bearer #{jwt.encoded}" }

View file

@ -7,6 +7,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
include DependencyProxyHelpers
let_it_be(:user) { create(:user) }
let(:group) { create(:group) }
let(:token_response) { { status: :success, token: 'abcd1234' } }
let(:jwt) { build_jwt(user) }
@ -102,6 +103,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
describe 'GET #manifest' do
let_it_be(:manifest) { create(:dependency_proxy_manifest) }
let(:pull_response) { { status: :success, manifest: manifest } }
before do
@ -182,6 +184,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
describe 'GET #blob' do
let_it_be(:blob) { create(:dependency_proxy_blob) }
let(:blob_sha) { blob.file_name.sub('.gz', '') }
let(:blob_response) { { status: :success, blob: blob } }

View file

@ -6,6 +6,7 @@ RSpec.describe Groups::Registry::RepositoriesController do
let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:group, reload: true) { create(:group) }
let(:additional_parameters) { {} }
subject do

View file

@ -8,6 +8,7 @@ RSpec.describe Groups::VariablesController do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:variable) { create(:ci_group_variable, group: group) }
let(:access_level) { :owner }
before do

View file

@ -33,7 +33,12 @@ describe('BoardContent', () => {
});
};
const createComponent = ({ state, props = {}, graphqlBoardListsEnabled = false } = {}) => {
const createComponent = ({
state,
props = {},
graphqlBoardListsEnabled = false,
canAdminList = true,
} = {}) => {
const store = createStore({
...defaultState,
...state,
@ -42,11 +47,11 @@ describe('BoardContent', () => {
localVue,
propsData: {
lists: mockListsWithModel,
canAdminList: true,
disabled: false,
...props,
},
provide: {
canAdminList,
glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled },
},
store,
@ -82,7 +87,7 @@ describe('BoardContent', () => {
describe('can admin list', () => {
beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: true } });
createComponent({ graphqlBoardListsEnabled: true, canAdminList: true });
});
it('renders draggable component', () => {
@ -92,7 +97,7 @@ describe('BoardContent', () => {
describe('can not admin list', () => {
beforeEach(() => {
createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: false } });
createComponent({ graphqlBoardListsEnabled: true, canAdminList: false });
});
it('does not render draggable component', () => {

View file

@ -4,6 +4,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import { inactiveId, LIST } from '~/boards/constants';
import { createStore } from '~/boards/stores';
@ -22,11 +23,18 @@ describe('BoardSettingsSidebar', () => {
const labelColor = '#FFFF';
const listId = 1;
const createComponent = () => {
wrapper = shallowMount(BoardSettingsSidebar, {
const findRemoveButton = () => wrapper.findByTestId('remove-list');
const createComponent = ({ canAdminList = false } = {}) => {
wrapper = extendedWrapper(
shallowMount(BoardSettingsSidebar, {
store,
localVue,
});
provide: {
canAdminList,
},
}),
);
};
const findLabel = () => wrapper.find(GlLabel);
const findDrawer = () => wrapper.find(GlDrawer);
@ -164,4 +172,29 @@ describe('BoardSettingsSidebar', () => {
expect(findDrawer().exists()).toBe(false);
});
});
it('does not render "Remove list" when user cannot admin the boards list', () => {
createComponent();
expect(findRemoveButton().exists()).toBe(false);
});
describe('when user can admin the boards list', () => {
beforeEach(() => {
store.state.activeId = listId;
store.state.sidebarType = LIST;
boardsStore.addList({
id: listId,
label: { title: labelTitle, color: labelColor },
list_type: 'label',
});
createComponent({ canAdminList: true });
});
it('renders "Remove list" button', () => {
expect(findRemoveButton().exists()).toBe(true);
});
});
});

View file

@ -64,6 +64,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
end
it 'returns a board_lists_path as lists_endpoint' do
@ -86,6 +87,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id of a project' do
expect(helper.board_data[:group_id]).to eq(project.group.id)
end
context 'can_admin_list' do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')
end
end
end
context 'group board' do
@ -96,6 +108,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(false)
end
it 'returns correct path for base group' do
@ -110,6 +123,17 @@ RSpec.describe BoardsHelper do
it 'returns the group id' do
expect(helper.board_data[:group_id]).to eq(base_group.id)
end
context 'can_admin_list' do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')
end
end
end
end