diff --git a/doc/api/README.md b/doc/api/README.md index 35eeb5ae99b..7332600c972 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -125,7 +125,7 @@ There are several methods you can use to authenticate with the GitLab API: - [Personal access tokens](../user/profile/personal_access_tokens.md) - [Project access tokens](../user/project/settings/project_access_tokens.md) - [Session cookie](#session-cookie) -- [GitLab CI/CD job token](#gitlab-ci-job-token) **(Specific endpoints only)** +- [GitLab CI/CD job token](#gitlab-cicd-job-token) **(Specific endpoints only)** NOTE: Project access tokens are supported for self-managed instances on Free and @@ -198,22 +198,50 @@ The primary user of this authentication method is the web frontend of GitLab itself, which can, for example, use the API as the authenticated user to get a list of their projects without needing to explicitly pass an access token. -### GitLab CI job token +### GitLab CI/CD job token -With a few API endpoints you can use a [GitLab CI/CD job token](../user/project/new_ci_build_permissions_model.md#job-token) -to authenticate with the API: +When a pipeline job is about to run, GitLab generates a unique token and injects it as the +[`CI_JOB_TOKEN` predefined variable](../ci/variables/predefined_variables.md). + +You can use a GitLab CI/CD job token to authenticate with specific API endpoints: - Packages: - - [Package Registry](../user/packages/package_registry/index.md) + - [Package Registry](../user/packages/package_registry/index.md). To push to the + Package Registry, you can use [deploy tokens](../user/project/deploy_tokens/index.md). - [Container Registry](../user/packages/container_registry/index.md) - (`$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`) -- [Get job artifacts](job_artifacts.md#get-job-artifacts) -- [Get job token's job](jobs.md#get-job-tokens-job) -- [Pipeline triggers](pipeline_triggers.md) (using the `token=` parameter) -- [Release creation](releases/index.md#create-a-release) -- [Terraform plan](../user/infrastructure/index.md) + (the `$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`). +- [Get job artifacts](job_artifacts.md#get-job-artifacts). +- [Get job token's job](jobs.md#get-job-tokens-job). +- [Pipeline triggers](pipeline_triggers.md), using the `token=` parameter. +- [Release creation](releases/index.md#create-a-release). +- [Terraform plan](../user/infrastructure/index.md). -The token is valid as long as the job is running. +The token has the same permissions to access the API as the user that triggers the +pipeline. Therefore, this user must be assigned to [a role that has the required privileges](../user/permissions.md). + +The token is valid only while the pipeline job runs. After the job finishes, you can't +use the token anymore. + +A job token can access a project's resources without any configuration, but it might +give extra permissions that aren't necessary. There is [a proposal](https://gitlab.com/groups/gitlab-org/-/epics/3559) +to redesign the feature for more strategic control of the access permissions. + +#### GitLab CI/CD job token security + +To make sure that this token doesn't leak, GitLab: + +- Masks the job token in job logs. +- Grants permissions to the job token only when the job is running. + +To make sure that this token doesn't leak, you should also configure +your [runners](../ci/runners/README.md) to be secure. Avoid: + +- Using Docker's `privileged` mode if the machines are re-used. +- Using the [`shell` executor](https://docs.gitlab.com/runner/executors/shell.html) when jobs + run on the same machine. + +If you have an insecure GitLab Runner configuration, you increase the risk that someone +tries to steal tokens from other jobs. ### Impersonation tokens diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 9d598c43ef0..95410b3d2d8 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -59,7 +59,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu | `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job. | | `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the job's stage. | | `CI_JOB_STATUS` | all | 13.5 | The status of the job as each runner stage is executed. Use with [`after_script`](../yaml/README.md#after_script). Can be `success`, `failed`, or `canceled`. | -| `CI_JOB_TOKEN` | 9.0 | 1.2 | A token to authenticate with [certain API endpoints](../../api/README.md#gitlab-ci-job-token) or download [dependent repositories](../../user/project/new_ci_build_permissions_model.md#dependent-repositories). The token is valid as long as the job is running. | +| `CI_JOB_TOKEN` | 9.0 | 1.2 | A token to authenticate with [certain API endpoints](../../api/README.md#gitlab-cicd-job-token). The token is valid as long as the job is running. | | `CI_JOB_URL` | 11.1 | 0.5 | The job details URL. | | `CI_JOB_STARTED_AT` | 13.10 | all | The UTC datetime when a job started, in [ISO 8601](https://tools.ietf.org/html/rfc3339#appendix-A) format. | | `CI_KUBERNETES_ACTIVE` | 13.0 | all | Only available if the pipeline has a Kubernetes cluster available for deployments. `true` when available. | diff --git a/doc/development/packages.md b/doc/development/packages.md index e8c326da974..d4982473d67 100644 --- a/doc/development/packages.md +++ b/doc/development/packages.md @@ -133,7 +133,7 @@ During this phase, the idea is to collect as much information as possible about - **Authentication**: What authentication mechanisms are available (OAuth, Basic Authorization, other). Keep in mind that GitLab users often want to use their [Personal Access Tokens](../user/profile/personal_access_tokens.md). - Although not needed for the MVC first iteration, the [CI job tokens](../user/project/new_ci_build_permissions_model.md#job-token) + Although not needed for the MVC first iteration, the [CI/CD job tokens](../api/README.md#gitlab-cicd-job-token) have to be supported at some point in the future. - **Requests**: Which requests are needed to have a working MVC. Ideally, produce a list of all the requests needed for the MVC (including required actions). Further diff --git a/doc/security/token_overview.md b/doc/security/token_overview.md index 2bb4ffa8eec..4cdfefb8063 100644 --- a/doc/security/token_overview.md +++ b/doc/security/token_overview.md @@ -71,7 +71,7 @@ You can use the runner registration token to add runners that execute jobs in a After registration, the runner receives an authentication token, which it uses to authenticate with GitLab when picking up jobs from the job queue. The authentication token is stored locally in the runner's [`config.toml`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html) file. -After authentication with GitLab, the runner receives a [job token](../user/project/new_ci_build_permissions_model.md#job-token), which it uses to execute the job. +After authentication with GitLab, the runner receives a [job token](../api/README.md#gitlab-cicd-job-token), which it uses to execute the job. In case of Docker Machine/Kubernetes/VirtualBox/Parallels/SSH executors, the execution environment has no access to the runner authentication token, because it stays on the runner machine. They have access to the job token only, which is needed to execute the job. @@ -79,9 +79,9 @@ Malicious access to a runner's file system may expose the `config.toml` file and ## CI/CD job tokens -The [CI/CD](../api/README.md#gitlab-ci-job-token) job token +The [CI/CD](../api/README.md#gitlab-cicd-job-token) job token is a short lived token only valid for the duration of a job. It gives a CI/CD job -access to a limited amount of [API endpoints](../api/README.md#gitlab-ci-job-token). +access to a limited amount of API endpoints. API authentication uses the job token, by using the authorization of the user triggering the job. @@ -105,4 +105,4 @@ This table shows available scopes per token. Scopes can be limited further on to 1. Limited to the one project. 1. Runner registration and authentication token don't provide direct access to repositories, but can be used to register and authenticate a new runner that may execute jobs which do have access to the repository -1. Limited to certain [endpoints](../api/README.md#gitlab-ci-job-token). +1. Limited to certain [endpoints](../api/README.md#gitlab-cicd-job-token). diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md index b9186e99357..a72128e6034 100644 --- a/doc/user/packages/generic_packages/index.md +++ b/doc/user/packages/generic_packages/index.md @@ -21,13 +21,13 @@ Publish generic files, like release binaries, in your project’s Package Regist ## Authenticate to the Package Registry To authenticate to the Package Registry, you need either a [personal access token](../../../api/README.md#personalproject-access-tokens), -[CI job token](../../../api/README.md#gitlab-ci-job-token), or [deploy token](../../project/deploy_tokens/index.md). +[CI/CD job token](../../../api/README.md#gitlab-cicd-job-token), or [deploy token](../../project/deploy_tokens/index.md). In addition to the standard API authentication mechanisms, the generic package API allows authentication with HTTP Basic authentication for use with tools that do not support the other available mechanisms. The `user-id` is not checked and may be any value, and the `password` must be either a [personal access token](../../../api/README.md#personalproject-access-tokens), -a [CI job token](../../../api/README.md#gitlab-ci-job-token), or a [deploy token](../../project/deploy_tokens/index.md). +a [CI/CD job token](../../../api/README.md#gitlab-cicd-job-token), or a [deploy token](../../project/deploy_tokens/index.md). ## Publish a package file diff --git a/doc/user/permissions.md b/doc/user/permissions.md index bde589661f9..aad1d7edfda 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -482,10 +482,6 @@ instance and project. In addition, all admins can use the admin interface under NOTE: In GitLab 11.0, the Master role was renamed to Maintainer. -NOTE: -GitLab 8.12 has a completely redesigned job permissions system. -Read all about the [new model and its implications](project/new_ci_build_permissions_model.md). - This table shows granted privileges for jobs triggered by specific types of users: @@ -507,11 +503,6 @@ users: 1. Only if the user is not an external one 1. Only if the user is a member of the project -### New CI job permissions model - -GitLab 8.12 has a completely redesigned job permissions system. To learn more, -read through the documentation on the [new CI/CD permissions model](project/new_ci_build_permissions_model.md#new-ci-job-permissions-model). - ## Running pipelines on protected branches The permission to merge or push to protected branches is used to define if a user can diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index 585d97c74c2..f7e8d3d140c 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -1,256 +1,8 @@ --- -stage: Verify -group: Continuous Integration -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -type: reference +redirect_to: '../../ci/README.md' --- -# New CI job permissions model +This document is deprecated. See the latest [GitLab CI/CD documentation](../../ci/README.md). -> Introduced in GitLab 8.12. - -GitLab 8.12 has a completely redesigned [job permissions](../permissions.md#job-permissions) system. You can find -all discussion and all our concerns when choosing the current approach in issue -[#18994](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/18994). - -Jobs permissions should be tightly integrated with the permissions of a user -who is triggering a job. - -The reasons to do it like that are: - -- We already have a permissions system in place: group and project membership - of users. -- We already fully know who is triggering a job (using `git push`, using the - web UI, executing triggers). -- We already know what user is allowed to do. -- We use the user permissions for jobs that are triggered by the user. -- It opens a lot of possibilities to further enforce user permissions, like - allowing only specific users to access runners or use secure variables and - environments. -- It is simple and convenient that your job can access everything that you - as a user have access to. -- Short living unique tokens are now used, granting access for time of the job - and maximizing security. - -With the new behavior, any job that is triggered by the user, is also marked -with their read permissions. When a user does a `git push` or changes files through -the web UI, a new pipeline is usually created. This pipeline is marked -as created by the pusher (local push or via the UI) and any job created in this -pipeline has the read permissions of the pusher but not write permissions. - -This allows us to make it really easy to evaluate the access for all projects -that have [Git submodules](../../ci/git_submodules.md) or are using container images that the pusher -would have access too. **The permission is granted only for the time that the job -is running. The access is revoked after the job is finished.** - -## Types of users - -It is important to note that we have a few types of users: - -- **Administrators**: CI jobs created by Administrators don't have access - to all GitLab projects, but only to projects and container images of projects - that the administrator is a member of. That means that if a project is either - public or internal users have access anyway, but if a project is private, the - Administrator has to be a member of it in order to have access to it - via another project's job. - -- **External users**: CI jobs created by [external users](../permissions.md#external-users) have - access only to projects to which the user has at least Reporter access. This - rules out accessing all internal projects by default. - -This allows us to make the CI and permission system more trustworthy. -Let's consider the following scenario: - -1. You are an employee of a company. Your company has a number of internal tools - hosted in private repositories and you have multiple CI jobs that make use - of these repositories. - -1. You invite a new [external user](../permissions.md#external-users). CI jobs created by that user do not - have access to internal repositories, because the user also doesn't have the - access from within GitLab. You as an employee have to grant explicit access - for this user. This allows us to prevent from accidental data leakage. - -## Job token - -When a pipeline job is about to run, GitLab generates a unique token and injects it as the -[`CI_JOB_TOKEN` predefined variable](../../ci/variables/predefined_variables.md). -This token can authenticate [API requests](../../api/README.md) -from the job script (Runner) that needs to access the project's resources (for example, when -fetching a job artifact). - -Once the token is authenticated, GitLab identifies the user who triggered the job and uses this user -to authorize access to the resource. Therefore, this user must be assigned to -[a role that has the required privileges](../permissions.md). - -The job token has these limitations: - -- Not all APIs allow job tokens for authentication. See [this list](../../api/README.md#gitlab-ci-job-token) - for available endpoints. -- The token is valid only while the pipeline job runs. Once the job finishes, the token can't be - used for authentication. - -Although a job token is handy to quickly access a project's resources without any configuration, it -sometimes gives extra permissions that aren't necessary. There is [a proposal](https://gitlab.com/groups/gitlab-org/-/epics/3559) -to redesign the feature for more strategic control of the access permissions. - -If you need your CI pipeline to push to the Package Registry, consider using [deploy tokens](deploy_tokens/index.md). - -We try to make sure that this token doesn't leak by: - -1. Securing all API endpoints to not expose the job token. -1. Masking the job token from job logs. -1. Granting permissions to the job token **only** when the job is running. - -However, this brings up a question about the runner's security. To make sure that -this token doesn't leak, you should also make sure that you configure -your runners in the most possible secure way, by avoiding the following: - -1. Any usage of Docker's `privileged` mode is risky if the machines are re-used. -1. Using the `shell` executor since jobs run on the same machine. - -By using an insecure GitLab Runner configuration, you allow the rogue developers -to steal the tokens of other jobs. - -## Before GitLab 8.12 - -In versions before GitLab 8.12, all CI jobs would use the runner's token -to checkout project sources. - -The project's runner token was a token that you could find under the -project's **Settings > Pipelines** and was limited to access only that -project. -It could be used for registering new specific runners assigned to the project -and to checkout project sources. -It could also be used with the GitLab Container Registry for that project, -allowing pulling and pushing Docker images from within the CI job. - -GitLab would create a special checkout URL like: - -```plaintext -https://gitlab-ci-token:/gitlab.com/gitlab-org/gitlab-foss.git -``` - -And then the users could also use it in their CI jobs all Docker related -commands to interact with GitLab Container Registry. For example: - -```shell -docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com -``` - -Using single token had multiple security implications: - -- The token would be readable to anyone who had Developer access to a project - that could run CI jobs, allowing the developer to register any specific - runner for that project. -- The token would allow to access only the project's sources, forbidding from - accessing any other projects. -- The token was not expiring and was multi-purpose: used for checking out sources, - for registering specific runners and for accessing a project's container - registry with read-write permissions. - -All the above led to a new permission model for jobs that was introduced -with GitLab 8.12. - -## Making use of the new CI job permissions model - -With the new job permissions model, there is now an easy way to access all -dependent source code in a project. That way, we can: - -1. Access a project's dependent repositories -1. Access a project's [Git submodules](../../ci/git_submodules.md) -1. Access private container images -1. Access project's and submodule LFS objects - -Below you can see the prerequisites needed to make use of the new permissions -model and how that works with Git submodules and private Docker images hosted on -the container registry. - -### Prerequisites to use the new permissions model - -With the new permissions model in place, there may be times that your job -fails. This is most likely because your project tries to access other project's -sources, and you don't have the appropriate permissions. In the job log look -for information about 403 or forbidden access messages. - -In short here's what you need to do should you encounter any issues. - -As an administrator: - -- **500 errors**: You need to update [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) to at - least 0.8.2. This is done automatically for Omnibus installations, you need to - [check manually](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/doc/update) for installations from source. -- **500 errors**: Check if you have another web proxy sitting in front of NGINX (HAProxy, - Apache, etc.). It might be a good idea to let GitLab use the internal NGINX - web server and not disable it completely. See [this comment](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/22484#note_16648302) for an - example. -- **403 errors**: You need to make sure that your installation has [HTTP(S) - cloning enabled](../admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols). HTTP(S) support is now a **requirement** by GitLab CI - to clone all sources. - -As a user: - -- Make sure you are a member of the group or project you're trying to have - access to. As an Administrator, you can verify that by impersonating the user - and retry the failing job in order to verify that everything is correct. - -### Dependent repositories - -The [CI/CD variable](../../ci/variables/README.md#predefined-cicd-variables) `CI_JOB_TOKEN` can be used to -authenticate any clones of dependent repositories. For example: - -```shell -git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com//.git -``` - -It can also be used for system-wide authentication -(only do this in a Docker container, it overwrites `~/.netrc`): - -```shell -echo -e "machine gitlab.com\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc -``` - -### Git submodules - -To properly configure submodules with GitLab CI/CD, read the -[Git submodules documentation](../../ci/git_submodules.md). - -### Container Registry - -With the update permission model we also extended the support for accessing -Container Registries for private projects. - -GitLab Runner versions prior to 1.8 don't incorporate the introduced changes -for permissions. This makes the `image:` directive not work with private -projects automatically and it needs to be configured manually on the GitLab Runner host -with a predefined account (for example administrator's personal account with -access token created explicitly for this purpose). This issue is resolved with -latest changes in GitLab Runner 1.8 which receives GitLab credentials with -build data. - -Starting from GitLab 8.12, if you have [2FA](../profile/account/two_factor_authentication.md) enabled in your account, you need -to pass a [personal access token](../profile/personal_access_tokens.md) instead of your password in order to -login to the Container Registry. - -Your jobs can access all container images that you would normally have access -to. The only implication is that you can push to the Container Registry of the -project for which the job is triggered. - -This is how an example usage can look like: - -```yaml -test: - script: - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - - docker pull $CI_REGISTRY/group/other-project:latest - - docker run $CI_REGISTRY/group/other-project:latest -``` - -### Pipeline triggers - -Since 9.0 [pipeline triggers](../../ci/triggers/README.md#ci-job-token) do support the new permission model. -The new triggers do impersonate their associated user including their access -to projects and their project permissions. - -### API - -GitLab API can be used via `CI_JOB_TOKEN`, see [the relevant documentation](../../api/README.md#gitlab-ci-job-token). + + diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb new file mode 100644 index 00000000000..cad49507323 --- /dev/null +++ b/spec/features/boards/sidebar_assignee_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Issue Boards', :js do + include BoardHelpers + + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let!(:development) { create(:label, project: project, name: 'Development') } + let!(:regression) { create(:label, project: project, name: 'Regression') } + let!(:stretch) { create(:label, project: project, name: 'Stretch') } + let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], labels: [development], relative_position: 2) } + let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } + let(:board) { create(:board, project: project) } + let!(:list) { create(:list, board: board, label: development, position: 0) } + let(:card) { find('.board:nth-child(2)').first('.board-card') } + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'assignee' do + it 'updates the issues assignee' do + click_card(card) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text + + page.within('.dropdown-menu-user') do + first('.gl-avatar-labeled').click + end + + click_button('Edit') + wait_for_requests + + expect(page).to have_content(assignee) + end + + expect(card).to have_selector('.avatar') + end + + it 'removes the assignee' do + card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)') + click_card(card_two) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + page.within('.dropdown-menu-user') do + find('[data-testid="unassign"]').click + end + + click_button('Edit') + wait_for_requests + + expect(page).to have_content('None') + end + + expect(card_two).not_to have_selector('.avatar') + end + + it 'assignees to current user' do + click_card(card) + + page.within(find('.assignee')) do + expect(page).to have_content('None') + + click_button 'assign yourself' + + wait_for_requests + + expect(page).to have_content(user.name) + end + + expect(card).to have_selector('.avatar') + end + + it 'updates assignee dropdown' do + click_card(card) + + page.within('.assignee') do + click_button('Edit') + + wait_for_requests + + assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text + + page.within('.dropdown-menu-user') do + first('.gl-avatar-labeled').click + end + + click_button('Edit') + wait_for_requests + + expect(page).to have_content(assignee) + end + + page.within(find('.board:nth-child(2)')) do + find('.board-card:nth-child(2)').click + end + + page.within('.assignee') do + click_button('Edit') + + expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon') + end + end + end +end diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb new file mode 100644 index 00000000000..0648bb67787 --- /dev/null +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Issue Boards', :js do + include BoardHelpers + + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let!(:development) { create(:label, project: project, name: 'Development') } + let!(:bug) { create(:label, project: project, name: 'Bug') } + let!(:regression) { create(:label, project: project, name: 'Regression') } + let!(:stretch) { create(:label, project: project, name: 'Stretch') } + let!(:issue1) { create(:labeled_issue, project: project, labels: [development], relative_position: 2) } + let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } + let(:board) { create(:board, project: project) } + let!(:list) { create(:list, board: board, label: development, position: 0) } + let(:card) { find('.board:nth-child(2)').first('.board-card') } + + before do + project.add_maintainer(user) + + sign_in(user) + + visit project_board_path(project, board) + wait_for_requests + end + + context 'labels' do + it 'shows current labels when editing' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + + wait_for_requests + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 2) + expect(page).to have_content(development.title) + expect(page).to have_content(stretch.title) + end + end + end + + it 'adds a single label' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + + wait_for_requests + + click_link bug.title + + wait_for_requests + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 3) + expect(page).to have_content(bug.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label', count: 2) + expect(card).to have_content(bug.title) + end + + it 'adds a multiple labels' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + + wait_for_requests + + click_link bug.title + + wait_for_requests + + click_link regression.title + + wait_for_requests + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 4) + expect(page).to have_content(bug.title) + expect(page).to have_content(regression.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label', count: 3) + expect(card).to have_content(bug.title) + expect(card).to have_content(regression.title) + end + + it 'removes a label' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + + wait_for_requests + + within('.dropdown-menu-labels') do + click_link stretch.title + end + + wait_for_requests + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.gl-label-text', count: 1) + expect(page).not_to have_content(stretch.title) + end + end + + # 'Development' label does not show since the card is in a 'Development' list label + expect(card).to have_selector('.gl-label-text', count: 0) + expect(card).not_to have_content(stretch.title) + end + + it 'creates project label' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + wait_for_requests + + click_link 'Create project label' + fill_in 'new_label_name', with: 'test label' + first('.suggest-colors-dropdown a').click + click_button 'Create' + wait_for_requests + + expect(page).to have_link 'test label' + end + expect(page).to have_selector('.board', count: 3) + end + + it 'creates project label and list' do + click_card(card) + + page.within('.labels') do + click_link 'Edit' + wait_for_requests + + click_link 'Create project label' + fill_in 'new_label_name', with: 'test label' + first('.suggest-colors-dropdown a').click + first('.js-add-list').click + click_button 'Create' + wait_for_requests + + expect(page).to have_link 'test label' + end + expect(page).to have_selector('.board', count: 4) + end + end +end diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index c79bf2abff1..5aeb5cff15f 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -4,17 +4,14 @@ require 'spec_helper' RSpec.describe 'Issue Boards', :js do include BoardHelpers - include FilteredSearchHelpers let(:user) { create(:user) } - let(:user2) { create(:user) } let(:project) { create(:project, :public) } let!(:milestone) { create(:milestone, project: project) } let!(:development) { create(:label, project: project, name: 'Development') } - let!(:bug) { create(:label, project: project, name: 'Bug') } let!(:regression) { create(:label, project: project, name: 'Regression') } let!(:stretch) { create(:label, project: project, name: 'Stretch') } - let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) } + let!(:issue1) { create(:labeled_issue, project: project, milestone: milestone, labels: [development], relative_position: 2) } let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } let(:board) { create(:board, project: project) } let!(:list) { create(:list, board: board, label: development, position: 0) } @@ -72,100 +69,6 @@ RSpec.describe 'Issue Boards', :js do end end - context 'assignee' do - it 'updates the issues assignee' do - click_card(card) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content(assignee) - end - - expect(card).to have_selector('.avatar') - end - - it 'removes the assignee' do - card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)') - click_card(card_two) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - page.within('.dropdown-menu-user') do - find('[data-testid="unassign"]').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content('None') - end - - expect(card_two).not_to have_selector('.avatar') - end - - it 'assignees to current user' do - click_card(card) - - page.within(find('.assignee')) do - expect(page).to have_content('None') - - click_button 'assign yourself' - - wait_for_requests - - expect(page).to have_content(user.name) - end - - expect(card).to have_selector('.avatar') - end - - it 'updates assignee dropdown' do - click_card(card) - - page.within('.assignee') do - click_button('Edit') - - wait_for_requests - - assignee = first('.gl-avatar-labeled').find('.gl-avatar-labeled-label').text - - page.within('.dropdown-menu-user') do - first('.gl-avatar-labeled').click - end - - click_button('Edit') - wait_for_requests - - expect(page).to have_content(assignee) - end - - page.within(find('.board:nth-child(2)')) do - find('.board-card:nth-child(2)').click - end - - page.within('.assignee') do - click_button('Edit') - - expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon') - end - end - end - context 'milestone' do it 'adds a milestone' do click_card(card) @@ -234,7 +137,7 @@ RSpec.describe 'Issue Boards', :js do page.within('.due_date') do click_link 'Edit' - click_button Date.today.day + click_button Date.today.day.to_s wait_for_requests @@ -243,144 +146,6 @@ RSpec.describe 'Issue Boards', :js do end end - context 'labels' do - it 'shows current labels when editing' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 2) - expect(page).to have_content(development.title) - expect(page).to have_content(stretch.title) - end - end - end - - it 'adds a single label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link bug.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 3) - expect(page).to have_content(bug.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label', count: 2) - expect(card).to have_content(bug.title) - end - - it 'adds a multiple labels' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - click_link bug.title - - wait_for_requests - - click_link regression.title - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 4) - expect(page).to have_content(bug.title) - expect(page).to have_content(regression.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label', count: 3) - expect(card).to have_content(bug.title) - expect(card).to have_content(regression.title) - end - - it 'removes a label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - within('.dropdown-menu-labels') do - click_link stretch.title - end - - wait_for_requests - - find('.dropdown-menu-close-icon').click - - page.within('.value') do - expect(page).to have_selector('.gl-label-text', count: 1) - expect(page).not_to have_content(stretch.title) - end - end - - # 'Development' label does not show since the card is in a 'Development' list label - expect(card).to have_selector('.gl-label-text', count: 0) - expect(card).not_to have_content(stretch.title) - end - - it 'creates project label' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - wait_for_requests - - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - click_button 'Create' - wait_for_requests - - expect(page).to have_link 'test label' - end - expect(page).to have_selector('.board', count: 3) - end - - it 'creates project label and list' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - wait_for_requests - - click_link 'Create project label' - fill_in 'new_label_name', with: 'test label' - first('.suggest-colors-dropdown a').click - first('.js-add-list').click - click_button 'Create' - wait_for_requests - - expect(page).to have_link 'test label' - end - expect(page).to have_selector('.board', count: 4) - end - end - context 'subscription' do it 'changes issue subscription' do click_card(card)