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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-11 09:10:03 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-11 09:10:03 +0300
commit281f1b6ba36531122463afd6869ca5dcdd0b0e35 (patch)
tree1f4c771d0b58b7fdd88643b4690b847e1e968c08
parentbdca0979165cb662173d959c358fce0c539c7544 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/stylesheets/pages/users.scss6
-rw-r--r--app/views/groups/group_members/index.html.haml115
-rw-r--r--app/views/groups/group_members/tab_pane/_form_item.html.haml2
-rw-r--r--app/views/groups/group_members/tab_pane/_header.html.haml2
-rw-r--r--app/views/groups/group_members/tab_pane/_title.html.haml2
-rw-r--r--app/views/projects/project_members/_team.html.haml1
-rw-r--r--app/views/shared/members/_filter_2fa_dropdown.html.haml2
-rw-r--r--app/views/shared/members/_search_field.html.haml6
-rw-r--r--app/views/shared/members/_sort_dropdown.html.haml1
-rw-r--r--changelogs/unreleased/229830-move-groups-members-pending-in-to-tabs.yml5
-rw-r--r--changelogs/unreleased/update-auto-build-image-to-v0-4-0.yml6
-rw-r--r--doc/ci/variables/predefined_variables.md4
-rw-r--r--doc/ci/yaml/README.md2
-rw-r--r--doc/install/azure/index.md3
-rw-r--r--doc/topics/git/lfs/migrate_to_git_lfs.md18
-rw-r--r--doc/user/group/img/add_new_members.pngbin66513 -> 79358 bytes
-rw-r--r--lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml2
-rw-r--r--lib/gitlab/danger/helper.rb6
-rw-r--r--locale/gitlab.pot18
-rw-r--r--qa/qa/page/group/members.rb3
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb16
-rw-r--r--spec/features/groups/members/manage_members_spec.rb2
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb1
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/groups/members/tabs_spec.rb112
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb1
-rw-r--r--spec/frontend/registry/explorer/pages/details_spec.js2
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/identicon_spec.js2
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb9
-rw-r--r--spec/requests/api/composer_packages_spec.rb145
-rw-r--r--spec/support/csv_response.rb5
-rw-r--r--spec/support/shared_contexts/csv_response_shared_context.rb5
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb21
-rw-r--r--spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb6
35 files changed, 393 insertions, 142 deletions
diff --git a/app/assets/stylesheets/pages/users.scss b/app/assets/stylesheets/pages/users.scss
index 3b018c1e087..0863b573f75 100644
--- a/app/assets/stylesheets/pages/users.scss
+++ b/app/assets/stylesheets/pages/users.scss
@@ -25,8 +25,12 @@
}
.form-control {
- width: 100%;
padding-right: 35px;
+ }
+
+ .search-control-wrap,
+ .form-control {
+ width: 100%;
@include media-breakpoint-up(sm) {
width: 250px;
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index b9ea8316bbc..c8e58a50b18 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,84 +1,99 @@
-- page_title _("Group members")
+- page_title _('Group members')
- can_manage_members = can?(current_user, :admin_group_member, @group)
- show_invited_members = can_manage_members && @invited_members.exists?
-- pending_active = params[:search_invited].present?
-- total_count = @members.count + @group.shared_with_group_links.count
+- show_access_requests = can_manage_members && @requesters.exists?
+- invited_active = params[:search_invited].present? || params[:invited_members_page].present?
+
+- form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center'
.js-remove-member-modal
.project-members-page.gl-mt-3
%h4
- = _("Group members")
+ = _('Group members')
%hr
- if can_manage_members
%ul.nav-links.nav.nav-tabs.gitlab-tabs{ role: 'tablist' }
%li.nav-tab{ role: 'presentation' }
- %a.nav-link.active{ href: '#invite-member-pane', id: 'invite-member-tab', data: { toggle: 'tab' }, role: 'tab' }= _("Invite member")
+ %a.nav-link.active{ href: '#invite-member-pane', id: 'invite-member-tab', data: { toggle: 'tab' }, role: 'tab' }= _('Invite member')
%li.nav-tab{ role: 'presentation' }
- %a.nav-link{ href: '#invite-group-pane', id: 'invite-group-tab', data: { toggle: 'tab', qa_selector: 'invite_group_tab' }, role: 'tab' }= _("Invite group")
+ %a.nav-link{ href: '#invite-group-pane', id: 'invite-group-tab', data: { toggle: 'tab', qa_selector: 'invite_group_tab' }, role: 'tab' }= _('Invite group')
.tab-content.gitlab-tab-content
.tab-pane.active{ id: 'invite-member-pane', role: 'tabpanel' }
= render_invite_member_for_group(@group, @group_member.access_level)
.tab-pane{ id: 'invite-group-pane', role: 'tabpanel' }
= render 'shared/members/invite_group', submit_url: group_group_links_path(@group), access_levels: GroupMember.access_level_roles, default_access_level: @group_member.access_level, group_link_field: 'shared_with_group_id', group_access_field: 'shared_group_access'
- = render 'shared/members/requests', membership_source: @group, requesters: @requesters
-
= render_if_exists 'groups/group_members/ldap_sync'
- %ul.nav-links.mobile-separator.nav.nav-tabs.clearfix
+ %ul.nav-links.mobile-separator.nav.nav-tabs
%li.nav-item
- = link_to "#existing_shares", class: ["nav-link", ("active" unless pending_active)] , 'data-toggle' => 'tab' do
+ = link_to '#tab-members', class: ['nav-link', ('active' unless invited_active)], data: { toggle: 'tab' } do
%span
- = _("Existing shares")
- %span.badge.badge-pill= total_count
+ = _('Members')
+ %span.badge.badge-pill= @members.total_count
+ - if @group.shared_with_group_links.any?
+ %li.nav-item
+ = link_to '#tab-groups', class: ['nav-link'] , data: { toggle: 'tab', qa_selector: 'groups_list_tab' } do
+ %span
+ = _('Groups')
+ %span.badge.badge-pill= @group.shared_with_group_links.count
- if show_invited_members
%li.nav-item
- = link_to "#invited_members", class: ["nav-link", ("active" if pending_active)], 'data-toggle' => 'tab' do
+ = link_to '#tab-invited-members', class: ['nav-link', ('active' if invited_active)], data: { toggle: 'tab' } do
%span
- = _("Pending")
+ = _('Invited')
%span.badge.badge-pill= @invited_members.total_count
-
+ - if show_access_requests
+ %li.nav-item
+ = link_to '#tab-access-requests', class: 'nav-link', data: { toggle: 'tab' } do
+ %span
+ = _('Access requests')
+ %span.badge.badge-pill= @requesters.count
.tab-content
- #existing_shares.tab-pane{ :class => ("active" unless pending_active) }
- - if @group.shared_with_group_links.any?
- .card.card-without-border
- .d-flex.flex-column.flex-md-row.row-content-block.second-block
- %span.flex-grow-1.align-self-md-center.col-form-label
- = _("Groups with access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- %ul.content-list.members-list{ data: { qa_selector: "groups_list" } }
- - can_admin_member = can?(current_user, :admin_group_member, @group)
- - @group.shared_with_group_links.each do |group_link|
- = render 'shared/members/group', group_link: group_link, can_admin_member: can_admin_member, group_link_path: group_group_link_path(@group, group_link)
+ #tab-members.tab-pane{ class: ('active' unless invited_active) }
.card.card-without-border
- .d-flex.flex-column.flex-md-row.row-content-block.second-block
- %span.flex-grow-1.align-self-md-center.col-form-label
- = _("Members with access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
- .form-group.flex-grow
- .position-relative.mr-md-2
- = search_field_tag :search, params[:search], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
- %button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
- = icon("search")
- - if can_manage_members
- = label_tag '2fa', '2FA', class: 'col-form-label label-bold pr-md-2'
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do
+ .gl-px-3.gl-py-2
+ .search-control-wrap.gl-relative
+ = render 'shared/members/search_field'
+ - if can_manage_members
+ = render 'groups/group_members/tab_pane/form_item' do
+ = label_tag '2fa', _('2FA'), class: form_item_label_css_class
= render 'shared/members/filter_2fa_dropdown'
+ = render 'groups/group_members/tab_pane/form_item' do
+ = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
= render 'shared/members/sort_dropdown'
- %ul.content-list.members-list{ data: { qa_selector: "members_list" } }
+ %ul.content-list.members-list{ data: { qa_selector: 'members_list' } }
= render partial: 'shared/members/member', collection: @members, as: :member
- = paginate @members, theme: 'gitlab'
-
+ = paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil }
+ - if @group.shared_with_group_links.any?
+ #tab-groups.tab-pane
+ .card.card-without-border
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ %ul.content-list.members-list{ data: { qa_selector: 'groups_list' } }
+ - @group.shared_with_group_links.each do |group_link|
+ = render 'shared/members/group', group_link: group_link, can_admin_member: can_manage_members, group_link_path: group_group_link_path(@group, group_link)
- if show_invited_members
- #invited_members.tab-pane{ :class => ("active" if pending_active) }
+ #tab-invited-members.tab-pane{ class: ('active' if invited_active) }
.card.card-without-border
- .d-flex.flex-column.flex-md-row.row-content-block.second-block
- %span.flex-grow-1
- = _("Members with pending access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
- .form-group
- .position-relative.mr-md-2
- = search_field_tag :search_invited, params[:search_invited], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
- %button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
- = icon("search")
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
+ = render 'shared/members/search_field', name: 'search_invited'
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @invited_members, as: :member
- = paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab'
+ = paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil }
+ - if show_access_requests
+ #tab-access-requests.tab-pane
+ .card.card-without-border
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ %ul.content-list.members-list
+ = render partial: 'shared/members/member', collection: @requesters, as: :member
diff --git a/app/views/groups/group_members/tab_pane/_form_item.html.haml b/app/views/groups/group_members/tab_pane/_form_item.html.haml
new file mode 100644
index 00000000000..9e57d3329d7
--- /dev/null
+++ b/app/views/groups/group_members/tab_pane/_form_item.html.haml
@@ -0,0 +1,2 @@
+.gl-px-3.gl-py-3.gl-display-flex.gl-flex-direction-column.gl-md-flex-direction-row
+ = yield
diff --git a/app/views/groups/group_members/tab_pane/_header.html.haml b/app/views/groups/group_members/tab_pane/_header.html.haml
new file mode 100644
index 00000000000..a02bf90eddf
--- /dev/null
+++ b/app/views/groups/group_members/tab_pane/_header.html.haml
@@ -0,0 +1,2 @@
+.gl-display-flex.gl-md-align-items-center.gl-flex-direction-column.gl-md-flex-direction-row.row-content-block.second-block
+ = yield
diff --git a/app/views/groups/group_members/tab_pane/_title.html.haml b/app/views/groups/group_members/tab_pane/_title.html.haml
new file mode 100644
index 00000000000..c1418a5f7c8
--- /dev/null
+++ b/app/views/groups/group_members/tab_pane/_title.html.haml
@@ -0,0 +1,2 @@
+%span.gl-flex-grow-1.gl-py-3.gl-pr-3
+ = yield
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 655984397e3..4b3fdf8d0b1 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -12,6 +12,7 @@
= search_field_tag :search, params[:search], { placeholder: _('Find existing members by name'), class: 'form-control', spellcheck: false }
%button.user-search-btn{ type: "submit", "aria-label" => _("Submit search") }
= icon("search")
+ = label_tag :sort_by, _('Sort by'), class: 'col-form-label label-bold px-2'
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list{ data: { qa_selector: 'members_list' } }
= render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/shared/members/_filter_2fa_dropdown.html.haml b/app/views/shared/members/_filter_2fa_dropdown.html.haml
index 44ea844028e..a2bc5e9ecdf 100644
--- a/app/views/shared/members/_filter_2fa_dropdown.html.haml
+++ b/app/views/shared/members/_filter_2fa_dropdown.html.haml
@@ -1,6 +1,6 @@
- filter = params[:two_factor] || 'everyone'
- filter_options = { 'everyone' => _('Everyone'), 'enabled' => _('Enabled'), 'disabled' => _('Disabled') }
-.dropdown.inline.member-filter-2fa-dropdown.pr-md-2
+.dropdown.inline.member-filter-2fa-dropdown
= dropdown_toggle(filter_options[filter], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
diff --git a/app/views/shared/members/_search_field.html.haml b/app/views/shared/members/_search_field.html.haml
new file mode 100644
index 00000000000..e70cb063324
--- /dev/null
+++ b/app/views/shared/members/_search_field.html.haml
@@ -0,0 +1,6 @@
+- name = local_assigns.fetch(:name, :search)
+
+.search-control-wrap.gl-relative
+ = search_field_tag name, params[name], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
+ %button.user-search-btn.border-left.gl-display-flex.gl-align-items-center.gl-justify-content-center{ type: 'submit', 'aria': { label: _('Submit search') } }
+ = sprite_icon('search')
diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml
index 50a55565c3c..606d3bcdfa8 100644
--- a/app/views/shared/members/_sort_dropdown.html.haml
+++ b/app/views/shared/members/_sort_dropdown.html.haml
@@ -1,4 +1,3 @@
-= label_tag :sort_by, 'Sort by', class: 'col-form-label label-bold px-2'
.dropdown.inline.qa-user-sort-dropdown
= dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
diff --git a/changelogs/unreleased/229830-move-groups-members-pending-in-to-tabs.yml b/changelogs/unreleased/229830-move-groups-members-pending-in-to-tabs.yml
new file mode 100644
index 00000000000..cb1101b0e6a
--- /dev/null
+++ b/changelogs/unreleased/229830-move-groups-members-pending-in-to-tabs.yml
@@ -0,0 +1,5 @@
+---
+title: Reorganize group member management into tabs
+merge_request: 38344
+author:
+type: changed
diff --git a/changelogs/unreleased/update-auto-build-image-to-v0-4-0.yml b/changelogs/unreleased/update-auto-build-image-to-v0-4-0.yml
new file mode 100644
index 00000000000..e5d4453622c
--- /dev/null
+++ b/changelogs/unreleased/update-auto-build-image-to-v0-4-0.yml
@@ -0,0 +1,6 @@
+---
+title: Update auto-build-image to v0.4.0 for an updated version of the pack CLI (v0.12.0) for
+ Cloud Native Buildpack builds
+merge_request: 39159
+author:
+type: changed
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 88cb560dd8f..f13f1f1f36e 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -72,8 +72,8 @@ Kubernetes-specific environment variables are detailed in the
| `CI_JOB_URL` | 11.1 | 0.5 | Job details URL |
| `CI_KUBERNETES_ACTIVE` | 13.0 | all | Included with the value `true` only if the pipeline has a Kubernetes cluster available for deployments. Not included if no cluster is available. Can be used as an alternative to [`only:kubernetes`/`except:kubernetes`](../yaml/README.md#onlykubernetesexceptkubernetes) with [`rules:if`](../yaml/README.md#rulesif) |
| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of username(s) of assignee(s) for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
-| `CI_MERGE_REQUEST_ID` | 11.6 | all | The project-level ID of the merge request. Only available if [the pipelines are for merge requests](../merge_request_pipelines/index.md) and the merge request is created. |
-| `CI_MERGE_REQUEST_IID` | 11.6 | all | The instance-level IID of the merge request. Only available If [the pipelines are for merge requests](../merge_request_pipelines/index.md) and the merge request is created. |
+| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. Only available if [the pipelines are for merge requests](../merge_request_pipelines/index.md) and the merge request is created. |
+| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID of the merge request. Only available If [the pipelines are for merge requests](../merge_request_pipelines/index.md) and the merge request is created. |
| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 5f0909ca749..790052128cc 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -3184,7 +3184,7 @@ stored on GitLab. If the expiry time is not defined, it defaults to the
[instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration-core-only)
(30 days by default).
-To override the expiration time and keep artifacts forever:
+To override the expiration date and protect artifacts from being automatically deleted:
- Use the **Keep** button on the job page.
- Set the value of `expire_in` to `never`. [Available](https://gitlab.com/gitlab-org/gitlab/-/issues/22761)
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 9ab37abc61f..548c7d8c92e 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -41,8 +41,7 @@ create SQL Databases, author websites, and perform lots of other cloud tasks.
The [Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/) is an online store for pre-configured applications and
services which have been optimized for the cloud by software vendors like GitLab,
available on the Azure Marketplace as pre-configured solutions. In this tutorial
-we will install GitLab Community Edition, but for GitLab Enterprise Edition you
-can follow the same process.
+we will install GitLab Community Edition.
To begin creating a new GitLab VM, click on the **+ New** icon, type "GitLab" into the search
box, and then click the **"GitLab Community Edition"** search result:
diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md
index 3e287c0816d..944f4d8f78d 100644
--- a/doc/topics/git/lfs/migrate_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_to_git_lfs.md
@@ -96,9 +96,10 @@ Consider an example upstream project, `git@gitlab.com:gitlab-tests/test-git-lfs-
1. Clean up the repository:
```shell
- # cd path/to/mirror/repo:
+ # Change into the mirror repo directory:
cd test-git-lfs-repo-migration.git
- # clean up the repo:
+
+ # Clean up the repo:
git reflog expire --expire=now --all && git gc --prune=now --aggressive
```
@@ -128,12 +129,23 @@ Consider an example upstream project, `git@gitlab.com:gitlab-tests/test-git-lfs-
1. Track the files you want with LFS:
```shell
- # cd path/to/upstream/repo:
+ # Change into the /tmp directory
+ cd /tmp
+
+ # Clone the repo
+ git clone git@gitlab.com:gitlab-tests/test-git-lfs-repo-migration.git
+
+ # Change into the upstream repo directory:
cd test-git-lfs-repo-migration
+
# You may need to reset your local copy with upstream's `master` after force-pushing from the mirror:
git reset --hard origin/master
+
# Track the files with LFS:
git lfs track "*.gif" "*.png" "*.jpg" "*.psd" "*.mp4" "img/"
+
+ # Push up changes to .gitattributes
+ git add .gitattributes && git commit -m 'Track .gif,.png,.jpg,.psd,.mp4 and img/' && git push
```
Now all existing the files you converted, as well as the new
diff --git a/doc/user/group/img/add_new_members.png b/doc/user/group/img/add_new_members.png
index 6d43e309e84..8bd9e2374bc 100644
--- a/doc/user/group/img/add_new_members.png
+++ b/doc/user/group/img/add_new_members.png
Binary files differ
diff --git a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
index dbe870953ae..0c3598a61a7 100644
--- a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
@@ -1,6 +1,6 @@
build:
stage: build
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.3.1"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.4.0"
variables:
DOCKER_TLS_CERTDIR: ""
services:
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index de6bcde213d..077c71f1233 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -172,9 +172,13 @@ module Gitlab
%r{\Atooling/} => :engineering_productivity,
%r{(CODEOWNERS)} => :engineering_productivity,
+ %r{\A(ee/)?spec/features/} => :test,
+ %r{\A(ee/)?spec/support/shared_examples/features/} => :test,
+ %r{\A(ee/)?spec/support/shared_contexts/features/} => :test,
+ %r{\A(ee/)?spec/support/helpers/features/} => :test,
+
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
- %r{\A(ee/)?spec/features/} => :test,
%r{\A(ee/)?spec/} => :backend,
%r{\A(ee/)?vendor/} => :backend,
%r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d4c32f65007..73c4ba5002d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1237,6 +1237,9 @@ msgstr ""
msgid "Access forbidden. Check your access level."
msgstr ""
+msgid "Access requests"
+msgstr ""
+
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
@@ -9926,9 +9929,6 @@ msgstr ""
msgid "Existing projects will be able to use expiration policies. Avoid enabling this if an external Container Registry is being used, as there is a performance risk if many images exist on one project."
msgstr ""
-msgid "Existing shares"
-msgstr ""
-
msgid "Existing sign in methods may be removed"
msgstr ""
@@ -13264,6 +13264,9 @@ msgstr ""
msgid "Invite teammates (optional)"
msgstr ""
+msgid "Invited"
+msgstr ""
+
msgid "Invited users will be added with developer level permissions. You can always change this later."
msgstr ""
@@ -14864,6 +14867,9 @@ msgstr ""
msgid "Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}"
msgstr ""
+msgid "Members invited to %{strong_start}%{group_name}%{strong_end}"
+msgstr ""
+
msgid "Members of %{strong_open}%{project_name}%{strong_close}"
msgstr ""
@@ -14873,9 +14879,6 @@ msgstr ""
msgid "Members with access to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
-msgid "Members with pending access to %{strong_start}%{group_name}%{strong_end}"
-msgstr ""
-
msgid "Memory Usage"
msgstr ""
@@ -26700,6 +26703,9 @@ msgstr ""
msgid "Users requesting access to"
msgstr ""
+msgid "Users requesting access to %{strong_start}%{group_name}%{strong_end}"
+msgstr ""
+
msgid "Users were successfully added."
msgstr ""
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index 20caa7e839f..dce18ee5c55 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -25,6 +25,7 @@ module QA
view 'app/views/groups/group_members/index.html.haml' do
element :invite_group_tab
+ element :groups_list_tab
element :groups_list
end
@@ -71,6 +72,8 @@ module QA
end
def has_existing_group_share?(group_name)
+ click_element :groups_list_tab
+
within_element(:groups_list) do
has_element?(:group_row, text: group_name)
end
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index f1cf04417c0..faf455e4ed9 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -20,26 +20,28 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
add_group(shared_with_group.id, 'Reporter')
+ click_groups_tab
+
page.within(first_row) do
expect(page).to have_content(shared_with_group.name)
expect(page).to have_content('Reporter')
end
end
- it 'remove user from group' do
+ it 'remove group from group' do
create(:group_group_link, shared_group: shared_group,
shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER)
visit group_group_members_path(shared_group)
+ click_groups_tab
+
expect(page).to have_content(shared_with_group.name)
accept_confirm do
- find(:css, '#existing_shares li', text: shared_with_group.name).find(:css, 'a.btn-remove').click
+ find(:css, '#tab-groups li', text: shared_with_group.name).find(:css, 'a.btn-remove').click
end
- wait_for_requests
-
expect(page).not_to have_content(shared_with_group.name)
end
@@ -49,6 +51,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
visit group_group_members_path(shared_group)
+ click_groups_tab
+
page.within(first_row) do
click_button('Developer')
click_link('Maintainer')
@@ -67,4 +71,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
click_button "Invite"
end
end
+
+ def click_groups_tab
+ click_link "Groups"
+ end
end
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 99846ecee27..1ea2e36ce28 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -101,7 +101,7 @@ RSpec.describe 'Groups > Members > Manage members' do
add_user('test@example.com', 'Reporter')
- click_link('Pending')
+ click_link('Invited')
page.within('.content-list.members-list') do
expect(page).to have_content('test@example.com')
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 2a17e7d2a5c..71c9b280ebe 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Groups > Members > Maintainer manages access requests' do
it_behaves_like 'Maintainer manages access requests' do
+ let(:has_tabs) { true }
let(:entity) { create(:group, :public) }
let(:members_page_path) { group_group_members_path(entity) }
end
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index 4c34ccf87c3..ad4f5c0b579 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'Search group member' do
end
it 'renders member users' do
- page.within '.user-search-form' do
+ page.within '[data-testid="user-search-form"]' do
fill_in 'search', with: member.name
find('.user-search-btn').click
end
diff --git a/spec/features/groups/members/tabs_spec.rb b/spec/features/groups/members/tabs_spec.rb
new file mode 100644
index 00000000000..fa77d1a2ff8
--- /dev/null
+++ b/spec/features/groups/members/tabs_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Groups > Members > Tabs' do
+ using RSpec::Parameterized::TableSyntax
+
+ shared_examples 'active "Members" tab' do
+ it 'displays "Members" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Members')
+ end
+ end
+
+ shared_examples 'active "Invited" tab' do
+ it 'displays "Invited" tab' do
+ expect(page).to have_selector('.nav-link.active', text: 'Invited')
+ end
+ end
+
+ let(:owner) { create(:user) }
+ let(:group) { create(:group) }
+
+ before do
+ stub_const('Groups::GroupMembersController::MEMBER_PER_PAGE_LIMIT', 1)
+ allow_any_instance_of(Member).to receive(:send_request).and_return(true)
+
+ group.add_owner(owner)
+ sign_in(owner)
+
+ create_list(:group_member, 2, group: group)
+ create_list(:group_member, 2, :invited, group: group)
+ create_list(:group_group_link, 2, shared_group: group)
+ create_list(:group_member, 2, :access_request, group: group)
+ end
+
+ where(:tab, :count) do
+ 'Members' | 3
+ 'Invited' | 2
+ 'Groups' | 2
+ 'Access requests' | 2
+ end
+
+ with_them do
+ it "renders #{params[:tab]} tab" do
+ visit group_group_members_path(group)
+
+ expect(page).to have_selector('.nav-link', text: "#{tab} #{count}")
+ end
+ end
+
+ context 'displays "Members" tab by default' do
+ before do
+ visit group_group_members_path(group)
+ end
+
+ it_behaves_like 'active "Members" tab'
+ end
+
+ context 'when searching "Invited"', :js do
+ before do
+ visit group_group_members_path(group)
+
+ click_link 'Invited'
+
+ page.within '[data-testid="user-search-form"]' do
+ fill_in 'search_invited', with: 'email'
+ find('button[type="submit"]').click
+ end
+ end
+
+ it_behaves_like 'active "Invited" tab'
+
+ context 'and then searching "Members"' do
+ before do
+ click_link 'Members'
+
+ page.within '[data-testid="user-search-form"]' do
+ fill_in 'search', with: 'test'
+ find('button[type="submit"]').click
+ end
+ end
+
+ it_behaves_like 'active "Members" tab'
+ end
+ end
+
+ context 'when using "Invited" pagination', :js do
+ before do
+ visit group_group_members_path(group)
+
+ click_link 'Invited'
+
+ page.within '.pagination' do
+ click_link '2'
+ end
+ end
+
+ it_behaves_like 'active "Invited" tab'
+
+ context 'and then using "Members" pagination' do
+ before do
+ click_link 'Members'
+
+ page.within '.pagination' do
+ click_link '2'
+ end
+ end
+
+ it_behaves_like 'active "Members" tab'
+ end
+ end
+end
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 4c3eaa93352..2fdc75dca91 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Maintainer manages access requests' do
it_behaves_like 'Maintainer manages access requests' do
+ let(:has_tabs) { false }
let(:entity) { create(:project, :public) }
let(:members_page_path) { project_project_members_path(entity) }
end
diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js
index 9bc0bae5c23..66e8a4aea0d 100644
--- a/spec/frontend/registry/explorer/pages/details_spec.js
+++ b/spec/frontend/registry/explorer/pages/details_spec.js
@@ -13,7 +13,7 @@ import {
SET_TAGS_LIST_SUCCESS,
SET_TAGS_PAGINATION,
SET_INITIAL_STATE,
-} from '~/registry/explorer/stores/mutation_types/';
+} from '~/registry/explorer/stores/mutation_types';
import { tagsListResponse } from '../mock_data';
import { DeleteModal } from '../stubs';
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index 2ece7593b41..0685da78924 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -14,7 +14,7 @@ import {
SET_IMAGES_LIST_SUCCESS,
SET_PAGINATION,
SET_INITIAL_STATE,
-} from '~/registry/explorer/stores/mutation_types/';
+} from '~/registry/explorer/stores/mutation_types';
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
diff --git a/spec/frontend/vue_shared/components/identicon_spec.js b/spec/frontend/vue_shared/components/identicon_spec.js
index 53a55dcd6bd..24fc3713e2b 100644
--- a/spec/frontend/vue_shared/components/identicon_spec.js
+++ b/spec/frontend/vue_shared/components/identicon_spec.js
@@ -25,7 +25,7 @@ describe('Identicon', () => {
});
describe('entity id is a number', () => {
- beforeEach(createComponent);
+ beforeEach(() => createComponent());
it('matches snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 3739e1d424d..37bc14c44f2 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -225,6 +225,15 @@ RSpec.describe Gitlab::Danger::Helper do
'ee/spec/foo' | [:backend]
'ee/spec/foo/bar' | [:backend]
+ 'spec/features/foo' | [:test]
+ 'ee/spec/features/foo' | [:test]
+ 'spec/support/shared_examples/features/foo' | [:test]
+ 'ee/spec/support/shared_examples/features/foo' | [:test]
+ 'spec/support/shared_contexts/features/foo' | [:test]
+ 'ee/spec/support/shared_contexts/features/foo' | [:test]
+ 'spec/support/helpers/features/foo' | [:test]
+ 'ee/spec/support/helpers/features/foo' | [:test]
+
'generator_templates/foo' | [:backend]
'vendor/languages.yml' | [:backend]
'vendor/licenses.csv' | [:backend]
diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb
index 29372d15a32..d5e23a91da0 100644
--- a/spec/requests/api/composer_packages_spec.rb
+++ b/spec/requests/api/composer_packages_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe API::ComposerPackages do
let_it_be(:project, reload: true) { create(:project, :custom_repo, files: { 'composer.json' => { name: package_name }.to_json }, group: group) }
let(:headers) { {} }
+ using RSpec::Parameterized::TableSyntax
+
describe 'GET /api/v4/group/:id/-/packages/composer/packages' do
let(:url) { "/group/#{group.id}/-/packages/composer/packages.json" }
@@ -19,32 +21,73 @@ RSpec.describe API::ComposerPackages do
context 'with valid project' do
let!(:package) { create(:composer_package, :with_metadatum, project: project) }
- using RSpec::Parameterized::TableSyntax
+ context 'with a public group' do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'Composer package index' | :success
- 'PUBLIC' | :guest | true | true | 'Composer package index' | :success
- 'PUBLIC' | :developer | true | false | 'Composer package index' | :success
- 'PUBLIC' | :guest | true | false | 'Composer package index' | :success
- 'PUBLIC' | :developer | false | true | 'Composer package index' | :success
- 'PUBLIC' | :guest | false | true | 'Composer package index' | :success
- 'PUBLIC' | :developer | false | false | 'Composer package index' | :success
- 'PUBLIC' | :guest | false | false | 'Composer package index' | :success
- 'PUBLIC' | :anonymous | false | true | 'Composer package index' | :success
- 'PRIVATE' | :developer | true | true | 'Composer package index' | :success
- 'PRIVATE' | :guest | true | true | 'Composer package index' | :success
- 'PRIVATE' | :developer | true | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | true | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
- 'PRIVATE' | :developer | false | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | false | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found
+ where(:project_visibility_level, :user_role, :member, :user_token, :include_package) do
+ 'PUBLIC' | :developer | true | true | :include_package
+ 'PUBLIC' | :developer | true | false | :include_package
+ 'PUBLIC' | :developer | false | false | :include_package
+ 'PUBLIC' | :developer | false | true | :include_package
+ 'PUBLIC' | :guest | true | true | :include_package
+ 'PUBLIC' | :guest | true | false | :include_package
+ 'PUBLIC' | :guest | false | true | :include_package
+ 'PUBLIC' | :guest | false | false | :include_package
+ 'PUBLIC' | :anonymous | false | true | :include_package
+ 'PRIVATE' | :developer | true | true | :include_package
+ 'PRIVATE' | :developer | true | false | :does_not_include_package
+ 'PRIVATE' | :developer | false | true | :does_not_include_package
+ 'PRIVATE' | :developer | false | false | :does_not_include_package
+ 'PRIVATE' | :guest | true | true | :does_not_include_package
+ 'PRIVATE' | :guest | true | false | :does_not_include_package
+ 'PRIVATE' | :guest | false | true | :does_not_include_package
+ 'PRIVATE' | :guest | false | false | :does_not_include_package
+ 'PRIVATE' | :anonymous | false | true | :does_not_include_package
+ end
+
+ with_them do
+ include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do
+ it_behaves_like 'Composer package index', params[:user_role], :success, params[:member], params[:include_package]
+ end
+ end
end
- with_them do
- include_context 'Composer api group access', params[:project_visibility_level], params[:user_role], params[:user_token] do
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ context 'with a private group' do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ context 'with access to the api' do
+ where(:project_visibility_level, :user_role, :member, :user_token, :include_package) do
+ 'PRIVATE' | :developer | true | true | :include_package
+ 'PRIVATE' | :guest | true | true | :does_not_include_package
+ end
+
+ with_them do
+ include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do
+ it_behaves_like 'Composer package index', params[:user_role], :success, params[:member], params[:include_package]
+ end
+ end
+ end
+
+ context 'without access to the api' do
+ where(:project_visibility_level, :user_role, :member, :user_token) do
+ 'PRIVATE' | :developer | true | false
+ 'PRIVATE' | :developer | false | true
+ 'PRIVATE' | :developer | false | false
+ 'PRIVATE' | :guest | true | false
+ 'PRIVATE' | :guest | false | true
+ 'PRIVATE' | :guest | false | false
+ 'PRIVATE' | :anonymous | false | true
+ end
+
+ with_them do
+ include_context 'Composer api project access', params[:project_visibility_level], params[:user_role], params[:user_token] do
+ it_behaves_like 'process Composer api request', params[:user_role], :not_found, params[:member]
+ end
+ end
end
end
end
@@ -60,25 +103,23 @@ RSpec.describe API::ComposerPackages do
subject { get api(url), headers: headers }
context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'Composer provider index' | :success
- 'PUBLIC' | :guest | true | true | 'Composer provider index' | :success
'PUBLIC' | :developer | true | false | 'Composer provider index' | :success
- 'PUBLIC' | :guest | true | false | 'Composer provider index' | :success
'PUBLIC' | :developer | false | true | 'Composer provider index' | :success
- 'PUBLIC' | :guest | false | true | 'Composer provider index' | :success
'PUBLIC' | :developer | false | false | 'Composer provider index' | :success
+ 'PUBLIC' | :guest | true | true | 'Composer provider index' | :success
+ 'PUBLIC' | :guest | true | false | 'Composer provider index' | :success
+ 'PUBLIC' | :guest | false | true | 'Composer provider index' | :success
'PUBLIC' | :guest | false | false | 'Composer provider index' | :success
'PUBLIC' | :anonymous | false | true | 'Composer provider index' | :success
'PRIVATE' | :developer | true | true | 'Composer provider index' | :success
- 'PRIVATE' | :guest | true | true | 'Composer empty provider index' | :success
'PRIVATE' | :developer | true | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | true | false | 'process Composer api request' | :not_found
'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :developer | false | false | 'process Composer api request' | :not_found
+ 'PRIVATE' | :guest | true | true | 'Composer empty provider index' | :success
+ 'PRIVATE' | :guest | true | false | 'process Composer api request' | :not_found
+ 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :guest | false | false | 'process Composer api request' | :not_found
'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found
end
@@ -106,27 +147,25 @@ RSpec.describe API::ComposerPackages do
end
context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
let!(:package) { create(:composer_package, :with_metadatum, name: package_name, project: project) }
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'Composer package api request' | :success
- 'PUBLIC' | :guest | true | true | 'Composer package api request' | :success
'PUBLIC' | :developer | true | false | 'Composer package api request' | :success
- 'PUBLIC' | :guest | true | false | 'Composer package api request' | :success
'PUBLIC' | :developer | false | true | 'Composer package api request' | :success
- 'PUBLIC' | :guest | false | true | 'Composer package api request' | :success
'PUBLIC' | :developer | false | false | 'Composer package api request' | :success
+ 'PUBLIC' | :guest | true | true | 'Composer package api request' | :success
+ 'PUBLIC' | :guest | true | false | 'Composer package api request' | :success
+ 'PUBLIC' | :guest | false | true | 'Composer package api request' | :success
'PUBLIC' | :guest | false | false | 'Composer package api request' | :success
'PUBLIC' | :anonymous | false | true | 'Composer package api request' | :success
'PRIVATE' | :developer | true | true | 'Composer package api request' | :success
- 'PRIVATE' | :guest | true | true | 'process Composer api request' | :not_found
'PRIVATE' | :developer | true | false | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | true | false | 'process Composer api request' | :not_found
'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :developer | false | false | 'process Composer api request' | :not_found
+ 'PRIVATE' | :guest | true | true | 'process Composer api request' | :not_found
+ 'PRIVATE' | :guest | true | false | 'process Composer api request' | :not_found
+ 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :guest | false | false | 'process Composer api request' | :not_found
'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :not_found
end
@@ -153,25 +192,23 @@ RSpec.describe API::ComposerPackages do
shared_examples 'composer package publish' do
context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'Composer package creation' | :created
- 'PUBLIC' | :guest | true | true | 'process Composer api request' | :forbidden
'PUBLIC' | :developer | true | false | 'process Composer api request' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized
'PUBLIC' | :developer | false | true | 'process Composer api request' | :forbidden
- 'PUBLIC' | :guest | false | true | 'process Composer api request' | :forbidden
'PUBLIC' | :developer | false | false | 'process Composer api request' | :unauthorized
+ 'PUBLIC' | :guest | true | true | 'process Composer api request' | :forbidden
+ 'PUBLIC' | :guest | true | false | 'process Composer api request' | :unauthorized
+ 'PUBLIC' | :guest | false | true | 'process Composer api request' | :forbidden
'PUBLIC' | :guest | false | false | 'process Composer api request' | :unauthorized
'PUBLIC' | :anonymous | false | true | 'process Composer api request' | :unauthorized
'PRIVATE' | :developer | true | true | 'Composer package creation' | :created
- 'PRIVATE' | :guest | true | true | 'process Composer api request' | :forbidden
'PRIVATE' | :developer | true | false | 'process Composer api request' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized
'PRIVATE' | :developer | false | true | 'process Composer api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :developer | false | false | 'process Composer api request' | :unauthorized
+ 'PRIVATE' | :guest | true | true | 'process Composer api request' | :forbidden
+ 'PRIVATE' | :guest | true | false | 'process Composer api request' | :unauthorized
+ 'PRIVATE' | :guest | false | true | 'process Composer api request' | :not_found
'PRIVATE' | :guest | false | false | 'process Composer api request' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'process Composer api request' | :unauthorized
end
@@ -251,25 +288,23 @@ RSpec.describe API::ComposerPackages do
let(:branch) { project.repository.find_branch('master') }
let(:sha) { branch.target }
- using RSpec::Parameterized::TableSyntax
-
where(:project_visibility_level, :user_role, :member, :user_token, :expected_status) do
'PUBLIC' | :developer | true | true | :success
- 'PUBLIC' | :guest | true | true | :success
'PUBLIC' | :developer | true | false | :success
- 'PUBLIC' | :guest | true | false | :success
'PUBLIC' | :developer | false | true | :success
- 'PUBLIC' | :guest | false | true | :success
'PUBLIC' | :developer | false | false | :success
+ 'PUBLIC' | :guest | true | true | :success
+ 'PUBLIC' | :guest | true | false | :success
+ 'PUBLIC' | :guest | false | true | :success
'PUBLIC' | :guest | false | false | :success
'PUBLIC' | :anonymous | false | true | :success
'PRIVATE' | :developer | true | true | :success
- 'PRIVATE' | :guest | true | true | :success
'PRIVATE' | :developer | true | false | :success
- 'PRIVATE' | :guest | true | false | :success
'PRIVATE' | :developer | false | true | :success
- 'PRIVATE' | :guest | false | true | :success
'PRIVATE' | :developer | false | false | :success
+ 'PRIVATE' | :guest | true | true | :success
+ 'PRIVATE' | :guest | true | false | :success
+ 'PRIVATE' | :guest | false | true | :success
'PRIVATE' | :guest | false | false | :success
'PRIVATE' | :anonymous | false | true | :success
end
diff --git a/spec/support/csv_response.rb b/spec/support/csv_response.rb
new file mode 100644
index 00000000000..9ed76dcdd4a
--- /dev/null
+++ b/spec/support/csv_response.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+RSpec.configure do |config|
+ config.include_context 'CSV response', type: :controller
+end
diff --git a/spec/support/shared_contexts/csv_response_shared_context.rb b/spec/support/shared_contexts/csv_response_shared_context.rb
new file mode 100644
index 00000000000..af79e393a91
--- /dev/null
+++ b/spec/support/shared_contexts/csv_response_shared_context.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'CSV response' do
+ let(:csv_response) { CSV.parse(response.body) }
+end
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
index 00ce690d2e3..ffe4fb83283 100644
--- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -8,17 +8,18 @@ RSpec.shared_examples 'Maintainer manages access requests' do
entity.request_access(user)
entity.respond_to?(:add_owner) ? entity.add_owner(maintainer) : entity.add_maintainer(maintainer)
sign_in(maintainer)
- end
-
- it 'maintainer can see access requests' do
visit members_page_path
+ if has_tabs
+ click_on 'Access requests'
+ end
+ end
+
+ it 'maintainer can see access requests', :js do
expect_visible_access_request(entity, user)
end
it 'maintainer can grant access', :js do
- visit members_page_path
-
expect_visible_access_request(entity, user)
click_on 'Grant access'
@@ -31,8 +32,6 @@ RSpec.shared_examples 'Maintainer manages access requests' do
end
it 'maintainer can deny access', :js do
- visit members_page_path
-
expect_visible_access_request(entity, user)
# Open modal
@@ -47,7 +46,13 @@ RSpec.shared_examples 'Maintainer manages access requests' do
end
def expect_visible_access_request(entity, user)
- expect(page).to have_content "Users requesting access to #{entity.name} 1"
+ if has_tabs
+ expect(page).to have_content "Access requests 1"
+ expect(page).to have_content "Users requesting access to #{entity.name}"
+ else
+ expect(page).to have_content "Users requesting access to #{entity.name} 1"
+ end
+
expect(page).to have_content user.name
end
diff --git a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
index 5257980d7df..5ebf4e350e3 100644
--- a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
@@ -7,13 +7,17 @@ RSpec.shared_context 'Composer user type' do |user_type, add_member|
end
end
-RSpec.shared_examples 'Composer package index' do |user_type, status, add_member = true|
+RSpec.shared_examples 'Composer package index' do |user_type, status, add_member, include_package|
include_context 'Composer user type', user_type, add_member do
+ let(:expected_packages) { include_package == :include_package ? [package] : [] }
+ let(:presenter) { ::Packages::Composer::PackagesPresenter.new(group, expected_packages ) }
+
it 'returns the package index' do
subject
expect(response).to have_gitlab_http_status(status)
expect(response).to match_response_schema('public_api/v4/packages/composer/index')
+ expect(json_response).to eq presenter.root
end
end
end