Commit a3b77cd9 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents be054b4a aaaf3ac5
...@@ -516,28 +516,8 @@ ...@@ -516,28 +516,8 @@
left: auto; left: auto;
max-height: $dropdown-max-height-lg; max-height: $dropdown-max-height-lg;
li.current-user {
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
.user-name {
display: block;
}
.user-status { .user-status {
margin-right: 0;
max-width: 240px; max-width: 240px;
font-size: $gl-font-size-small;
gl-emoji {
font-size: $gl-font-size-small;
}
.user-status-emoji {
gl-emoji {
font-size: $gl-font-size;
}
}
}
} }
svg { svg {
......
...@@ -159,8 +159,8 @@ class JiraService < IssueTrackerService ...@@ -159,8 +159,8 @@ class JiraService < IssueTrackerService
# support any events. # support any events.
end end
def find_issue(issue_key) def find_issue(issue_key, options = {})
jira_request { client.Issue.find(issue_key) } jira_request { client.Issue.find(issue_key, options) }
end end
def close_issue(entity, external_issue, current_user) def close_issue(entity, external_issue, current_user)
......
...@@ -2,18 +2,12 @@ ...@@ -2,18 +2,12 @@
%ul %ul
%li.current-user %li.current-user
.user-name.gl-font-weight-bold - if current_user_menu?(:profile)
= current_user.name = link_to current_user, class: 'gl-line-height-20!', data: { user: current_user.username, testid: 'user-profile-link' } do
- if current_user&.status && user_status_set_to_busy?(current_user.status) = render 'layouts/header/current_user_dropdown_item'
%span.gl-font-weight-normal.gl-text-gray-500= s_("UserProfile|(Busy)") - else
= current_user.to_reference .gl-py-3.gl-px-4
- if current_user.status = render 'layouts/header/current_user_dropdown_item'
.user-status.d-flex.align-items-center.gl-mt-2.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } }
- if show_status_emoji?(current_user.status)
.user-status-emoji.d-flex.align-items-center
= emoji_icon current_user.status.emoji
%span.user-status-message.str-truncated
= current_user.status.message_html.html_safe
%li.divider %li.divider
- if can?(current_user, :update_user_status, current_user) - if can?(current_user, :update_user_status, current_user)
%li %li
...@@ -22,9 +16,6 @@ ...@@ -22,9 +16,6 @@
= s_('SetStatusModal|Edit status') = s_('SetStatusModal|Edit status')
- else - else
= s_('SetStatusModal|Set status') = s_('SetStatusModal|Set status')
- if current_user_menu?(:profile)
%li
= link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username }
- if current_user_menu?(:start_trial) - if current_user_menu?(:start_trial)
%li %li
%a.trial-link{ href: trials_link_url } %a.trial-link{ href: trials_link_url }
...@@ -32,7 +23,9 @@ ...@@ -32,7 +23,9 @@
= emoji_icon('rocket') = emoji_icon('rocket')
- if current_user_menu?(:settings) - if current_user_menu?(:settings)
%li %li
= link_to s_("CurrentUser|Settings"), profile_path, data: { qa_selector: 'settings_link' } = link_to s_("CurrentUser|Edit profile"), profile_path, data: { qa_selector: 'edit_profile_link' }
%li
= link_to s_("CurrentUser|Preferences"), profile_preferences_path
= render_if_exists 'layouts/header/buy_pipeline_minutes', project: @project, namespace: @group = render_if_exists 'layouts/header/buy_pipeline_minutes', project: @project, namespace: @group
= render_if_exists 'layouts/header/upgrade' = render_if_exists 'layouts/header/upgrade'
......
.gl-font-weight-bold
= current_user.name
- if current_user&.status && user_status_set_to_busy?(current_user.status)
%span.gl-font-weight-normal.gl-text-gray-500= s_("UserProfile|(Busy)")
= current_user.to_reference
- if current_user.status
.user-status.d-flex.align-items-center.gl-mt-2.gl-mr-0.gl-font-sm.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } }
- if show_status_emoji?(current_user.status)
.user-status-emoji.d-flex.align-items-center
= emoji_icon current_user.status.emoji
%span.user-status-message.str-truncated
= current_user.status.message_html.html_safe
---
title: Improve user dropdown items
merge_request: 53175
author:
type: changed
...@@ -80,7 +80,7 @@ Learn more about GitLab account management: ...@@ -80,7 +80,7 @@ Learn more about GitLab account management:
|:-----------------------------------------------------------|:------------| |:-----------------------------------------------------------|:------------|
| [User account](user/profile/index.md) | Manage your account. | | [User account](user/profile/index.md) | Manage your account. |
| [Authentication](topics/authentication/index.md) | Account security with two-factor authentication, set up your SSH keys, and deploy keys for secure access to your projects. | | [Authentication](topics/authentication/index.md) | Account security with two-factor authentication, set up your SSH keys, and deploy keys for secure access to your projects. |
| [Profile settings](user/profile/index.md#profile-settings) | Manage your profile settings, two factor authentication, and more. | | [User settings](user/profile/index.md#user-settings) | Manage your user settings, two factor authentication, and more. |
| [User permissions](user/permissions.md) | Learn what each role in a project can do. | | [User permissions](user/permissions.md) | Learn what each role in a project can do. |
### Git and GitLab ### Git and GitLab
......
...@@ -339,7 +339,7 @@ an expiry for the artifacts, they are marked for deletion right after that date ...@@ -339,7 +339,7 @@ an expiry for the artifacts, they are marked for deletion right after that date
Otherwise, they expire per the [default artifacts expiration setting](../user/admin_area/settings/continuous_integration.md). Otherwise, they expire per the [default artifacts expiration setting](../user/admin_area/settings/continuous_integration.md).
Artifacts are cleaned up by the `expire_build_artifacts_worker` cron job which Sidekiq Artifacts are cleaned up by the `expire_build_artifacts_worker` cron job which Sidekiq
runs every hour at 50 minutes (`50 * * * *`). runs every 7 minutes (`*/7 * * * *`).
To change the default schedule on which the artifacts are expired, follow the To change the default schedule on which the artifacts are expired, follow the
steps below. steps below.
...@@ -350,7 +350,7 @@ steps below. ...@@ -350,7 +350,7 @@ steps below.
your schedule in cron syntax: your schedule in cron syntax:
```ruby ```ruby
gitlab_rails['expire_build_artifacts_worker_cron'] = "50 * * * *" gitlab_rails['expire_build_artifacts_worker_cron'] = "*/7 * * * *"
``` ```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
...@@ -362,7 +362,7 @@ steps below. ...@@ -362,7 +362,7 @@ steps below.
```yaml ```yaml
expire_build_artifacts_worker: expire_build_artifacts_worker:
cron: "50 * * * *" cron: "*/7 * * * *"
``` ```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
......
...@@ -235,7 +235,7 @@ separate Rails process to debug the issue: ...@@ -235,7 +235,7 @@ separate Rails process to debug the issue:
1. Log in to your GitLab account. 1. Log in to your GitLab account.
1. Copy the URL that is causing problems (e.g. `https://gitlab.com/ABC`). 1. Copy the URL that is causing problems (e.g. `https://gitlab.com/ABC`).
1. Create a Personal Access Token for your user (Profile Settings -> Access Tokens). 1. Create a Personal Access Token for your user (User Settings -> Access Tokens).
1. Bring up the [GitLab Rails console.](../operations/rails_console.md#starting-a-rails-console-session) 1. Bring up the [GitLab Rails console.](../operations/rails_console.md#starting-a-rails-console-session)
1. At the Rails console, run: 1. At the Rails console, run:
......
...@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Get a single avatar URL ## Get a single avatar URL
Get a single [avatar](../user/profile/index.md#profile-settings) URL for a user with the given email address. Get a single [avatar](../user/profile/index.md#user-settings) URL for a user with the given email address.
If: If:
......
...@@ -179,7 +179,9 @@ If you're getting the message `Signing in using your GitHub account without a pr ...@@ -179,7 +179,9 @@ If you're getting the message `Signing in using your GitHub account without a pr
GitLab account is not allowed. Create a GitLab account first, and then connect it to your GitLab account is not allowed. Create a GitLab account first, and then connect it to your
GitHub account` when signing in, in GitLab: GitHub account` when signing in, in GitLab:
1. Go to your **Profile > Account**. 1. In the top-right corner, select your avatar.
1. Under the "Social sign-in" section, click **Connect** near the GitHub icon. 1. Select **Edit profile**.
1. In the left sidebar, select **Account**.
1. In the **Social sign-in** section, select **Connect to GitHub**.
After that, you should be able to sign in via GitHub successfully. After that, you should be able to sign in via GitHub successfully.
...@@ -12,11 +12,9 @@ To enable the GitLab.com OmniAuth provider you must register your application wi ...@@ -12,11 +12,9 @@ To enable the GitLab.com OmniAuth provider you must register your application wi
GitLab.com generates an application ID and secret key for you to use. GitLab.com generates an application ID and secret key for you to use.
1. Sign in to GitLab.com. 1. Sign in to GitLab.com.
1. In the top-right corner, select your avatar.
1. On the upper right corner, click on your avatar and go to your **Settings**. 1. Select **Edit profile**.
1. In the left sidebar, select **Applications**.
1. Select **Applications** in the left menu.
1. Provide the required details for **Add new application**. 1. Provide the required details for **Add new application**.
- Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive.
- Redirect URI: - Redirect URI:
...@@ -29,10 +27,8 @@ GitLab.com generates an application ID and secret key for you to use. ...@@ -29,10 +27,8 @@ GitLab.com generates an application ID and secret key for you to use.
The first link is required for the importer and second for the authorization. The first link is required for the importer and second for the authorization.
1. Select **Save application**. 1. Select **Save application**.
1. You should now see an **Application ID** and **Secret**. Keep this page open as you continue 1. You should now see an **Application ID** and **Secret**. Keep this page open as you continue
configuration. configuration.
1. On your GitLab server, open the configuration file. 1. On your GitLab server, open the configuration file.
For Omnibus package: For Omnibus package:
...@@ -50,10 +46,9 @@ GitLab.com generates an application ID and secret key for you to use. ...@@ -50,10 +46,9 @@ GitLab.com generates an application ID and secret key for you to use.
``` ```
1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
1. Add the provider configuration: 1. Add the provider configuration:
For Omnibus package: For Omnibus installations:
```ruby ```ruby
gitlab_rails['omniauth_providers'] = [ gitlab_rails['omniauth_providers'] = [
...@@ -76,16 +71,13 @@ GitLab.com generates an application ID and secret key for you to use. ...@@ -76,16 +71,13 @@ GitLab.com generates an application ID and secret key for you to use.
``` ```
1. Change `'YOUR_APP_ID'` to the Application ID from the GitLab.com application page. 1. Change `'YOUR_APP_ID'` to the Application ID from the GitLab.com application page.
1. Change `'YOUR_APP_SECRET'` to the secret from the GitLab.com application page. 1. Change `'YOUR_APP_SECRET'` to the secret from the GitLab.com application page.
1. Save the configuration file. 1. Save the configuration file.
1. Based on how GitLab was installed, implement these changes by using 1. Based on how GitLab was installed, implement these changes by using
the appropriate method: the appropriate method:
- Omnibus GitLab: [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). - Omnibus GitLab: [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure).
- Source: [Restart GitLab](../administration/restart_gitlab.md#installations-from-source). - Source: [restart GitLab](../administration/restart_gitlab.md#installations-from-source).
On the sign-in page, there should now be a GitLab.com icon following the On the sign-in page, there should now be a GitLab.com icon following the
regular sign-in form. Select the icon to begin the authentication process. regular sign-in form. Select the icon to begin the authentication process.
......
...@@ -33,7 +33,9 @@ To learn more about Gitpod, see their [features](https://www.gitpod.io/features/ ...@@ -33,7 +33,9 @@ To learn more about Gitpod, see their [features](https://www.gitpod.io/features/
With the Gitpod integration enabled for your GitLab instance, to enable it for yourself: With the Gitpod integration enabled for your GitLab instance, to enable it for yourself:
1. Select your avatar in the top-right corner, then select **Settings > Preferences**. 1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Account**.
1. Under **Integrations**, locate the **Gitpod** section. 1. Under **Integrations**, locate the **Gitpod** section.
1. Check the **Enable Gitpod integration** checkbox and select the **Save changes** button. 1. Check the **Enable Gitpod integration** checkbox and select the **Save changes** button.
......
...@@ -70,9 +70,10 @@ Grant a GitLab user access to the select GitLab projects. ...@@ -70,9 +70,10 @@ Grant a GitLab user access to the select GitLab projects.
Create a personal access token to authorize Jenkins' access to GitLab. Create a personal access token to authorize Jenkins' access to GitLab.
1. Log in to GitLab as the user to be used with Jenkins. 1. Sign in to GitLab as the user to be used with Jenkins.
1. Click your avatar, then **Settings**. 1. In the top-right corner, select your avatar.
1. Click **Access Tokens** in the sidebar. 1. Select **Edit profile**.
1. In the left sidebar, select **Access Tokens**.
1. Create a personal access token with the **API** scope checkbox checked. For more details, see 1. Create a personal access token with the **API** scope checkbox checked. For more details, see
[Personal access tokens](../user/profile/personal_access_tokens.md). [Personal access tokens](../user/profile/personal_access_tokens.md).
1. Record the personal access token's value, because it's required in [Configure the Jenkins server](#configure-the-jenkins-server) section. 1. Record the personal access token's value, because it's required in [Configure the Jenkins server](#configure-the-jenkins-server) section.
......
...@@ -77,10 +77,10 @@ To ensure that regular user account maintenance doesn't impact your integration, ...@@ -77,10 +77,10 @@ To ensure that regular user account maintenance doesn't impact your integration,
create and use a single-purpose `jira` user in GitLab. create and use a single-purpose `jira` user in GitLab.
1. In GitLab, create a new application to allow Jira to connect with your GitLab account. 1. In GitLab, create a new application to allow Jira to connect with your GitLab account.
1. Sign in to the GitLab account that you want Jira to use to connect to GitLab. 1. Sign in to the GitLab account that you want Jira to use to connect to GitLab.
1. In the top right corner, click your profile avatar. 1. In the top-right corner, select your avatar.
1. Click **Settings > Applications** to display the form to create a new application. 1. Select **Edit profile**.
1. In the left sidebar, select **Applications**.
1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`. 1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`.
1. In the **Redirect URI** field, enter `https://<gitlab.example.com>/login/oauth/callback`, 1. In the **Redirect URI** field, enter `https://<gitlab.example.com>/login/oauth/callback`,
replacing `<gitlab.example.com>` with your GitLab instance domain. For example, if you are using GitLab.com, replacing `<gitlab.example.com>` with your GitLab instance domain. For example, if you are using GitLab.com,
...@@ -94,8 +94,7 @@ create and use a single-purpose `jira` user in GitLab. ...@@ -94,8 +94,7 @@ create and use a single-purpose `jira` user in GitLab.
![GitLab application setup](img/jira_dev_panel_gl_setup_1.png) ![GitLab application setup](img/jira_dev_panel_gl_setup_1.png)
1. Check **API** in the Scopes section, and uncheck any other checkboxes. 1. Check **API** in the **Scopes** section, and clear any other checkboxes.
1. Click **Save application**. GitLab displays the generated **Application ID** 1. Click **Save application**. GitLab displays the generated **Application ID**
and **Secret** values. Copy these values, which you use in Jira. and **Secret** values. Copy these values, which you use in Jira.
......
...@@ -115,9 +115,10 @@ existing GitLab account. To do so: ...@@ -115,9 +115,10 @@ existing GitLab account. To do so:
If you're not an administrator: If you're not an administrator:
1. Select your avatar in the upper-right corner, and select **Settings**. 1. In the top-right corner, select your avatar.
1. Select Account. In the **Social sign-in** section, select 1. Select **Edit profile**.
**Connect Kerberos SPNEGO**. 1. In the left sidebar, select **Account**.
1. In the **Social sign-in** section, select **Connect Kerberos SPNEGO**.
If you don't see a **Social sign-in** Kerberos option, follow the If you don't see a **Social sign-in** Kerberos option, follow the
requirements in [Enable single sign-on](#enable-single-sign-on). requirements in [Enable single sign-on](#enable-single-sign-on).
......
...@@ -43,16 +43,17 @@ levels. The default callback URL is `http://your-gitlab.example.com/users/auth/g ...@@ -43,16 +43,17 @@ levels. The default callback URL is `http://your-gitlab.example.com/users/auth/g
## Add an application through the profile ## Add an application through the profile
To add a new application via your profile, select your avatar in the top right, and then select To add a new application via your profile:
**Settings > Applications**. You can then enter a **Name**, **Redirect URI** and
OAuth 2 scopes as defined in [Authorized Applications](#authorized-applications).
- The **Redirect URI** is the URL where users are sent after they authorize with GitLab. 1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Applications**.
1. Enter a **Name**, **Redirect URI** and OAuth 2 scopes as defined in [Authorized Applications](#authorized-applications).
The **Redirect URI** is the URL where users are sent after they authorize with GitLab.
1. Select **Save application**. GitLab displays:
When you select **Save application**, GitLab displays: - Application ID: OAuth 2 Client ID.
- Secret: OAuth 2 Client Secret.
- Application ID: OAuth 2 Client ID.
- Secret: OAuth 2 Client Secret.
## OAuth applications in the Admin Area ## OAuth applications in the Admin Area
......
...@@ -138,9 +138,10 @@ provider such as Twitter can be enabled. Follow the steps below to enable an ...@@ -138,9 +138,10 @@ provider such as Twitter can be enabled. Follow the steps below to enable an
OmniAuth provider for an existing user. OmniAuth provider for an existing user.
1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider. 1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider.
1. Go to profile settings (the silhouette icon in the top right corner). 1. In the top-right corner, select your avatar.
1. Select the "Account" tab. 1. Select **Edit profile**.
1. Under "Connected Accounts" select the desired OmniAuth provider, such as Twitter. 1. In the left sidebar, select **Account**.
1. In the **Connected Accounts** section, select the desired OmniAuth provider, such as Twitter.
1. The user is redirected to the provider. After the user authorizes GitLab, 1. The user is redirected to the provider. After the user authorizes GitLab,
they are redirected back to GitLab. they are redirected back to GitLab.
......
...@@ -87,9 +87,12 @@ You can skip this step if you already have your GitLab repositories searchable i ...@@ -87,9 +87,12 @@ You can skip this step if you already have your GitLab repositories searchable i
If a GitLab administrator has enabled Sourcegraph, you can enable this feature in your user preferences. If a GitLab administrator has enabled Sourcegraph, you can enable this feature in your user preferences.
1. In GitLab, click your avatar in the top-right corner, then click **Settings**. On the left-hand nav, click **Preferences**. In GitLab:
1. Under **Integrations**, find the **Sourcegraph** section.
1. Check **Enable Sourcegraph**. 1. In the top-right corner, select your avatar.
1. Select **Preferences**.
1. In the **Integrations** section, select the checkbox under **Sourcegraph**.
1. Select **Save changes**.
![Sourcegraph user preferences](img/sourcegraph_user_preferences_v12_5.png) ![Sourcegraph user preferences](img/sourcegraph_user_preferences_v12_5.png)
......
...@@ -38,8 +38,11 @@ If your instance's URL is `https://example.com`, your API URL is `https://exampl ...@@ -38,8 +38,11 @@ If your instance's URL is `https://example.com`, your API URL is `https://exampl
Your GitLab personal access token enables your GitLab account to be accessed Your GitLab personal access token enables your GitLab account to be accessed
from Trello. from Trello.
> Find it in GitLab by clicking on your avatar (upright corner), from which you access To find it in GitLab:
your user **Settings** > **Access Tokens**.
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Access Tokens**.
Learn more about generating a personal access token in the Learn more about generating a personal access token in the
[Personal Access Token Documentation](../user/profile/personal_access_tokens.md). [Personal Access Token Documentation](../user/profile/personal_access_tokens.md).
......
...@@ -18,12 +18,15 @@ The following assumes you already have Vault installed and running. ...@@ -18,12 +18,15 @@ The following assumes you already have Vault installed and running.
1. **Get the OpenID Connect client ID and secret from GitLab:** 1. **Get the OpenID Connect client ID and secret from GitLab:**
First you must create a GitLab application to obtain an application ID and secret for authenticating into Vault. To do this, sign in to GitLab and follow these steps: First you must create a GitLab application to obtain an application ID and secret for authenticating into Vault.
To do this, sign in to GitLab and follow these steps:
1. On GitLab, click your avatar on the top-right corner, and select your user **Settings > Applications**.
1. Fill out the application **Name** and [**Redirect URI**](https://www.vaultproject.io/docs/auth/jwt#redirect-uris), 1. In the top-right corner, select your avatar.
making sure to select the **OpenID** scope. 1. Select **Edit profile**.
1. Save application. 1. In the left sidebar, select **Applications**.
1. Fill out the application **Name** and [**Redirect URI**](https://www.vaultproject.io/docs/auth/jwt#redirect-uris).
1. Select the **OpenID** scope.
1. Select **Save application**.
1. Copy client ID and secret, or keep the page open for reference. 1. Copy client ID and secret, or keep the page open for reference.
![GitLab OAuth provider](img/gitlab_oauth_vault_v12_6.png) ![GitLab OAuth provider](img/gitlab_oauth_vault_v12_6.png)
......
...@@ -15,7 +15,13 @@ This command enables the namespaces feature introduced in GitLab 4.0. It moves e ...@@ -15,7 +15,13 @@ This command enables the namespaces feature introduced in GitLab 4.0. It moves e
The **repository location changes as part of this task**, so you must **update all your Git URLs** to The **repository location changes as part of this task**, so you must **update all your Git URLs** to
point to the new location. point to the new location.
The username can be changed at **Profile > Account**. To change your username:
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Account**.
1. In the **Change username** section, type the new username.
1. Select **Update username**.
For example: For example:
......
...@@ -182,8 +182,9 @@ Now you can copy the SSH key you created to your GitLab account. ...@@ -182,8 +182,9 @@ Now you can copy the SSH key you created to your GitLab account.
If you're using an RSA key, substitute accordingly. If you're using an RSA key, substitute accordingly.
1. Navigate to `https://gitlab.com` or your local GitLab instance URL and sign in. 1. Navigate to `https://gitlab.com` or your local GitLab instance URL and sign in.
1. Select your avatar in the upper right corner, and click **Settings** 1. In the top-right corner, select your avatar.
1. Click **SSH Keys**. 1. Select **Edit profile**.
1. In the left sidebar, select **SSH Keys**.
1. Paste the public key that you copied into the **Key** text box. 1. Paste the public key that you copied into the **Key** text box.
1. Make sure your key includes a descriptive name in the **Title** text box, such as _Work Laptop_ or 1. Make sure your key includes a descriptive name in the **Title** text box, such as _Work Laptop_ or
_Home Workstation_. _Home Workstation_.
......
...@@ -93,7 +93,7 @@ The **Seat usage** page lists all users occupying seats. Details for each user i ...@@ -93,7 +93,7 @@ The **Seat usage** page lists all users occupying seats. Details for each user i
- Full name - Full name
- Username - Username
- Public email address (if they have provided one in their [profile settings](../../user/profile/index.md#profile-settings)) - Public email address (if they have provided one in their [user settings](../../user/profile/index.md#user-settings))
The Seat usage listing is updated live, but the usage statistics on the billing page are updated The Seat usage listing is updated live, but the usage statistics on the billing page are updated
only once per day. For this reason there can be a minor difference between the seat usage listing only once per day. For this reason there can be a minor difference between the seat usage listing
...@@ -247,8 +247,11 @@ Quotas apply to: ...@@ -247,8 +247,11 @@ Quotas apply to:
subgroups, and nested projects. To view the group's usage, navigate to the group, subgroups, and nested projects. To view the group's usage, navigate to the group,
then **Settings > Usage Quotas**. then **Settings > Usage Quotas**.
- Your personal account, where the minutes are available for your personal projects. - Your personal account, where the minutes are available for your personal projects.
To view and buy personal minutes, click your avatar, then To view and buy personal minutes:
**Settings > [Usage Quotas](https://gitlab.com/profile/usage_quotas#pipelines-quota-tab)**.
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **[Usage Quotas](https://gitlab.com/profile/usage_quotas#pipelines-quota-tab)**.
Only pipeline minutes for GitLab shared runners are restricted. If you have a Only pipeline minutes for GitLab shared runners are restricted. If you have a
specific runner set up for your projects, there is no limit to your build time on GitLab SaaS. specific runner set up for your projects, there is no limit to your build time on GitLab SaaS.
...@@ -282,10 +285,12 @@ To purchase additional minutes for your group on GitLab SaaS: ...@@ -282,10 +285,12 @@ To purchase additional minutes for your group on GitLab SaaS:
To purchase additional minutes for your personal namespace: To purchase additional minutes for your personal namespace:
1. Click your avatar, then go to **Settings > Usage Quotas**. 1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Usage Quotas**.
1. Select **Buy additional minutes** and GitLab redirects you to the Customers Portal. 1. Select **Buy additional minutes** and GitLab redirects you to the Customers Portal.
1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes are synced to your personal namespace. 1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes are synced to your personal namespace.
1. To confirm the available CI minutes for your personal projects, click your avatar, then go to **Settings > Usage Quotas**. 1. To confirm the available CI minutes for your personal projects, go to the **Usage Quotas** settings again.
The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month. The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month.
......
...@@ -22,7 +22,7 @@ SAML SSO is not supported at the subgroup level. ...@@ -22,7 +22,7 @@ SAML SSO is not supported at the subgroup level.
## Configuring your Identity Provider ## Configuring your Identity Provider
1. Navigate to the group and click **Settings > SAML SSO**. 1. Navigate to the group and select **Settings > SAML SSO**.
1. Configure your SAML server using the **Assertion consumer service URL**, **Identifier**, and **GitLab single sign-on URL**. Alternatively GitLab provides [metadata XML configuration](#metadata-configuration). See [specific identity provider documentation](#providers) for more details. 1. Configure your SAML server using the **Assertion consumer service URL**, **Identifier**, and **GitLab single sign-on URL**. Alternatively GitLab provides [metadata XML configuration](#metadata-configuration). See [specific identity provider documentation](#providers) for more details.
1. Configure the SAML response to include a NameID that uniquely identifies each user. 1. Configure the SAML response to include a NameID that uniquely identifies each user.
1. Configure [required assertions](group_managed_accounts.md#assertions) if using [Group Managed Accounts](group_managed_accounts.md). 1. Configure [required assertions](group_managed_accounts.md#assertions) if using [Group Managed Accounts](group_managed_accounts.md).
...@@ -57,7 +57,7 @@ We recommend setting the NameID format to `Persistent` unless using a field (suc ...@@ -57,7 +57,7 @@ We recommend setting the NameID format to `Persistent` unless using a field (suc
GitLab provides metadata XML that can be used to configure your Identity Provider. GitLab provides metadata XML that can be used to configure your Identity Provider.
1. Navigate to the group and click **Settings > SAML SSO**. 1. Navigate to the group and select **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**. 1. Copy the provided **GitLab metadata URL**.
1. Follow your Identity Provider's documentation and paste the metadata URL when it's requested. 1. Follow your Identity Provider's documentation and paste the metadata URL when it's requested.
...@@ -69,8 +69,8 @@ After you set up your identity provider to work with GitLab, you must configure ...@@ -69,8 +69,8 @@ After you set up your identity provider to work with GitLab, you must configure
1. Find the SSO URL from your Identity Provider and enter it the **Identity provider single sign-on URL** field. 1. Find the SSO URL from your Identity Provider and enter it the **Identity provider single sign-on URL** field.
1. Find and enter the fingerprint for the SAML token signing certificate in the **Certificate** field. 1. Find and enter the fingerprint for the SAML token signing certificate in the **Certificate** field.
1. Select the access level to be applied to newly added users in the **Default membership role** field. The default access level is 'Guest'. 1. Select the access level to be applied to newly added users in the **Default membership role** field. The default access level is 'Guest'.
1. Click the **Enable SAML authentication for this group** toggle switch. 1. Select the **Enable SAML authentication for this group** toggle switch.
1. Click the **Save changes** button. 1. Select the **Save changes** button.
![Group SAML Settings for GitLab.com](img/group_saml_settings_v13_3.png) ![Group SAML Settings for GitLab.com](img/group_saml_settings_v13_3.png)
...@@ -216,7 +216,7 @@ To link SAML to your existing GitLab.com account: ...@@ -216,7 +216,7 @@ To link SAML to your existing GitLab.com account:
1. Sign in to your GitLab.com account. 1. Sign in to your GitLab.com account.
1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group owner can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider. 1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group owner can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider.
1. Click **Authorize**. 1. Select **Authorize**.
1. Enter your credentials on the Identity Provider if prompted. 1. Enter your credentials on the Identity Provider if prompted.
1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com. 1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
...@@ -225,7 +225,7 @@ On subsequent visits, you should be able to go [sign in to GitLab.com with SAML] ...@@ -225,7 +225,7 @@ On subsequent visits, you should be able to go [sign in to GitLab.com with SAML]
### Signing in to GitLab.com with SAML ### Signing in to GitLab.com with SAML
1. Sign in to your identity provider. 1. Sign in to your identity provider.
1. From the list of apps, click on the "GitLab.com" app (The name is set by the administrator of the identity provider). 1. From the list of apps, select the "GitLab.com" app. (The name is set by the administrator of the identity provider.)
1. You are then signed in to GitLab.com and redirected to the group. 1. You are then signed in to GitLab.com and redirected to the group.
### Configure user settings from SAML response ### Configure user settings from SAML response
...@@ -293,10 +293,15 @@ Users can unlink SAML for a group from their profile page. This can be helpful i ...@@ -293,10 +293,15 @@ Users can unlink SAML for a group from their profile page. This can be helpful i
- Your SAML NameID has changed and so GitLab can no longer find your user. - Your SAML NameID has changed and so GitLab can no longer find your user.
WARNING: WARNING:
Unlinking an account removes all roles assigned to that user within the group. Unlinking an account removes all roles assigned to that user in the group.
If a user re-links their account, roles need to be reassigned. If a user re-links their account, roles need to be reassigned.
For example, to unlink the `MyOrg` account, the following **Disconnect** button is available under **Profile > Accounts**: For example, to unlink the `MyOrg` account:
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Account**.
1. In the **Social sign-in** section, select **Disconnect** next to the connected account.
![Unlink Group SAML](img/unlink_group_saml.png) ![Unlink Group SAML](img/unlink_group_saml.png)
......
...@@ -17,20 +17,21 @@ Deleting a user will delete all projects in that user namespace. ...@@ -17,20 +17,21 @@ Deleting a user will delete all projects in that user namespace.
## As a user ## As a user
As a user, you can delete your own account by: As a user, to delete your own account:
1. Clicking on your avatar. 1. In the top-right corner, select your avatar.
1. Navigating to **Settings > Account**. 1. Select **Edit profile**.
1. Selecting **Delete account**. 1. In the left sidebar, select **Account**.
1. Select **Delete account**.
## As an administrator ## As an administrator
As an administrator, you can delete a user account by: As an administrator, to delete a user account:
1. Navigating to **Admin Area > Overview > Users**. 1. Go to **Admin Area > Overview > Users**.
1. Selecting a user. 1. Select a user.
1. Under the **Account** tab, clicking: 1. Under the **Account** tab, select:
- **Delete user** to delete only the user but maintaining their - **Delete user** to delete only the user but maintain their
[associated records](#associated-records). [associated records](#associated-records).
- **Delete user and contributions** to delete the user and - **Delete user and contributions** to delete the user and
their associated records. their associated records.
......
--- ---
redirect_to: '../index.md#profile-settings' redirect_to: '../index.md#user-settings'
--- ---
This document was moved to [../index.md#profile-settings](../index.md#profile-settings). This document was moved to [../index.md#user-settings](../index.md#user-settings).
<!-- This redirect file can be deleted after February 1, 2021. --> <!-- This redirect file can be deleted after February 1, 2021. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
...@@ -44,7 +44,7 @@ To enable 2FA: ...@@ -44,7 +44,7 @@ To enable 2FA:
1. **In GitLab:** 1. **In GitLab:**
1. Sign in to your GitLab account. 1. Sign in to your GitLab account.
1. Go to your [**Profile settings**](../index.md#profile-settings). 1. Go to your [**User settings**](../index.md#user-settings).
1. Go to **Account**. 1. Go to **Account**.
1. Select **Enable Two-factor Authentication**. 1. Select **Enable Two-factor Authentication**.
1. **On your device (usually your phone):** 1. **On your device (usually your phone):**
...@@ -246,7 +246,7 @@ Search for `security.webauth.u2f` and double click on it to toggle to `true`. ...@@ -246,7 +246,7 @@ Search for `security.webauth.u2f` and double click on it to toggle to `true`.
To set up 2FA with a U2F device: To set up 2FA with a U2F device:
1. Sign in to your GitLab account. 1. Sign in to your GitLab account.
1. Go to your [**Profile settings**](../index.md#profile-settings). 1. Go to your [**User settings**](../index.md#user-settings).
1. Go to **Account**. 1. Go to **Account**.
1. Click **Enable Two-Factor Authentication**. 1. Click **Enable Two-Factor Authentication**.
1. Connect your U2F device. 1. Connect your U2F device.
...@@ -282,7 +282,7 @@ and the following mobile browsers: ...@@ -282,7 +282,7 @@ and the following mobile browsers:
To set up 2FA with a WebAuthn compatible device: To set up 2FA with a WebAuthn compatible device:
1. Sign in to your GitLab account. 1. Sign in to your GitLab account.
1. Go to your [**Profile settings**](../index.md#profile-settings). 1. Go to your [**User settings**](../index.md#user-settings).
1. Go to **Account**. 1. Go to **Account**.
1. Select **Enable Two-Factor Authentication**. 1. Select **Enable Two-Factor Authentication**.
1. Plug in your WebAuthn device. 1. Plug in your WebAuthn device.
...@@ -349,7 +349,7 @@ request and you're automatically signed in. ...@@ -349,7 +349,7 @@ request and you're automatically signed in.
If you ever need to disable 2FA: If you ever need to disable 2FA:
1. Sign in to your GitLab account. 1. Sign in to your GitLab account.
1. Go to your [**Profile settings**](../index.md#profile-settings). 1. Go to your [**User settings**](../index.md#user-settings).
1. Go to **Account**. 1. Go to **Account**.
1. Click **Disable**, under **Two-Factor Authentication**. 1. Click **Disable**, under **Two-Factor Authentication**.
...@@ -434,7 +434,7 @@ a new set of recovery codes with SSH: ...@@ -434,7 +434,7 @@ a new set of recovery codes with SSH:
When prompted for a two-factor code, enter one of the recovery codes obtained When prompted for a two-factor code, enter one of the recovery codes obtained
from the command-line output. from the command-line output.
After signing in, visit your **Profile settings > Account** immediately to set After signing in, visit your **User settings > Account** immediately to set
up two-factor authentication with a new device. up two-factor authentication with a new device.
### Regenerate 2FA recovery codes ### Regenerate 2FA recovery codes
...@@ -443,8 +443,8 @@ To regenerate 2FA recovery codes, you need access to a desktop browser: ...@@ -443,8 +443,8 @@ To regenerate 2FA recovery codes, you need access to a desktop browser:
1. Navigate to GitLab. 1. Navigate to GitLab.
1. Sign in to your GitLab account. 1. Sign in to your GitLab account.
1. Go to your [**Profile settings**](../index.md#profile-settings). 1. Go to your [**User settings**](../index.md#user-settings).
1. Select **{account}** **Account > Two-Factor Authentication (2FA)**. 1. Select **Account > Two-Factor Authentication (2FA)**.
1. If you've already configured 2FA, click **Manage two-factor authentication**. 1. If you've already configured 2FA, click **Manage two-factor authentication**.
1. In the **Register Two-Factor Authenticator** pane, click **Regenerate recovery codes**. 1. In the **Register Two-Factor Authenticator** pane, click **Regenerate recovery codes**.
......
...@@ -14,9 +14,11 @@ review the sessions, and revoke any you don't recognize. ...@@ -14,9 +14,11 @@ review the sessions, and revoke any you don't recognize.
## Listing all active sessions ## Listing all active sessions
1. Click your avatar. To list all active sessions:
1. Select **Settings**.
1. Click **Active Sessions** in the sidebar. 1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Active Sessions**.
![Active sessions list](img/active_sessions_list.png) ![Active sessions list](img/active_sessions_list.png)
...@@ -29,8 +31,12 @@ exceeds 100, the oldest ones are deleted. ...@@ -29,8 +31,12 @@ exceeds 100, the oldest ones are deleted.
## Revoking a session ## Revoking a session
1. Use the previous steps to navigate to **Active Sessions**. To revoke an active session:
1. Click on **Revoke** besides a session. The current session cannot be revoked, as this would sign you out of GitLab.
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the left sidebar, select **Active Sessions**.
1. Select **Revoke** next to a session. The current session cannot be revoked, as this would sign you out of GitLab.
NOTE: NOTE:
When any session is revoked all **Remember me** tokens for all When any session is revoked all **Remember me** tokens for all
......
...@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# User account # User account
Each GitLab account has a user profile, and settings. Your [profile](#user-profile) Each GitLab account has a user profile, and settings. Your [profile](#user-profile)
contains information about you, and your GitLab activity. Your [settings](#profile-settings) contains information about you, and your GitLab activity. Your [settings](#user-settings)
allow you to customize some aspects of GitLab to suit yourself. allow you to customize some aspects of GitLab to suit yourself.
## Creating users ## Creating users
...@@ -29,8 +29,8 @@ See [Unknown Sign-In Notification](unknown_sign_in_notification.md) for more det ...@@ -29,8 +29,8 @@ See [Unknown Sign-In Notification](unknown_sign_in_notification.md) for more det
To access your profile: To access your profile:
1. Click on your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select your name or username.
On your profile page, you can see the following information: On your profile page, you can see the following information:
...@@ -42,12 +42,12 @@ On your profile page, you can see the following information: ...@@ -42,12 +42,12 @@ On your profile page, you can see the following information:
- Starred projects: projects you starred - Starred projects: projects you starred
- Snippets: your personal code [snippets](../snippets.md#personal-snippets) - Snippets: your personal code [snippets](../snippets.md#personal-snippets)
## Profile settings ## User settings
To access your profile settings: To access your user settings:
1. Click on your avatar. 1. In the top-right corner, select your avatar.
1. Select **Settings**. 1. Select **Edit profile**.
From there, you can: From there, you can:
...@@ -78,11 +78,12 @@ From there, you can: ...@@ -78,11 +78,12 @@ From there, you can:
## Changing your password ## Changing your password
1. Navigate to your [profile's](#profile-settings) **Settings > Password**. 1. Go to your [user settings](#user-settings).
1. Enter your current password in the 'Current password' field. 1. In the left sidebar, select **Password**.
1. Enter your desired new password twice, once in the 'New password' field and 1. Enter your current password in the **Current password** field.
once in the 'Password confirmation' field. 1. Enter your desired new password twice, once in the **New password** field and
1. Click the 'Save password' button. once in the **Password confirmation** field.
1. Select **Save password**.
If you don't know your current password, select the 'I forgot my password' link. If you don't know your current password, select the 'I forgot my password' link.
...@@ -97,12 +98,13 @@ before proceeding. ...@@ -97,12 +98,13 @@ before proceeding.
To change your `username`: To change your `username`:
1. Navigate to your [profile's](#profile-settings) **Settings > Account**. 1. Navigate to your [user settings](#user-settings).
1. In the left sidebar, select **Account**.
1. Enter a new username under **Change username**. 1. Enter a new username under **Change username**.
1. Click **Update username**. 1. Select **Update username**.
WARNING: WARNING:
It is currently not possible to change your username if it contains a It is not possible to change your username if it contains a
project with [Container Registry](../packages/container_registry/index.md) tags, project with [Container Registry](../packages/container_registry/index.md) tags,
because the project cannot be moved. because the project cannot be moved.
...@@ -127,33 +129,31 @@ The following information is hidden from the user profile page (`https://gitlab. ...@@ -127,33 +129,31 @@ The following information is hidden from the user profile page (`https://gitlab.
- Starred projects tab - Starred projects tab
- Snippets tab - Snippets tab
To enable private profile: To make your profile private:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (pencil icon). 1. Select the **Private profile** checkbox.
1. Check the **Private profile** option in the **Main settings** section. 1. Select **Update profile settings**.
1. Click **Update profile settings**.
NOTE: NOTE:
All your profile information can be seen by yourself, and GitLab admins, even if All your profile information can be seen by yourself and GitLab administrators even if
the **Private profile** option is enabled. the **Private profile** option is enabled.
## Add details of external accounts ## Add details of external accounts
GitLab allows you to add links to certain other external accounts you might have, like Skype and Twitter. They can help other users connect with you on other platforms. You can add links to certain other external accounts you might have, like Skype and Twitter.
They can help other users connect with you on other platforms.
To add links to other accounts: To add links to other accounts:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (pencil icon). 1. Edit the desired fields for external accounts:
1. Complete the desired fields for external accounts, in the **Main settings**
section:
- Skype - Skype
- Twitter
- LinkedIn - LinkedIn
1. Click **Update profile settings**. - Twitter
1. Select **Update profile settings**.
## Private contributions ## Private contributions
...@@ -163,11 +163,10 @@ Enabling private contributions includes contributions to private projects, in th ...@@ -163,11 +163,10 @@ Enabling private contributions includes contributions to private projects, in th
To enable private contributions: To enable private contributions:
1. Click on your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (pencil icon). 1. Select the **Private contributions** checkbox.
1. Check the **Private contributions** option. 1. Select **Update profile settings**.
1. Click **Update profile settings**.
## Current status ## Current status
...@@ -183,23 +182,23 @@ They may however contain emoji codes such as `I'm on vacation :palm_tree:`. ...@@ -183,23 +182,23 @@ They may however contain emoji codes such as `I'm on vacation :palm_tree:`.
To set your current status: To set your current status:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Click **Set status**, or **Edit status** if you have already set a status. 1. Select **Set status**, or **Edit status** if you have already set a status.
1. Set the desired emoji and/or status message. 1. Set the desired emoji and status message.
1. Click **Set status**. Alternatively, you can click **Remove status** to remove your user status entirely. 1. Select **Set status**. Alternatively, you can select **Remove status** to remove your user status entirely.
or or
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select your name or username.
1. Click **Edit profile** (pencil icon). 1. Select the Edit profile icon (**{pencil}**).
1. Enter your status message in the **Your status** text field. 1. Enter your status message in the **Your status** text field.
1. Click **Add status emoji** (smiley face), and select the desired emoji. 1. Select Add status emoji icon (**{slight-smile}**), and select the desired emoji.
1. Click **Update profile settings**. 1. Select **Update profile settings**.
You can also set your current status [using the API](../../api/users.md#user-status). You can also set your current status [using the API](../../api/users.md#user-status).
If you previously selected the "Busy" checkbox, remember to deselect it when you become available again. If you previously selected the **Busy** checkbox, remember to deselect it when you become available again.
## Busy status indicator ## Busy status indicator
...@@ -210,7 +209,7 @@ If you previously selected the "Busy" checkbox, remember to deselect it when you ...@@ -210,7 +209,7 @@ If you previously selected the "Busy" checkbox, remember to deselect it when you
> - It's not recommended for production use. > - It's not recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-busy-status-feature). > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-busy-status-feature).
To indicate to others that you are busy, you can set an indicator To indicate to others that you are busy, you can set an indicator.
![Busy status indicator](img/busy_status_indicator_v13_6.png) ![Busy status indicator](img/busy_status_indicator_v13_6.png)
...@@ -218,16 +217,15 @@ To set the busy status indicator, either: ...@@ -218,16 +217,15 @@ To set the busy status indicator, either:
- Set it directly: - Set it directly:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Click **Set status**, or **Edit status** if you have already set a status. 1. Select **Set status**, or **Edit status** if you have already set a status.
1. Select the **Busy** checkbox 1. Select the **Busy** checkbox.
- Set it on your profile: - Set it on your profile:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (**{pencil}**). 1. Select the **Busy** checkbox.
1. Select the **Busy** checkbox
### Disable busy status feature ### Disable busy status feature
...@@ -256,12 +254,11 @@ Any of your own verified email addresses can be used as the commit email. ...@@ -256,12 +254,11 @@ Any of your own verified email addresses can be used as the commit email.
To change your commit email: To change your commit email:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (pencil icon). 1. Select the **Commit email** dropdown.
1. Click **Commit email** dropdown.
1. Select any of the verified emails. 1. Select any of the verified emails.
1. Click **Update profile settings**. 1. Select **Update profile settings**.
### Private commit email ### Private commit email
...@@ -272,14 +269,13 @@ which allows the user to keep their email information private. ...@@ -272,14 +269,13 @@ which allows the user to keep their email information private.
To enable this option: To enable this option:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Profile**. 1. Select **Edit profile**.
1. Click **Edit profile** (pencil icon). 1. Select the **Commit email** dropdown.
1. Click **Commit email** dropdown.
1. Select **Use a private email** option. 1. Select **Use a private email** option.
1. Click **Update profile settings**. 1. Select **Update profile settings**.
Once this option is enabled, every Git-related action is performed using the private commit email. After this option is enabled, every Git-related action is performed using the private commit email.
To stay fully anonymous, you can also copy this private commit email To stay fully anonymous, you can also copy this private commit email
and configure it on your local machine using the following command: and configure it on your local machine using the following command:
...@@ -298,7 +294,7 @@ and expires after "Application settings -> Session duration (minutes)"/`session_ ...@@ -298,7 +294,7 @@ and expires after "Application settings -> Session duration (minutes)"/`session_
(defaults to `10080` minutes = 7 days) of no activity. (defaults to `10080` minutes = 7 days) of no activity.
When signing in to the main GitLab application, you can also check the When signing in to the main GitLab application, you can also check the
"Remember me" option which sets the `remember_user_token` **Remember me** option which sets the `remember_user_token`
cookie (via [`devise`](https://github.com/heartcombo/devise)). cookie (via [`devise`](https://github.com/heartcombo/devise)).
`remember_user_token` expires after `remember_user_token` expires after
`config/initializers/devise.rb` -> `config.remember_for` (defaults to 2 weeks). `config/initializers/devise.rb` -> `config.remember_for` (defaults to 2 weeks).
...@@ -326,7 +322,8 @@ GitLab uses both session and persistent cookies: ...@@ -326,7 +322,8 @@ GitLab uses both session and persistent cookies:
- Session cookie: Session cookies are normally removed at the end of the browser session when - Session cookie: Session cookies are normally removed at the end of the browser session when
the browser is closed. The `_gitlab_session` cookie has no fixed expiration date. However, the browser is closed. The `_gitlab_session` cookie has no fixed expiration date. However,
it expires based on its [`session_expire_delay`](#why-do-i-keep-getting-signed-out). it expires based on its [`session_expire_delay`](#why-do-i-keep-getting-signed-out).
- Persistent cookie: The `remember_user_token` is a cookie with an expiration date of two weeks. GitLab activates this cookie if you click Remember Me when you sign in. - Persistent cookie: The `remember_user_token` is a cookie with an expiration date of two weeks.
GitLab activates this cookie if you select **Remember Me** when you sign in.
By default, the server sets a time-to-live (TTL) of 1-week on any session that is used. By default, the server sets a time-to-live (TTL) of 1-week on any session that is used.
...@@ -336,15 +333,3 @@ The server continues to reset the TTL for that session, independent of whether 2 ...@@ -336,15 +333,3 @@ The server continues to reset the TTL for that session, independent of whether 2
If you close your browser and open it up again, the `remember_user_token` cookie allows your user to reauthenticate itself. If you close your browser and open it up again, the `remember_user_token` cookie allows your user to reauthenticate itself.
Without the `config.extend_remember_period` flag, you would be forced to sign in again after two weeks. Without the `config.extend_remember_period` flag, you would be forced to sign in again after two weeks.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
...@@ -33,13 +33,14 @@ You can create as many personal access tokens as you like from your GitLab ...@@ -33,13 +33,14 @@ You can create as many personal access tokens as you like from your GitLab
profile. profile.
1. Sign in to GitLab. 1. Sign in to GitLab.
1. In the upper-right corner, click your avatar and select **Settings**. 1. In the top-right corner, select your avatar.
1. On the **User Settings** menu, select **Access Tokens**. 1. Select **Edit profile**.
1. In the left sidebar, select **Access Tokens**.
1. Choose a name and optional expiry date for the token. 1. Choose a name and optional expiry date for the token.
1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token). 1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token).
1. Click the **Create personal access token** button. 1. Select **Create personal access token**.
1. Save the personal access token somewhere safe. If you navigate away or refresh 1. Save the personal access token somewhere safe. If you navigate away or refresh
your page, and you did not save the token, you must create a new one. your page, and you did not save the token, you must create a new one.
### Revoking a personal access token ### Revoking a personal access token
......
...@@ -12,9 +12,8 @@ of GitLab to their liking. ...@@ -12,9 +12,8 @@ of GitLab to their liking.
To navigate to your profile's preferences: To navigate to your profile's preferences:
1. Click your avatar. 1. In the top-right corner, select your avatar.
1. Select **Settings**. 1. Select **Preferences**.
1. Click **Preferences** in the sidebar.
## Navigation theme ## Navigation theme
...@@ -36,8 +35,7 @@ The default theme is Indigo. You can choose between 10 themes: ...@@ -36,8 +35,7 @@ The default theme is Indigo. You can choose between 10 themes:
- Light Red - Light Red
- Dark - Dark
- Light - Light
- [Dark Mode](#dark-mode)
![Profile preferences navigation themes](img/profil-preferences-navigation-theme.png)
## Dark mode ## Dark mode
...@@ -47,7 +45,8 @@ GitLab has started work on dark mode! The dark mode Alpha release is available i ...@@ -47,7 +45,8 @@ GitLab has started work on dark mode! The dark mode Alpha release is available i
spirit of iteration and the lower expectations of spirit of iteration and the lower expectations of
[Alpha versions](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha). [Alpha versions](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
Progress on dark mode is tracked in the [Dark theme epic](https://gitlab.com/groups/gitlab-org/-/epics/2902). See the epic for: Progress on dark mode is tracked in the [Dark theme epic](https://gitlab.com/groups/gitlab-org/-/epics/2902).
See the epic for:
- A list of known issues. - A list of known issues.
- Our planned direction and next steps. - Our planned direction and next steps.
...@@ -60,14 +59,15 @@ the future, we plan to make it configurable in its own section along with suppor ...@@ -60,14 +59,15 @@ the future, we plan to make it configurable in its own section along with suppor
[different navigation themes](https://gitlab.com/gitlab-org/gitlab/-/issues/219512). [different navigation themes](https://gitlab.com/gitlab-org/gitlab/-/issues/219512).
NOTE: NOTE:
Dark theme currently only works with the 'Dark' syntax highlighting. Dark theme only works with the **Dark** syntax highlighting theme.
## Syntax highlighting theme ## Syntax highlighting theme
NOTE: NOTE:
GitLab uses the [rouge Ruby library](http://rouge.jneen.net/ "Rouge website") GitLab uses the [rouge Ruby library](http://rouge.jneen.net/ "Rouge website")
for syntax highlighting outside of any Editor context. The WebIDE (like Snippets) for syntax highlighting outside of any Editor context. The WebIDE (like Snippets)
uses [Monaco Editor](https://microsoft.github.io/monaco-editor/) and it's provided [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) library for uses [Monaco Editor](https://microsoft.github.io/monaco-editor/) and it's provided
[Monarch](https://microsoft.github.io/monaco-editor/monarch.html) library for
syntax highlighting. For a list of supported languages, visit the documentation of syntax highlighting. For a list of supported languages, visit the documentation of
the respective libraries. the respective libraries.
...@@ -121,7 +121,7 @@ You have 8 options here that you can use for your default dashboard view: ...@@ -121,7 +121,7 @@ You have 8 options here that you can use for your default dashboard view:
- Your projects' activity - Your projects' activity
- Starred projects' activity - Starred projects' activity
- Your groups - Your groups
- Your [to-dos](../todos.md) - Your [To-Do List](../todos.md)
- Assigned Issues - Assigned Issues
- Assigned Merge Requests - Assigned Merge Requests
- Operations Dashboard **(PREMIUM)** - Operations Dashboard **(PREMIUM)**
......
...@@ -13,7 +13,7 @@ functionality to GitLab. ...@@ -13,7 +13,7 @@ functionality to GitLab.
## Accessing integrations ## Accessing integrations
You can find the available integrations under your project's You can find the available integrations under your project's
**Settings Integrations** page. **Settings > Integrations** page.
There are more than 20 integrations to integrate with. Click on the one that you There are more than 20 integrations to integrate with. Click on the one that you
want to configure. want to configure.
......
...@@ -201,7 +201,7 @@ issue's description are listed in the [issue history](#issue-history). **(PREMIU ...@@ -201,7 +201,7 @@ issue's description are listed in the [issue history](#issue-history). **(PREMIU
You can mention a user or a group present in your GitLab instance with `@username` or You can mention a user or a group present in your GitLab instance with `@username` or
`@groupname`. All mentioned users are notified via to-do items and emails, `@groupname`. All mentioned users are notified via to-do items and emails,
unless they have disabled all notifications in their profile settings. unless they have disabled all [notifications](#notifications) in their user settings.
This is controlled in the [notification settings](../../profile/notifications.md). This is controlled in the [notification settings](../../profile/notifications.md).
Mentions for yourself (the current logged in user) are highlighted Mentions for yourself (the current logged in user) are highlighted
...@@ -245,8 +245,8 @@ Also: ...@@ -245,8 +245,8 @@ Also:
- You can mention a user or a group present in your GitLab instance with - You can mention a user or a group present in your GitLab instance with
`@username` or `@groupname` and they are notified via to-do items `@username` or `@groupname` and they are notified via to-do items
and emails, unless they have [disabled all notifications](#notifications) and emails, unless they have disabled all [notifications](#notifications)
in their profile settings. in their user settings.
- Mentions for yourself (the current logged-in user) are highlighted - Mentions for yourself (the current logged-in user) are highlighted
in a different color, which allows you to quickly see which comments involve you. in a different color, which allows you to quickly see which comments involve you.
......
...@@ -82,11 +82,10 @@ Click **Expand file** on any file to view the changes for that file. ...@@ -82,11 +82,10 @@ Click **Expand file** on any file to view the changes for that file.
For larger merge requests, consider reviewing one file at a time. To enable this feature: For larger merge requests, consider reviewing one file at a time. To enable this feature:
1. In the top right corner of the navigation bar, click your user avatar. 1. In the top-right corner, select your avatar.
1. Click **Settings**. 1. Select **Preferences**.
1. In the left sidebar, go to **Preferences**.
1. Scroll to the **Behavior** section and select **Show one file at a time on merge request's Changes tab**. 1. Scroll to the **Behavior** section and select **Show one file at a time on merge request's Changes tab**.
1. Click **Save changes** to apply. 1. Select **Save changes**.
After you enable this setting, GitLab displays only one file at a time in the **Changes** tab when you review merge requests. You can click **Prev** and **Next** to view other changed files. After you enable this setting, GitLab displays only one file at a time in the **Changes** tab when you review merge requests. You can click **Prev** and **Next** to view other changed files.
...@@ -97,7 +96,7 @@ this behavior, you can do so from your **User preferences** (as explained above) ...@@ -97,7 +96,7 @@ this behavior, you can do so from your **User preferences** (as explained above)
merge request: merge request:
1. Go to the merge request's **Changes** tab. 1. Go to the merge request's **Changes** tab.
1. Click the cog icon (**{settings}**) to reveal the merge request's settings dropdown. 1. Select the cog icon (**{settings}**) to reveal the merge request's settings dropdown.
1. Select or deselect the checkbox **Show one file at a time** to change the setting accordingly. 1. Select or deselect the checkbox **Show one file at a time** to change the setting accordingly.
This change overrides the choice you made in your user preferences and persists until you clear your This change overrides the choice you made in your user preferences and persists until you clear your
...@@ -109,11 +108,11 @@ browser's cookies or change this behavior again. ...@@ -109,11 +108,11 @@ browser's cookies or change this behavior again.
To seamlessly navigate among commits in a merge request: To seamlessly navigate among commits in a merge request:
1. Click the **Commits** tab. 1. Select the **Commits** tab.
1. Click a commit to open it in the single-commit view. 1. Select a commit to open it in the single-commit view.
1. Navigate through the commits by either: 1. Navigate through the commits by either:
- Clicking **Prev** and **Next** buttons on the top-right of the page. - Selecting **Prev** and **Next** buttons on the top-right of the page.
- Using the <kbd>X</kbd> and <kbd>C</kbd> keyboard shortcuts. - Using the <kbd>X</kbd> and <kbd>C</kbd> keyboard shortcuts.
![Merge requests commit navigation](img/commit_nav_v13_4.png) ![Merge requests commit navigation](img/commit_nav_v13_4.png)
......
...@@ -138,27 +138,25 @@ started: ...@@ -138,27 +138,25 @@ started:
gpg --armor --export 30F2B65B9246B6CA gpg --armor --export 30F2B65B9246B6CA
``` ```
1. Finally, copy the public key and [add it in your profile settings](#adding-a-gpg-key-to-your-account) 1. Finally, copy the public key and [add it in your user settings](#adding-a-gpg-key-to-your-account)
## Adding a GPG key to your account ## Adding a GPG key to your account
NOTE: NOTE:
Once you add a key, you cannot edit it, only remove it. In case the paste After you add a key, you cannot edit it, only remove it. In case the paste
didn't work, you'll have to remove the offending key and re-add it. didn't work, you have to remove the offending key and re-add it.
You can add a GPG key in your profile's settings: You can add a GPG key in your user settings:
1. On the upper right corner, click on your avatar and go to your **Settings**. 1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
![Settings dropdown](../../../profile/img/profile_settings_dropdown.png) 1. In the left sidebar, select **GPG Keys**.
1. Paste your _public_ key in the **Key** text box.
1. Navigate to the **GPG keys** tab and paste your _public_ key in the 'Key'
box.
![Paste GPG public key](img/profile_settings_gpg_keys_paste_pub.png) ![Paste GPG public key](img/profile_settings_gpg_keys_paste_pub.png)
1. Finally, click on **Add key** to add it to GitLab. You will be able to see 1. Select **Add key** to add it to GitLab. You can see the key's fingerprint, the corresponding
its fingerprint, the corresponding email address and creation date. email address, and creation date.
![GPG key single page](img/profile_settings_gpg_keys_single_key.png) ![GPG key single page](img/profile_settings_gpg_keys_single_key.png)
...@@ -248,22 +246,24 @@ in case your key has been compromised. ...@@ -248,22 +246,24 @@ in case your key has been compromised.
To revoke a GPG key: To revoke a GPG key:
1. On the upper right corner, click on your avatar and go to your **Settings**. 1. In the top-right corner, select your avatar.
1. Navigate to the **GPG keys** tab. 1. Select **Edit profile**.
1. Click on **Revoke** besides the GPG key you want to delete. 1. In the left sidebar, select **GPG Keys**.
1. Select **Revoke** next to the GPG key you want to delete.
## Removing a GPG key ## Removing a GPG key
Removing a key **does not unverify** already signed commits. Commits that were Removing a key **does not unverify** already signed commits. Commits that were
verified by using this key will stay verified. Only unpushed commits will stay verified by using this key stay verified. Only unpushed commits stay
unverified once you remove this key. To unverify already signed commits, you need unverified after you remove this key. To unverify already signed commits, you need
to [revoke the associated GPG key](#revoking-a-gpg-key) from your account. to [revoke the associated GPG key](#revoking-a-gpg-key) from your account.
To remove a GPG key from your account: To remove a GPG key from your account:
1. On the upper right corner, click on your avatar and go to your **Settings**. 1. In the top-right corner, select your avatar.
1. Navigate to the **GPG keys** tab. 1. Select **Edit profile**.
1. Click on the trash icon besides the GPG key you want to delete. 1. In the left sidebar, select **GPG Keys**.
1. Select the trash icon (**{remove}**) next to the GPG key you want to delete.
## Rejecting commits that are not signed **(PREMIUM)** ## Rejecting commits that are not signed **(PREMIUM)**
......
...@@ -116,7 +116,7 @@ LDAP Users remain confirmed if all of the following conditions are met: ...@@ -116,7 +116,7 @@ LDAP Users remain confirmed if all of the following conditions are met:
- The ["User email confirmation at sign-up" option](../security/user_email_confirmation.md) is set to false. - The ["User email confirmation at sign-up" option](../security/user_email_confirmation.md) is set to false.
- The first sign-in is based on user LDAP credentials. - The first sign-in is based on user LDAP credentials.
- The user has added and verified [a secondary email address](profile/index.md#profile-settings) some time later. - The user has added and verified [a secondary email address](profile/index.md#user-settings) some time later.
NOTE: NOTE:
Confirmation timestamps (primary vs. secondary) are different. Confirmation timestamps (primary vs. secondary) are different.
...@@ -124,6 +124,6 @@ Confirmation timestamps (primary vs. secondary) are different. ...@@ -124,6 +124,6 @@ Confirmation timestamps (primary vs. secondary) are different.
Users remain unconfirmed by the background migration if any of the following conditions are met: Users remain unconfirmed by the background migration if any of the following conditions are met:
- They [create an account through GitLab](profile/account/create_accounts.md). - They [create an account through GitLab](profile/account/create_accounts.md).
- They [swap their primary email address](profile/index.md#profile-settings) and verify it. - They [swap their primary email address](profile/index.md#user-settings) and verify it.
- If they have two email addresses with the same `confirmed_at` timestamp due to the linked [security issue](https://gitlab.com/gitlab-org/gitlab/-/issues/121664). - If they have two email addresses with the same `confirmed_at` timestamp due to the linked [security issue](https://gitlab.com/gitlab-org/gitlab/-/issues/121664).
- [LDAP is introduced](../administration/auth/ldap/index.md), and users' primary email address matches that in LDAP. - [LDAP is introduced](../administration/auth/ldap/index.md), and users' primary email address matches that in LDAP.
...@@ -65,27 +65,8 @@ module Projects ...@@ -65,27 +65,8 @@ module Projects
end end
def issue_json def issue_json
{ ::Integrations::Jira::IssueDetailSerializer.new
title_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira', .represent(project.jira_service.find_issue(params[:id], { expand: 'renderedFields' }), project: project)
description_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
created_at: 2.hours.ago,
author: {
id: 2,
username: 'justin_ho',
name: 'Justin Ho',
web_url: 'http://127.0.0.1:3000/root',
avatar_url: 'http://127.0.0.1:3000/uploads/-/system/user/avatar/1/avatar.png?width=90'
},
labels: [
{
title: 'In Progress',
description: 'Work that is still in progress',
color: '#EBECF0',
text_color: '#283856'
}
],
state: 'opened'
}
end end
def finder def finder
......
# frozen_string_literal: true
module Integrations
module Jira
class IssueDetailEntity < ::Integrations::Jira::IssueEntity
expose :description_html do |jira_issue|
Banzai::Pipeline::GfmPipeline.call(jira_issue.renderedFields['description'], project: nil)[:output].to_html
end
expose :state do |jira_issue|
jira_issue.resolutiondate ? 'closed' : 'opened'
end
end
end
end
# frozen_string_literal: true
module Integrations
module Jira
class IssueDetailSerializer < BaseSerializer
entity ::Integrations::Jira::IssueDetailEntity
end
end
end
...@@ -33,7 +33,6 @@ module Integrations ...@@ -33,7 +33,6 @@ module Integrations
name: name, name: name,
color: '#EBECF0', color: '#EBECF0',
text_color: '#283856' text_color: '#283856'
} }
end end
end end
...@@ -41,7 +40,8 @@ module Integrations ...@@ -41,7 +40,8 @@ module Integrations
expose :author do |jira_issue| expose :author do |jira_issue|
{ {
name: jira_issue.reporter.displayName, name: jira_issue.reporter.displayName,
web_url: author_web_url(jira_issue) web_url: author_web_url(jira_issue),
avatar_url: jira_issue.reporter.avatarUrls['48x48']
} }
end end
......
...@@ -195,6 +195,8 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do ...@@ -195,6 +195,8 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do
end end
context 'when `jira_issues_show_integration` feature is enabled' do context 'when `jira_issues_show_integration` feature is enabled' do
let(:jira_issue) { {} }
before do before do
stub_feature_flags(jira_issues_show_integration: true) stub_feature_flags(jira_issues_show_integration: true)
end end
...@@ -207,15 +209,15 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do ...@@ -207,15 +209,15 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do
end end
it 'returns JSON response' do it 'returns JSON response' do
get :show, params: { namespace_id: project.namespace, project_id: project, id: 1, format: :json } expect_next_found_instance_of(JiraService) do |service|
expect(service).to receive(:find_issue).with('1', { expand: 'renderedFields' }).and_return(jira_issue)
end
expect(response).to have_gitlab_http_status(:ok) expect_next_instance_of(Integrations::Jira::IssueDetailSerializer) do |serializer|
expect(json_response).to include( expect(serializer).to receive(:represent).with(jira_issue, project: project)
'title_html', end
'description_html',
'author', get :show, params: { namespace_id: project.namespace, project_id: project, id: 1, format: :json }
'labels'
)
end end
end end
end end
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Audit Events', :js do RSpec.describe 'Projects > Audit Events', :js do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:pete) { create(:user, name: 'Pete') } let(:pete) { create(:user, name: 'Pete') }
let(:project) { create(:project, :repository, namespace: user.namespace) } let(:project) { create(:project, :repository, namespace: user.namespace) }
before do before do
stub_feature_flags(vue_project_members_list: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end end
...@@ -109,6 +109,33 @@ RSpec.describe 'Projects > Audit Events', :js do ...@@ -109,6 +109,33 @@ RSpec.describe 'Projects > Audit Events', :js do
project.add_developer(pete) project.add_developer(pete)
end end
context 'when `vue_project_members_list` feature flag is enabled' do
it "appears in the project's audit events" do
visit project_project_members_path(project)
page.within find_member_row(pete) do
click_button 'Developer'
click_button 'Maintainer'
end
page.within('.qa-project-sidebar') do
find(:link, text: 'Security & Compliance').click
click_link 'Audit Events'
end
page.within('.audit-log-table') do
expect(page).to have_content 'Changed access level from Developer to Maintainer'
expect(page).to have_content(project.owner.name)
expect(page).to have_content('Pete')
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it "appears in the project's audit events" do it "appears in the project's audit events" do
visit project_project_members_path(project) visit project_project_members_path(project)
...@@ -131,6 +158,7 @@ RSpec.describe 'Projects > Audit Events', :js do ...@@ -131,6 +158,7 @@ RSpec.describe 'Projects > Audit Events', :js do
end end
end end
end end
end
describe 'changing merge request approval permission for authors and reviewers' do describe 'changing merge request approval permission for authors and reviewers' do
before do before do
......
...@@ -5,12 +5,12 @@ require 'spec_helper' ...@@ -5,12 +5,12 @@ require 'spec_helper'
RSpec.describe 'Project > Members > Invite group and members', :js do RSpec.describe 'Project > Members > Invite group and members', :js do
include Select2Helper include Select2Helper
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user) } let(:maintainer) { create(:user) }
before do before do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: false)
stub_feature_flags(vue_project_members_list: false)
end end
describe 'Share group lock' do describe 'Share group lock' do
...@@ -71,7 +71,30 @@ RSpec.describe 'Project > Members > Invite group and members', :js do ...@@ -71,7 +71,30 @@ RSpec.describe 'Project > Members > Invite group and members', :js do
context 'when the group has "Share with group lock" and "Member lock" disabled' do context 'when the group has "Share with group lock" and "Member lock" disabled' do
it_behaves_like 'the project can be shared with groups and members' it_behaves_like 'the project can be shared with groups and members'
it 'the project can be shared with another group' do context 'when `vue_project_members_list` feature flag is enabled' do
it 'allows the project to be shared with another group' do
visit project_project_members_path(project)
click_on 'invite-group-tab'
select2 group_to_share_with.id, from: '#link_group_id'
page.find('body').click
find('.btn-success').click
click_link 'Groups'
page.within(members_table) do
expect(page).to have_content(group_to_share_with.name)
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'allows the project to be shared with another group' do
visit project_project_members_path(project) visit project_project_members_path(project)
click_on 'invite-group-tab' click_on 'invite-group-tab'
...@@ -87,6 +110,7 @@ RSpec.describe 'Project > Members > Invite group and members', :js do ...@@ -87,6 +110,7 @@ RSpec.describe 'Project > Members > Invite group and members', :js do
end end
end end
end end
end
context 'when the group has "Share with group lock" enabled' do context 'when the group has "Share with group lock" enabled' do
before do before do
......
...@@ -8,10 +8,50 @@ RSpec.describe 'Projects > Members > Member is removed from project' do ...@@ -8,10 +8,50 @@ RSpec.describe 'Projects > Members > Member is removed from project' do
let(:other_user) { create(:user) } let(:other_user) { create(:user) }
before do before do
stub_feature_flags(vue_project_members_list: false)
project.add_maintainer(user) project.add_maintainer(user)
project.add_maintainer(other_user) project.add_maintainer(other_user)
end
context 'when `vue_project_members_list` feature flag is enabled', :js do
before do
sign_in(user)
visit project_project_members_path(project)
end
it 'user is removed from project' do
click_button 'Leave'
page.within('[role="dialog"]') do
click_button('Leave')
end
expect(project.users.exists?(user.id)).to be_falsey
end
context 'when the user has been specifically allowed to access a protected branch' do
let!(:matching_protected_branch) { create(:protected_branch, authorize_user_to_push: user, authorize_user_to_merge: user, project: project) }
let!(:non_matching_protected_branch) { create(:protected_branch, authorize_user_to_push: other_user, authorize_user_to_merge: other_user, project: project) }
it 'user leaves project' do
click_button 'Leave'
page.within('[role="dialog"]') do
click_button('Leave')
end
expect(project.users.exists?(user.id)).to be_falsey
expect(matching_protected_branch.push_access_levels.where(user: user)).not_to exist
expect(matching_protected_branch.merge_access_levels.where(user: user)).not_to exist
expect(non_matching_protected_branch.push_access_levels.where(user: other_user)).to exist
expect(non_matching_protected_branch.merge_access_levels.where(user: other_user)).to exist
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
sign_in(user) sign_in(user)
visit project_project_members_path(project) visit project_project_members_path(project)
end end
...@@ -36,4 +76,5 @@ RSpec.describe 'Projects > Members > Member is removed from project' do ...@@ -36,4 +76,5 @@ RSpec.describe 'Projects > Members > Member is removed from project' do
expect(non_matching_protected_branch.merge_access_levels.where(user: other_user)).to exist expect(non_matching_protected_branch.merge_access_levels.where(user: other_user)).to exist
end end
end end
end
end end
...@@ -20,7 +20,7 @@ RSpec.describe Resolvers::ExternalIssueResolver do ...@@ -20,7 +20,7 @@ RSpec.describe Resolvers::ExternalIssueResolver do
status: double(name: 'To Do'), status: double(name: 'To Do'),
key: 'GV-1', key: 'GV-1',
labels: [], labels: [],
reporter: double(displayName: 'User', accountId: '10000'), reporter: double(displayName: 'User', accountId: '10000', avatarUrls: { '48x48' => 'http://reporter.avatar' }),
assignee: nil, assignee: nil,
client: double(options: { site: 'http://jira.com/' }) client: double(options: { site: 'http://jira.com/' })
) )
...@@ -37,7 +37,8 @@ RSpec.describe Resolvers::ExternalIssueResolver do ...@@ -37,7 +37,8 @@ RSpec.describe Resolvers::ExternalIssueResolver do
'labels' => [], 'labels' => [],
'author' => { 'author' => {
'name' => 'User', 'name' => 'User',
'web_url' => 'http://jira.com/people/10000' 'web_url' => 'http://jira.com/people/10000',
'avatar_url' => 'http://reporter.avatar'
}, },
'assignees' => [], 'assignees' => [],
'web_url' => 'http://jira.com/browse/GV-1', 'web_url' => 'http://jira.com/browse/GV-1',
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::Jira::IssueDetailEntity do
let(:project) { build(:project) }
let(:jira_client) { double(options: { site: 'http://jira.com/' }) }
let(:reporter) do
double(
'displayName' => 'reporter',
'avatarUrls' => { '48x48' => 'http://reporter.avatar' },
'name' => double # Default to Jira Server issue response, Jira Cloud replaces name with accountId,
)
end
let(:jira_issue) do
double(
renderedFields: { 'description' => '<p>Description</p>' },
summary: 'Title',
created: '2020-06-25T15:39:30.000+0000',
updated: '2020-06-26T15:38:32.000+0000',
resolutiondate: '2020-06-27T13:23:51.000+0000',
labels: ['backend'],
reporter: reporter,
assignee: double('displayName' => 'assignee'),
project: double(key: 'GL'),
key: 'GL-5',
client: jira_client,
status: double(name: 'To Do')
)
end
subject { described_class.new(jira_issue, project: project).as_json }
it 'returns the Jira issues attributes' do
expect(subject).to include(
project_id: project.id,
title: 'Title',
description_html: "<p dir=\"auto\">Description</p>",
created_at: '2020-06-25T15:39:30.000+0000'.to_datetime.utc,
updated_at: '2020-06-26T15:38:32.000+0000'.to_datetime.utc,
closed_at: '2020-06-27T13:23:51.000+0000'.to_datetime.utc,
status: 'To Do',
state: 'closed',
labels: [
{
name: 'backend',
color: '#EBECF0',
text_color: '#283856'
}
],
author: hash_including(
name: 'reporter',
avatar_url: 'http://reporter.avatar'
),
assignees: [
{ name: 'assignee' }
],
web_url: 'http://jira.com/browse/GL-5',
references: { relative: 'GL-5' },
external_tracker: 'jira'
)
end
context 'with Jira Server configuration' do
before do
allow(reporter).to receive(:name).and_return('reporter@reporter.com')
end
it 'returns the Jira Server profile URL' do
expect(subject[:author]).to include(web_url: 'http://jira.com/secure/ViewProfile.jspa?name=reporter@reporter.com')
end
context 'and context_path' do
let(:jira_client) { double(options: { site: 'http://jira.com/', context_path: '/jira-sub-path' }) }
it 'returns URLs including context path' do
expect(subject[:author]).to include(web_url: 'http://jira.com/jira-sub-path/secure/ViewProfile.jspa?name=reporter@reporter.com')
expect(subject[:web_url]).to eq('http://jira.com/jira-sub-path/browse/GL-5')
end
end
end
context 'with Jira Cloud configuration' do
before do
allow(reporter).to receive(:accountId).and_return('12345')
end
it 'returns the Jira Cloud profile URL' do
expect(subject[:author]).to include(web_url: 'http://jira.com/people/12345')
end
end
context 'without assignee' do
before do
allow(jira_issue).to receive(:assignee).and_return(nil)
end
it 'returns an empty array' do
expect(subject).to include(assignees: [])
end
end
context 'without labels' do
before do
allow(jira_issue).to receive(:labels).and_return([])
end
it 'returns an empty array' do
expect(subject).to include(labels: [])
end
end
context 'without resolution date' do
before do
allow(jira_issue).to receive(:resolutiondate).and_return(nil)
end
it "returns 'Open' state" do
expect(subject).to include(state: 'opened')
end
end
end
...@@ -8,6 +8,7 @@ RSpec.describe Integrations::Jira::IssueEntity do ...@@ -8,6 +8,7 @@ RSpec.describe Integrations::Jira::IssueEntity do
let(:reporter) do let(:reporter) do
double( double(
'displayName' => 'reporter', 'displayName' => 'reporter',
'avatarUrls' => { '48x48' => 'http://reporter.avatar' },
'name' => double # Default to Jira Server issue response, Jira Cloud replaces name with accountId 'name' => double # Default to Jira Server issue response, Jira Cloud replaces name with accountId
) )
end end
...@@ -47,7 +48,10 @@ RSpec.describe Integrations::Jira::IssueEntity do ...@@ -47,7 +48,10 @@ RSpec.describe Integrations::Jira::IssueEntity do
text_color: '#283856' text_color: '#283856'
} }
], ],
author: hash_including(name: 'reporter'), author: hash_including(
name: 'reporter',
avatar_url: 'http://reporter.avatar'
),
assignees: [ assignees: [
{ name: 'assignee' } { name: 'assignee' }
], ],
......
...@@ -8664,13 +8664,13 @@ msgstr "" ...@@ -8664,13 +8664,13 @@ msgstr ""
msgid "CurrentUser|Buy Pipeline minutes" msgid "CurrentUser|Buy Pipeline minutes"
msgstr "" msgstr ""
msgid "CurrentUser|One of your groups is running out" msgid "CurrentUser|Edit profile"
msgstr "" msgstr ""
msgid "CurrentUser|Profile" msgid "CurrentUser|One of your groups is running out"
msgstr "" msgstr ""
msgid "CurrentUser|Settings" msgid "CurrentUser|Preferences"
msgstr "" msgstr ""
msgid "CurrentUser|Start an Ultimate trial" msgid "CurrentUser|Start an Ultimate trial"
......
...@@ -6,7 +6,7 @@ module QA ...@@ -6,7 +6,7 @@ module QA
class Menu < Page::Base class Menu < Page::Base
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :sign_out_link element :sign_out_link
element :settings_link element :edit_profile_link
end end
view 'app/views/layouts/header/_default.html.haml' do view 'app/views/layouts/header/_default.html.haml' do
...@@ -115,10 +115,10 @@ module QA ...@@ -115,10 +115,10 @@ module QA
sign_out if signed_in? sign_out if signed_in?
end end
def click_settings_link def click_edit_profile_link
retry_until(reload: false) do retry_until(reload: false) do
within_user_menu do within_user_menu do
click_link 'Settings' click_element(:edit_profile_link)
end end
has_text?('User Settings') has_text?('User Settings')
......
...@@ -15,7 +15,7 @@ module QA ...@@ -15,7 +15,7 @@ module QA
end end
def fabricate! def fabricate!
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_access_tokens) Page::Profile::Menu.perform(&:click_access_tokens)
Page::Profile::PersonalAccessTokens.perform do |token_page| Page::Profile::PersonalAccessTokens.perform do |token_page|
......
...@@ -22,7 +22,7 @@ module QA ...@@ -22,7 +22,7 @@ module QA
end end
def fabricate! def fabricate!
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_ssh_keys) Page::Profile::Menu.perform(&:click_ssh_keys)
Page::Profile::SSHKeys.perform do |profile_page| Page::Profile::SSHKeys.perform do |profile_page|
......
...@@ -48,7 +48,7 @@ module QA ...@@ -48,7 +48,7 @@ module QA
def enable_2fa_for_user(user) def enable_2fa_for_user(user)
Flow::Login.while_signed_in(as: user) do Flow::Login.while_signed_in(as: user) do
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_account) Page::Profile::Menu.perform(&:click_account)
Page::Profile::Accounts::Show.perform(&:click_enable_2fa_button) Page::Profile::Accounts::Show.perform(&:click_enable_2fa_button)
......
...@@ -58,7 +58,7 @@ module QA ...@@ -58,7 +58,7 @@ module QA
# this is the only test that exercise this UI. # this is the only test that exercise this UI.
# Other tests should use the API for this purpose. # Other tests should use the API for this purpose.
Flow::Login.sign_in(as: user) Flow::Login.sign_in(as: user)
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_account) Page::Profile::Menu.perform(&:click_account)
Page::Profile::Accounts::Show.perform do |show| Page::Profile::Accounts::Show.perform do |show|
show.delete_account(user.password) show.delete_account(user.password)
......
...@@ -21,7 +21,7 @@ module QA ...@@ -21,7 +21,7 @@ module QA
# Note this context ensures that the example it contains is executed after the example above. Be aware of the order of execution if you add new examples in either context. # Note this context ensures that the example it contains is executed after the example above. Be aware of the order of execution if you add new examples in either context.
context 'after adding an ssh key' do context 'after adding an ssh key' do
it 'can delete an ssh key', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/930' do it 'can delete an ssh key', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/930' do
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_ssh_keys) Page::Profile::Menu.perform(&:click_ssh_keys)
Page::Profile::SSHKeys.perform do |ssh_keys| Page::Profile::SSHKeys.perform do |ssh_keys|
ssh_keys.remove_key(key.title) ssh_keys.remove_key(key.title)
......
...@@ -65,7 +65,7 @@ module QA ...@@ -65,7 +65,7 @@ module QA
before do before do
sign_in sign_in
new_email_address = 'new_email@example.com' new_email_address = 'new_email@example.com'
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_emails) Page::Profile::Menu.perform(&:click_emails)
Support::Retrier.retry_until(sleep_interval: 3) do Support::Retrier.retry_until(sleep_interval: 3) do
Page::Profile::Emails.perform do |emails| Page::Profile::Emails.perform do |emails|
...@@ -91,7 +91,7 @@ module QA ...@@ -91,7 +91,7 @@ module QA
login_page.sign_in_using_credentials(user: user) login_page.sign_in_using_credentials(user: user)
end end
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_password) Page::Profile::Menu.perform(&:click_password)
Page::Profile::Password.perform do |password_page| Page::Profile::Password.perform do |password_page|
password_page.update_password('new_password', user.password) password_page.update_password('new_password', user.password)
......
...@@ -27,7 +27,7 @@ module QA ...@@ -27,7 +27,7 @@ module QA
Runtime::Browser.visit(ENV['GITLAB_ADDRESS'], Page::Main::Login) Runtime::Browser.visit(ENV['GITLAB_ADDRESS'], Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials) Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:click_settings_link) Page::Main::Menu.perform(&:click_edit_profile_link)
Page::Profile::Menu.perform(&:click_access_tokens) Page::Profile::Menu.perform(&:click_access_tokens)
token_name = 'api-test-token' token_name = 'api-test-token'
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe "Admin::Projects" do RSpec.describe "Admin::Projects" do
include Spec::Support::Helpers::Features::MembersHelpers
include Select2Helper include Select2Helper
let(:user) { create :user } let(:user) { create :user }
...@@ -10,8 +11,6 @@ RSpec.describe "Admin::Projects" do ...@@ -10,8 +11,6 @@ RSpec.describe "Admin::Projects" do
let(:current_user) { create(:admin) } let(:current_user) { create(:admin) }
before do before do
stub_feature_flags(vue_project_members_list: false)
sign_in(current_user) sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user)
end end
...@@ -93,13 +92,63 @@ RSpec.describe "Admin::Projects" do ...@@ -93,13 +92,63 @@ RSpec.describe "Admin::Projects" do
end end
end end
describe 'add admin himself to a project' do context 'when `vue_project_members_list` feature flag is enabled', :js do
describe 'admin adds themselves to the project' do
before do before do
project.add_maintainer(user) project.add_maintainer(user)
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: false)
end end
it 'adds admin a to a project as developer', :js do it 'adds admin to the project as developer', :js do
visit project_project_members_path(project)
page.within '.invite-users-form' do
select2(current_user.id, from: '#user_ids', multiple: true)
select 'Developer', from: 'access_level'
end
click_button 'Invite'
expect(find_member_row(current_user)).to have_content('Developer')
end
end
describe 'admin removes themselves from the project' do
before do
project.add_maintainer(user)
project.add_developer(current_user)
end
it 'removes admin from the project' do
visit project_project_members_path(project)
expect(find_member_row(current_user)).to have_content('Developer')
page.within find_member_row(current_user) do
click_button 'Leave'
end
page.within('[role="dialog"]') do
click_button('Leave')
end
expect(current_path).to match dashboard_projects_path
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
describe 'admin adds themselves to the project' do
before do
project.add_maintainer(user)
stub_feature_flags(invite_members_group_modal: false)
end
it 'adds admin to the project as developer', :js do
visit project_project_members_path(project) visit project_project_members_path(project)
page.within '.invite-users-form' do page.within '.invite-users-form' do
...@@ -116,7 +165,7 @@ RSpec.describe "Admin::Projects" do ...@@ -116,7 +165,7 @@ RSpec.describe "Admin::Projects" do
end end
end end
describe 'admin remove himself from a project' do describe 'admin removes themselves from the project' do
before do before do
project.add_maintainer(user) project.add_maintainer(user)
project.add_developer(current_user) project.add_developer(current_user)
...@@ -135,4 +184,5 @@ RSpec.describe "Admin::Projects" do ...@@ -135,4 +184,5 @@ RSpec.describe "Admin::Projects" do
expect(page).not_to have_selector(:css, '.content-list') expect(page).not_to have_selector(:css, '.content-list')
end end
end end
end
end end
...@@ -171,7 +171,7 @@ RSpec.describe 'Admin::Users::User' do ...@@ -171,7 +171,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs in as the user when impersonate is clicked' do it 'logs in as the user when impersonate is clicked' do
subject subject
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username) expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username)
end end
it 'sees impersonation log out icon' do it 'sees impersonation log out icon' do
...@@ -205,7 +205,7 @@ RSpec.describe 'Admin::Users::User' do ...@@ -205,7 +205,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs out of impersonated user back to original user' do it 'logs out of impersonated user back to original user' do
subject subject
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username) expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username)
end end
it 'is redirected back to the impersonated users page in the admin after stopping' do it 'is redirected back to the impersonated users page in the admin after stopping' do
......
...@@ -39,7 +39,7 @@ RSpec.describe 'User visits their profile' do ...@@ -39,7 +39,7 @@ RSpec.describe 'User visits their profile' do
find(:css, '.header-user-dropdown-toggle').click find(:css, '.header-user-dropdown-toggle').click
page.within ".header-user" do page.within ".header-user" do
click_link "Profile" click_link user.username
end end
end end
......
...@@ -3,17 +3,30 @@ ...@@ -3,17 +3,30 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Members > Anonymous user sees members' do RSpec.describe 'Projects > Members > Anonymous user sees members' do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
before do before do
stub_feature_flags(vue_project_members_list: false)
project.add_maintainer(user) project.add_maintainer(user)
create(:project_group_link, project: project, group: group) create(:project_group_link, project: project, group: group)
end end
context 'when `vue_project_members_list` feature flag is enabled', :js do
it "anonymous user visits the project's members page and sees the list of members" do
visit project_project_members_path(project)
expect(find_member_row(user)).to have_content(user.name)
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it "anonymous user visits the project's members page and sees the list of members" do it "anonymous user visits the project's members page and sees the list of members" do
visit project_project_members_path(project) visit project_project_members_path(project)
...@@ -21,4 +34,5 @@ RSpec.describe 'Projects > Members > Anonymous user sees members' do ...@@ -21,4 +34,5 @@ RSpec.describe 'Projects > Members > Anonymous user sees members' do
project_project_members_path(project)) project_project_members_path(project))
expect(page).to have_content(user.name) expect(page).to have_content(user.name)
end end
end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects members', :js do RSpec.describe 'Projects members', :js do
include Spec::Support::Helpers::Features::MembersHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:developer) { create(:user) } let(:developer) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
...@@ -13,13 +15,111 @@ RSpec.describe 'Projects members', :js do ...@@ -13,13 +15,111 @@ RSpec.describe 'Projects members', :js do
let(:group_requester) { create(:user) } let(:group_requester) { create(:user) }
before do before do
stub_feature_flags(vue_project_members_list: false)
project.add_developer(developer) project.add_developer(developer)
group.add_owner(user) group.add_owner(user)
sign_in(user) sign_in(user)
end end
context 'when `vue_project_members_list` feature flag is enabled' do
context 'with a group invitee' do
before do
group_invitee
visit project_project_members_path(project)
end
it 'does not appear in the project members page' do
expect(members_table).not_to have_content('test2@abc.com')
end
end
context 'with a group' do
it 'shows group and project members by default' do
visit project_project_members_path(project)
expect(members_table).to have_content(developer.name)
expect(members_table).to have_content(user.name)
expect(members_table).to have_content(group.name)
end
it 'shows project members only if requested' do
visit project_project_members_path(project, with_inherited_permissions: 'exclude')
expect(members_table).to have_content(developer.name)
expect(members_table).not_to have_content(user.name)
expect(members_table).not_to have_content(group.name)
end
it 'shows group members only if requested' do
visit project_project_members_path(project, with_inherited_permissions: 'only')
expect(members_table).not_to have_content(developer.name)
expect(members_table).to have_content(user.name)
expect(members_table).to have_content(group.name)
end
end
context 'with a group, a project invitee, and a project requester' do
before do
group.request_access(group_requester)
project.request_access(project_requester)
group_invitee
project_invitee
visit project_project_members_path(project)
end
it 'shows the group owner' do
expect(members_table).to have_content(user.name)
expect(members_table).to have_content(group.name)
end
it 'shows the project developer' do
expect(members_table).to have_content(developer.name)
end
it 'shows the project invitee' do
click_link 'Invited'
expect(members_table).to have_content('test1@abc.com')
expect(members_table).not_to have_content('test2@abc.com')
end
it 'shows the project requester' do
click_link 'Access requests'
expect(members_table).to have_content(project_requester.name)
expect(members_table).not_to have_content(group_requester.name)
end
end
context 'with a group requester' do
before do
stub_feature_flags(invite_members_group_modal: false)
group.request_access(group_requester)
visit project_project_members_path(project)
end
it 'does not appear in the project members page' do
expect(page).not_to have_link('Access requests')
expect(members_table).not_to have_content(group_requester.name)
end
end
context 'showing status of members' do
it 'shows the status' do
create(:user_status, user: user, emoji: 'smirk', message: 'Authoring this object')
visit project_project_members_path(project)
expect(first_row).to have_selector('gl-emoji[data-name="smirk"]')
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
context 'with a group invitee' do context 'with a group invitee' do
before do before do
group_invitee group_invitee
...@@ -126,11 +226,12 @@ RSpec.describe 'Projects members', :js do ...@@ -126,11 +226,12 @@ RSpec.describe 'Projects members', :js do
end end
end end
describe 'showing status of members' do context 'showing status of members' do
it_behaves_like 'showing user status' do it_behaves_like 'showing user status' do
let(:user_with_status) { developer } let(:user_with_status) { developer }
subject { visit project_project_members_path(project) } subject { visit project_project_members_path(project) }
end end
end end
end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Members > Groups with access list', :js do RSpec.describe 'Projects > Members > Groups with access list', :js do
include Spec::Support::Helpers::Features::MembersHelpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) } let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
...@@ -11,12 +13,95 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do ...@@ -11,12 +13,95 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
let!(:group_link) { create(:project_group_link, project: project, group: group, **additional_link_attrs) } let!(:group_link) { create(:project_group_link, project: project, group: group, **additional_link_attrs) }
before do before do
stub_feature_flags(vue_project_members_list: false)
travel_to Time.now.utc.beginning_of_day travel_to Time.now.utc.beginning_of_day
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end
context 'when `vue_project_members_list` feature flag is enabled' do
before do
visit project_project_members_path(project)
click_groups_tab
end
it 'updates group access level' do
click_button group_link.human_access
click_button 'Guest'
wait_for_requests
visit project_project_members_path(project)
click_groups_tab
expect(find_group_row(group)).to have_content('Guest')
end
it 'updates expiry date' do
page.within find_group_row(group) do
fill_in 'Expiration date', with: 5.days.from_now.to_date
find_field('Expiration date').native.send_keys :enter
wait_for_requests
expect(page).to have_content(/in \d days/)
end
end
context 'when link has expiry date set' do
let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } }
it 'clears expiry date' do
page.within find_group_row(group) do
expect(page).to have_content(/in \d days/)
find('[data-testid="clear-button"]').click
wait_for_requests
expect(page).to have_content('No expiration set')
end
end
end
it 'deletes group link' do
expect(page).to have_content(group.full_name)
page.within find_group_row(group) do
click_button 'Remove group'
end
page.within('[role="dialog"]') do
click_button('Remove group')
end
expect(page).not_to have_content(group.full_name)
end
context 'search in existing members' do
it 'finds no results' do
fill_in_filtered_search 'Search groups', with: 'testing 123'
click_groups_tab
expect(page).not_to have_content(group.full_name)
end
it 'finds results' do
fill_in_filtered_search 'Search groups', with: group.full_name
click_groups_tab
expect(members_table).to have_content(group.full_name)
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
visit project_project_members_path(project) visit project_project_members_path(project)
click_groups_tab click_groups_tab
end end
...@@ -99,6 +184,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do ...@@ -99,6 +184,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
expect(page).to have_selector('.group_member', count: 1) expect(page).to have_selector('.group_member', count: 1)
end end
end end
end
def click_groups_tab def click_groups_tab
click_link 'Groups' click_link 'Groups'
......
...@@ -5,12 +5,12 @@ require 'spec_helper' ...@@ -5,12 +5,12 @@ require 'spec_helper'
RSpec.describe 'Project > Members > Invite group', :js do RSpec.describe 'Project > Members > Invite group', :js do
include Select2Helper include Select2Helper
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user) } let(:maintainer) { create(:user) }
before do before do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: false)
stub_feature_flags(vue_project_members_list: false)
end end
describe 'Share with group lock' do describe 'Share with group lock' do
...@@ -41,6 +41,29 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -41,6 +41,29 @@ RSpec.describe 'Project > Members > Invite group', :js do
context 'when the group has "Share with group lock" disabled' do context 'when the group has "Share with group lock" disabled' do
it_behaves_like 'the project can be shared with groups' it_behaves_like 'the project can be shared with groups'
context 'when `vue_project_members_list` feature flag is enabled' do
it 'the project can be shared with another group' do
visit project_project_members_path(project)
expect(page).not_to have_link 'Groups'
click_on 'invite-group-tab'
select2 group_to_share_with.id, from: '#link_group_id'
page.find('body').click
find('.btn-success').click
click_link 'Groups'
expect(members_table).to have_content(group_to_share_with.name)
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'the project can be shared with another group' do it 'the project can be shared with another group' do
visit project_project_members_path(project) visit project_project_members_path(project)
...@@ -59,6 +82,7 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -59,6 +82,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
end end
end end
end end
end
context 'when the group has "Share with group lock" enabled' do context 'when the group has "Share with group lock" enabled' do
before do before do
...@@ -122,7 +146,7 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -122,7 +146,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
freeze_time { example.run } freeze_time { example.run }
end end
before do def setup
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
group.add_guest(maintainer) group.add_guest(maintainer)
sign_in(maintainer) sign_in(maintainer)
...@@ -133,12 +157,28 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -133,12 +157,28 @@ RSpec.describe 'Project > Members > Invite group', :js do
select2 group.id, from: '#link_group_id' select2 group.id, from: '#link_group_id'
fill_in 'expires_at_groups', with: (Time.now + 4.5.days).strftime('%Y-%m-%d') fill_in 'expires_at_groups', with: 5.days.from_now.strftime('%Y-%m-%d')
click_on 'invite-group-tab' click_on 'invite-group-tab'
find('.btn-success').click find('.btn-success').click
end end
context 'when `vue_project_members_list` feature flag is enabled' do
it 'the group link shows the expiration time with a warning class' do it 'the group link shows the expiration time with a warning class' do
setup
click_link 'Groups'
expect(find_group_row(group)).to have_content(/in \d days/)
expect(find_group_row(group)).to have_selector('.gl-text-orange-500')
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'the group link shows the expiration time with a warning class' do
setup
click_link 'Groups' click_link 'Groups'
page.within('[data-testid="project-member-groups"]') do page.within('[data-testid="project-member-groups"]') do
...@@ -150,6 +190,7 @@ RSpec.describe 'Project > Members > Invite group', :js do ...@@ -150,6 +190,7 @@ RSpec.describe 'Project > Members > Invite group', :js do
end end
end end
end end
end
describe 'the groups dropdown' do describe 'the groups dropdown' do
context 'with multiple groups to choose from' do context 'with multiple groups to choose from' do
......
...@@ -4,7 +4,6 @@ require 'spec_helper' ...@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Project members list' do RSpec.describe 'Project members list' do
include Select2Helper include Select2Helper
include Spec::Support::Helpers::Features::ListRowsHelpers
let(:user1) { create(:user, name: 'John Doe') } let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') } let(:user2) { create(:user, name: 'Mary Jane') }
...@@ -13,16 +12,118 @@ RSpec.describe 'Project members list' do ...@@ -13,16 +12,118 @@ RSpec.describe 'Project members list' do
before do before do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: false)
stub_feature_flags(vue_project_members_list: false)
sign_in(user1) sign_in(user1)
group.add_owner(user1) group.add_owner(user1)
end end
context 'when `vue_project_members_list` feature flag is enabled', :js do
include Spec::Support::Helpers::Features::MembersHelpers
it 'pushes `vue_project_members_list` feature flag to the frontend' do it 'pushes `vue_project_members_list` feature flag to the frontend' do
visit_members_page visit_members_page
expect(page).to have_pushed_frontend_feature_flags(vueProjectMembersList: false) expect(page).to have_pushed_frontend_feature_flags(vueProjectMembersList: true)
end
it 'show members from project and group' do
project.add_developer(user2)
visit_members_page
expect(first_row).to have_content(user1.name)
expect(second_row).to have_content(user2.name)
end
it 'show user once if member of both group and project' do
project.add_developer(user1)
visit_members_page
expect(first_row).to have_content(user1.name)
expect(second_row).to be_blank
end
it 'update user access level', :js do
project.add_developer(user2)
visit_members_page
page.within find_member_row(user2) do
click_button('Developer')
click_button('Reporter')
expect(page).to have_button('Reporter')
end
end
it 'add user to project', :js do
visit_members_page
add_user(user2.id, 'Reporter')
page.within find_member_row(user2) do
expect(page).to have_button('Reporter')
end
end
it 'remove user from project', :js do
other_user = create(:user)
project.add_developer(other_user)
visit_members_page
# Open modal
page.within find_member_row(other_user) do
click_button 'Remove member'
end
page.within('[role="dialog"]') do
expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
click_button('Remove member')
end
wait_for_requests
expect(members_table).not_to have_content(other_user.name)
end
it 'invite user to project', :js do
visit_members_page
add_user('test@example.com', 'Reporter')
click_link 'Invited'
page.within find_invited_member_row('test@example.com') do
expect(page).to have_button('Reporter')
end
end
context 'project bots' do
let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
before do
project.add_maintainer(project_bot)
end
it 'does not show form used to change roles and "Expiration date" or the remove user button' do
visit_members_page
page.within find_member_row(project_bot) do
expect(page).not_to have_button('Maintainer')
expect(page).to have_field('Expiration date', disabled: true)
expect(page).not_to have_button('Remove member')
end
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
include Spec::Support::Helpers::Features::ListRowsHelpers
before do
stub_feature_flags(vue_project_members_list: false)
end end
it 'show members from project and group' do it 'show members from project and group' do
...@@ -116,6 +217,9 @@ RSpec.describe 'Project members list' do ...@@ -116,6 +217,9 @@ RSpec.describe 'Project members list' do
expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger") expect(page).to have_no_selector("#project_member_#{project_member.id} .btn-danger")
end end
end end
end
private
def add_user(id, role) def add_user(id, role)
page.within ".invite-users-form" do page.within ".invite-users-form" do
......
...@@ -5,20 +5,74 @@ require 'spec_helper' ...@@ -5,20 +5,74 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js do RSpec.describe 'Projects > Members > Maintainer adds member with expiration date', :js do
include Select2Helper include Select2Helper
include ActiveSupport::Testing::TimeHelpers include ActiveSupport::Testing::TimeHelpers
include Spec::Support::Helpers::Features::MembersHelpers
let_it_be(:maintainer) { create(:user) } let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:new_member) { create(:user) } let(:new_member) { create(:user) }
before do before do
stub_feature_flags(vue_project_members_list: false)
travel_to Time.now.utc.beginning_of_day travel_to Time.now.utc.beginning_of_day
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
sign_in(maintainer) sign_in(maintainer)
end end
context 'when `vue_project_members_list` feature flag is enabled' do
it 'expiration date is displayed in the members list' do
stub_feature_flags(invite_members_group_modal: false)
visit project_project_members_path(project)
page.within '.invite-users-form' do
select2(new_member.id, from: '#user_ids', multiple: true)
fill_in 'expires_at', with: 5.days.from_now.to_date
find_field('expires_at').native.send_keys :enter
click_on 'Invite'
end
page.within find_member_row(new_member) do
expect(page).to have_content(/in \d days/)
end
end
it 'changes expiration date' do
project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
visit project_project_members_path(project)
page.within find_member_row(new_member) do
fill_in 'Expiration date', with: 5.days.from_now.to_date
find_field('Expiration date').native.send_keys :enter
wait_for_requests
expect(page).to have_content(/in \d days/)
end
end
it 'clears expiration date' do
project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date)
visit project_project_members_path(project)
page.within find_member_row(new_member) do
expect(page).to have_content(/in \d days/)
find('[data-testid="clear-button"]').click
wait_for_requests
expect(page).to have_content('No expiration set')
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'expiration date is displayed in the members list' do it 'expiration date is displayed in the members list' do
stub_feature_flags(invite_members_group_modal: false) stub_feature_flags(invite_members_group_modal: false)
...@@ -39,7 +93,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date ...@@ -39,7 +93,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
end end
it 'changes expiration date' do it 'changes expiration date' do
project.team.add_users([new_member.id], :developer, expires_at: Date.today.to_date) project.team.add_users([new_member.id], :developer, expires_at: 1.day.from_now.to_date)
visit project_project_members_path(project) visit project_project_members_path(project)
page.within "#project_member_#{project_member_id}" do page.within "#project_member_#{project_member_id}" do
...@@ -66,6 +120,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date ...@@ -66,6 +120,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
expect(page).not_to have_content('Expires in') expect(page).not_to have_content('Expires in')
end end
end end
end
def project_member_id def project_member_id
project.members.find_by(user_id: new_member).id project.members.find_by(user_id: new_member).id
......
...@@ -3,18 +3,106 @@ ...@@ -3,18 +3,106 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Members > Sorting' do RSpec.describe 'Projects > Members > Sorting' do
include Spec::Support::Helpers::Features::MembersHelpers
let(:maintainer) { create(:user, name: 'John Doe') } let(:maintainer) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) } let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) }
before do before do
stub_feature_flags(vue_project_members_list: false)
create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago) create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago)
sign_in(maintainer) sign_in(maintainer)
end end
context 'when `vue_project_members_list` feature flag is enabled', :js do
it 'sorts by account by default' do
visit_members_list(sort: nil)
expect(first_row).to have_content(maintainer.name)
expect(second_row).to have_content(developer.name)
expect_sort_by('Account', :asc)
end
it 'sorts by max role ascending' do
visit_members_list(sort: :access_level_asc)
expect(first_row).to have_content(developer.name)
expect(second_row).to have_content(maintainer.name)
expect_sort_by('Max role', :asc)
end
it 'sorts by max role descending' do
visit_members_list(sort: :access_level_desc)
expect(first_row).to have_content(maintainer.name)
expect(second_row).to have_content(developer.name)
expect_sort_by('Max role', :desc)
end
it 'sorts by access granted ascending' do
visit_members_list(sort: :last_joined)
expect(first_row).to have_content(maintainer.name)
expect(second_row).to have_content(developer.name)
expect_sort_by('Access granted', :asc)
end
it 'sorts by access granted descending' do
visit_members_list(sort: :oldest_joined)
expect(first_row).to have_content(developer.name)
expect(second_row).to have_content(maintainer.name)
expect_sort_by('Access granted', :desc)
end
it 'sorts by account ascending' do
visit_members_list(sort: :name_asc)
expect(first_row).to have_content(maintainer.name)
expect(second_row).to have_content(developer.name)
expect_sort_by('Account', :asc)
end
it 'sorts by account descending' do
visit_members_list(sort: :name_desc)
expect(first_row).to have_content(developer.name)
expect(second_row).to have_content(maintainer.name)
expect_sort_by('Account', :desc)
end
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
expect(first_row).to have_content(maintainer.name)
expect(second_row).to have_content(developer.name)
expect_sort_by('Last sign-in', :asc)
end
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
expect(first_row).to have_content(developer.name)
expect(second_row).to have_content(maintainer.name)
expect_sort_by('Last sign-in', :desc)
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'sorts alphabetically by default' do it 'sorts alphabetically by default' do
visit_members_list(sort: nil) visit_members_list(sort: nil)
...@@ -86,6 +174,9 @@ RSpec.describe 'Projects > Members > Sorting' do ...@@ -86,6 +174,9 @@ RSpec.describe 'Projects > Members > Sorting' do
expect(second_member).to include(maintainer.name) expect(second_member).to include(maintainer.name)
expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in') expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end end
end
private
def visit_members_list(sort:) def visit_members_list(sort:)
visit project_project_members_path(project, sort: sort) visit project_project_members_path(project, sort: sort)
...@@ -98,4 +189,11 @@ RSpec.describe 'Projects > Members > Sorting' do ...@@ -98,4 +189,11 @@ RSpec.describe 'Projects > Members > Sorting' do
def second_member def second_member
page.all('ul.content-list > li').last.text page.all('ul.content-list > li').last.text
end end
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
expect(page).to have_css('button[aria-haspopup="true"]', text: text)
expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
end end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Members > Tabs' do RSpec.describe 'Projects > Members > Tabs' do
include Spec::Support::Helpers::Features::MembersHelpers
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
...@@ -19,10 +20,8 @@ RSpec.describe 'Projects > Members > Tabs' do ...@@ -19,10 +20,8 @@ RSpec.describe 'Projects > Members > Tabs' do
end end
end end
context 'tabs' do
before do before do
stub_feature_flags(vue_project_members_list: false)
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
sign_in(user) sign_in(user)
visit project_project_members_path(project) visit project_project_members_path(project)
end end
...@@ -43,6 +42,44 @@ RSpec.describe 'Projects > Members > Tabs' do ...@@ -43,6 +42,44 @@ RSpec.describe 'Projects > Members > Tabs' do
context 'displays "Members" tab by default' do context 'displays "Members" tab by default' do
it_behaves_like 'active "Members" tab' it_behaves_like 'active "Members" tab'
end end
end
context 'when `vue_project_members_list` feature flag is enabled' do
before do
sign_in(user)
visit project_project_members_path(project)
end
context 'when searching "Groups"', :js do
before do
click_link 'Groups'
fill_in_filtered_search 'Search groups', with: 'group'
end
it 'displays "Groups" tab' do
expect(page).to have_selector('.nav-link.active', text: 'Groups')
end
context 'and then searching "Members"' do
before do
click_link 'Members 3'
fill_in_filtered_search 'Filter members', with: 'user'
end
it_behaves_like 'active "Members" tab'
end
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
sign_in(user)
visit project_project_members_path(project)
end
context 'when searching "Groups"', :js do context 'when searching "Groups"', :js do
before do before do
...@@ -71,4 +108,5 @@ RSpec.describe 'Projects > Members > Tabs' do ...@@ -71,4 +108,5 @@ RSpec.describe 'Projects > Members > Tabs' do
it_behaves_like 'active "Members" tab' it_behaves_like 'active "Members" tab'
end end
end end
end
end end
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Settings > User manages project members' do RSpec.describe 'Projects > Settings > User manages project members' do
include Spec::Support::Helpers::Features::MembersHelpers
include Select2Helper
let(:group) { create(:group, name: 'OpenSource') } let(:group) { create(:group, name: 'OpenSource') }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:project2) { create(:project) } let(:project2) { create(:project) }
...@@ -11,13 +14,69 @@ RSpec.describe 'Projects > Settings > User manages project members' do ...@@ -11,13 +14,69 @@ RSpec.describe 'Projects > Settings > User manages project members' do
let(:user_mike) { create(:user, name: 'Mike') } let(:user_mike) { create(:user, name: 'Mike') }
before do before do
stub_feature_flags(vue_project_members_list: false)
project.add_maintainer(user) project.add_maintainer(user)
project.add_developer(user_dmitriy) project.add_developer(user_dmitriy)
sign_in(user) sign_in(user)
end end
context 'when `vue_project_members_list` feature flag is enabled' do
it 'cancels a team member', :js do
visit(project_project_members_path(project))
page.within find_member_row(user_dmitriy) do
click_button 'Remove member'
end
page.within('[role="dialog"]') do
expect(page).to have_unchecked_field 'Also unassign this user from related issues and merge requests'
click_button('Remove member')
end
visit(project_project_members_path(project))
expect(members_table).not_to have_content(user_dmitriy.name)
expect(members_table).not_to have_content(user_dmitriy.username)
end
it 'imports a team from another project', :js do
stub_feature_flags(invite_members_group_modal: false)
project2.add_maintainer(user)
project2.add_reporter(user_mike)
visit(project_project_members_path(project))
page.within('.invite-users-form') do
click_link('Import')
end
select2(project2.id, from: '#source_project_id')
click_button('Import project members')
expect(find_member_row(user_mike)).to have_content('Reporter')
end
it 'shows all members of project shared group', :js do
group.add_owner(user)
group.add_developer(user_dmitriy)
share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
share_link.group_id = group.id
share_link.save!
visit(project_project_members_path(project))
click_link 'Groups'
expect(find_group_row(group)).to have_content('Maintainer')
end
end
context 'when `vue_project_members_list` feature flag is disabled' do
before do
stub_feature_flags(vue_project_members_list: false)
end
it 'cancels a team member', :js do it 'cancels a team member', :js do
visit(project_project_members_path(project)) visit(project_project_members_path(project))
...@@ -78,4 +137,5 @@ RSpec.describe 'Projects > Settings > User manages project members' do ...@@ -78,4 +137,5 @@ RSpec.describe 'Projects > Settings > User manages project members' do
expect(first('.group_member')).to have_content('Maintainer') expect(first('.group_member')).to have_content('Maintainer')
end end
end end
end
end end
...@@ -458,6 +458,16 @@ RSpec.describe JiraService do ...@@ -458,6 +458,16 @@ RSpec.describe JiraService do
expect(WebMock).to have_requested(:get, issue_url) expect(WebMock).to have_requested(:get, issue_url)
end end
context 'with options' do
let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields" }
it 'calls the Jira API with the options to get the issue' do
jira_service.find_issue(issue_key, { expand: 'renderedFields' })
expect(WebMock).to have_requested(:get, issue_url)
end
end
end end
describe '#close_issue' do describe '#close_issue' do
......
...@@ -30,6 +30,32 @@ module Spec ...@@ -30,6 +30,32 @@ module Spec
def invite_users_form def invite_users_form
page.find('[data-testid="invite-users-form"]') page.find('[data-testid="invite-users-form"]')
end end
def find_row(name)
page.within(members_table) do
page.find('tbody > tr', text: name)
end
end
def find_member_row(user)
find_row(user.name)
end
def find_invited_member_row(email)
find_row(email)
end
def find_group_row(group)
find_row(group.full_name)
end
def fill_in_filtered_search(label, with:)
page.within '[data-testid="members-filtered-search-bar"]' do
find_field(label).click
find('input').native.send_keys(with)
click_button 'Search'
end
end
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment