提交 ac6a3197 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 abe11a6a
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
<!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." --> <!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." -->
### User experience goal
<!-- What is the single user experience workflow this problem addresses?
For example, "The user should be able to use the UI/API/.gitlab-ci.yml with GitLab to <perform a specific task>"
https://about.gitlab.com/handbook/engineering/ux/ux-research-training/user-story-mapping/ -->
### Intended users ### Intended users
<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later. <!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
......
...@@ -52,7 +52,7 @@ module Resolvers ...@@ -52,7 +52,7 @@ module Resolvers
type Types::IssueType, null: true type Types::IssueType, null: true
NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc].freeze NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc label_priority_asc label_priority_desc].freeze
def resolve(**args) def resolve(**args)
# The project could have been loaded in batch by `BatchLoader`. # The project could have been loaded in batch by `BatchLoader`.
......
...@@ -7,5 +7,7 @@ module Types ...@@ -7,5 +7,7 @@ module Types
value 'PRIORITY_ASC', 'Priority by ascending order', value: :priority_asc value 'PRIORITY_ASC', 'Priority by ascending order', value: :priority_asc
value 'PRIORITY_DESC', 'Priority by descending order', value: :priority_desc value 'PRIORITY_DESC', 'Priority by descending order', value: :priority_desc
value 'LABEL_PRIORITY_ASC', 'Label priority by ascending order', value: :label_priority_asc
value 'LABEL_PRIORITY_DESC', 'Label priority by descending order', value: :label_priority_desc
end end
end end
...@@ -5,9 +5,9 @@ module Types ...@@ -5,9 +5,9 @@ module Types
graphql_name 'IssueSort' graphql_name 'IssueSort'
description 'Values for sorting issues' description 'Values for sorting issues'
value 'DUE_DATE_ASC', 'Due date by ascending order', value: 'due_date_asc' value 'DUE_DATE_ASC', 'Due date by ascending order', value: :due_date_asc
value 'DUE_DATE_DESC', 'Due date by descending order', value: 'due_date_desc' value 'DUE_DATE_DESC', 'Due date by descending order', value: :due_date_desc
value 'RELATIVE_POSITION_ASC', 'Relative position by ascending order', value: 'relative_position_asc' value 'RELATIVE_POSITION_ASC', 'Relative position by ascending order', value: :relative_position_asc
end end
end end
......
...@@ -311,7 +311,8 @@ module ApplicationSettingsHelper ...@@ -311,7 +311,8 @@ module ApplicationSettingsHelper
:snippet_size_limit, :snippet_size_limit,
:email_restrictions_enabled, :email_restrictions_enabled,
:email_restrictions, :email_restrictions,
:issues_create_limit :issues_create_limit,
:raw_blob_request_limit
] ]
end end
......
...@@ -144,7 +144,7 @@ class ApplicationSetting < ApplicationRecord ...@@ -144,7 +144,7 @@ class ApplicationSetting < ApplicationRecord
validates :default_artifacts_expire_in, presence: true, duration: true validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_expiration_policies_enable_historic_entries, validates :container_expiration_policies_enable_historic_entries,
inclusion: { in: [true, false], message: 'must be a boolean value' } inclusion: { in: [true, false], message: 'must be a boolean value' }
validates :container_registry_token_expire_delay, validates :container_registry_token_expire_delay,
presence: true, presence: true,
...@@ -346,7 +346,10 @@ class ApplicationSetting < ApplicationRecord ...@@ -346,7 +346,10 @@ class ApplicationSetting < ApplicationRecord
numericality: { only_integer: true, greater_than_or_equal_to: 0 } numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :issues_create_limit, validates :issues_create_limit,
numericality: { greater_than_or_equal_to: 0 } numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :raw_blob_request_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
attr_encrypted :asset_proxy_secret_key, attr_encrypted :asset_proxy_secret_key,
mode: :per_attribute_iv, mode: :per_attribute_iv,
......
---
title: Add raw_blob_request_limit to Application Settings API
merge_request: 30211
author:
type: added
---
title: Add sorting issues by label priority to graphQL endpoint
merge_request: 27936
author:
type: added
...@@ -237,6 +237,7 @@ Qualys ...@@ -237,6 +237,7 @@ Qualys
Rackspace Rackspace
Raketask Raketask
Raketasks Raketasks
reachability
rebase rebase
rebased rebased
rebases rebases
...@@ -372,6 +373,7 @@ unstage ...@@ -372,6 +373,7 @@ unstage
unstaged unstaged
unstages unstages
unstaging unstaging
untarred
untracked untracked
untrusted untrusted
unverified unverified
......
...@@ -312,7 +312,7 @@ things to check to debug the situation. ...@@ -312,7 +312,7 @@ things to check to debug the situation.
interval](ldap-ee.md#adjusting-ldap-group-sync-schedule) for the group to interval](ldap-ee.md#adjusting-ldap-group-sync-schedule) for the group to
sync. To speed up the process, either go to the GitLab group **Settings -> sync. To speed up the process, either go to the GitLab group **Settings ->
Members** and press **Sync now** (sync one group) or [run the group sync Rake Members** and press **Sync now** (sync one group) or [run the group sync Rake
task](../raketasks/ldap.md#run-a-group-sync) (sync all groups). task](../raketasks/ldap.md#run-a-group-sync-starter-only) (sync all groups).
If all of the above looks good, jump in to a little more advanced debugging in If all of the above looks good, jump in to a little more advanced debugging in
the rails console. the rails console.
...@@ -352,7 +352,7 @@ GitLab syncs the `admin_group`. ...@@ -352,7 +352,7 @@ GitLab syncs the `admin_group`.
NOTE: **NOTE:** NOTE: **NOTE:**
To sync all groups manually when debugging is unnecessary, [use the Rake To sync all groups manually when debugging is unnecessary, [use the Rake
task](../raketasks/ldap.md#run-a-group-sync) instead. task](../raketasks/ldap.md#run-a-group-sync-starter-only) instead.
The output from a manual [group sync](ldap-ee.md#group-sync) can show you what happens The output from a manual [group sync](ldap-ee.md#group-sync) can show you what happens
when GitLab syncs its LDAP group memberships against LDAP. when GitLab syncs its LDAP group memberships against LDAP.
......
# Integrity check Rake task # Integrity check Rake task **(CORE ONLY)**
GitLab provides Rake tasks to check the integrity of various components. GitLab provides Rake tasks to check the integrity of various components.
......
# GitHub import # GitHub import **(CORE ONLY)**
> [Introduced]( https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10308) in GitLab 9.1. > [Introduced]( https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10308) in GitLab 9.1.
......
# LDAP Rake tasks # LDAP Rake tasks **(CORE ONLY)**
The following are LDAP-related Rake tasks. The following are LDAP-related Rake tasks.
...@@ -28,7 +28,7 @@ limit by passing a number to the check task: ...@@ -28,7 +28,7 @@ limit by passing a number to the check task:
rake gitlab:ldap:check[50] rake gitlab:ldap:check[50]
``` ```
## Run a group sync ## Run a group sync **(STARTER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14735) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.2. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14735) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.2.
......
# Maintenance Rake tasks # Maintenance Rake tasks **(CORE ONLY)**
GitLab provides Rake tasks for general maintenance. GitLab provides Rake tasks for general maintenance.
......
# Praefect Rake Tasks # Praefect Rake Tasks **(CORE ONLY)**
> [Introduced]( https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28369) in GitLab 12.10. > [Introduced]( https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28369) in GitLab 12.10.
......
# Repository storage Rake tasks # Repository storage Rake tasks **(CORE ONLY)**
This is a collection of Rake tasks you can use to help you list and migrate This is a collection of Rake tasks you can use to help you list and migrate
existing projects and attachments associated with it from Legacy storage to existing projects and attachments associated with it from Legacy storage to
......
# Uploads migrate Rake tasks # Uploads migrate Rake tasks **(CORE ONLY)**
`gitlab:uploads:migrate` migrates uploads between different storage types. `gitlab:uploads:migrate` migrates uploads between different storage types.
......
# Uploads sanitize Rake tasks # Uploads sanitize Rake tasks **(CORE ONLY)**
Since GitLab 11.9, EXIF data is automatically stripped from JPG or TIFF image uploads. Since GitLab 11.9, EXIF data is automatically stripped from JPG or TIFF image uploads.
......
...@@ -4593,6 +4593,16 @@ enum IssueSort { ...@@ -4593,6 +4593,16 @@ enum IssueSort {
""" """
DUE_DATE_DESC DUE_DATE_DESC
"""
Label priority by ascending order
"""
LABEL_PRIORITY_ASC
"""
Label priority by descending order
"""
LABEL_PRIORITY_DESC
""" """
Priority by ascending order Priority by ascending order
""" """
......
...@@ -13027,6 +13027,18 @@ ...@@ -13027,6 +13027,18 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "LABEL_PRIORITY_ASC",
"description": "Label priority by ascending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "LABEL_PRIORITY_DESC",
"description": "Label priority by descending order",
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "DUE_DATE_ASC", "name": "DUE_DATE_ASC",
"description": "Due date by ascending order", "description": "Due date by ascending order",
......
...@@ -21,10 +21,10 @@ Parameters: ...@@ -21,10 +21,10 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ------ | -------- | ----------- | | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids[]` | integer array | optional | Return only the milestones having the given `iid` | | `iids[]` | integer array | no | Return only the milestones having the given `iid` |
| `state` | string | optional | Return only `active` or `closed` milestones | | `state` | string | no | Return only `active` or `closed` milestones |
| `title` | string | optional | Return only the milestones having the given `title` | | `title` | string | no | Return only the milestones having the given `title` |
| `search` | string | optional | Return only milestones with a title or description matching the provided string | | `search` | string | no | Return only milestones with a title or description matching the provided string |
```shell ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/milestones curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/milestones
...@@ -44,7 +44,8 @@ Example Response: ...@@ -44,7 +44,8 @@ Example Response:
"start_date": "2013-11-10", "start_date": "2013-11-10",
"state": "active", "state": "active",
"updated_at": "2013-10-02T09:24:18Z", "updated_at": "2013-10-02T09:24:18Z",
"created_at": "2013-10-02T09:24:18Z" "created_at": "2013-10-02T09:24:18Z",
"web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/42"
} }
] ]
``` ```
...@@ -59,8 +60,10 @@ GET /groups/:id/milestones/:milestone_id ...@@ -59,8 +60,10 @@ GET /groups/:id/milestones/:milestone_id
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of the group milestone | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `milestone_id` | integer | yes | The ID of the group milestone |
## Create new milestone ## Create new milestone
...@@ -72,11 +75,13 @@ POST /groups/:id/milestones ...@@ -72,11 +75,13 @@ POST /groups/:id/milestones
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `title` (required) - The title of a milestone | --------- | ------ | -------- | ----------- |
- `description` (optional) - The description of the milestone | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
- `due_date` (optional) - The due date of the milestone | `title` | string | yes | The title of a milestone |
- `start_date` (optional) - The start date of the milestone | `description` | string | no | The description of the milestone |
| `due_date` | date | no | The due date of the milestone, in YYYY-MM-DD format (ISO 8601) |
| `start_date` | date | no | The start date of the milestone, in YYYY-MM-DD format (ISO 8601) |
## Edit milestone ## Edit milestone
...@@ -88,13 +93,15 @@ PUT /groups/:id/milestones/:milestone_id ...@@ -88,13 +93,15 @@ PUT /groups/:id/milestones/:milestone_id
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of a group milestone | --------- | ------ | -------- | ----------- |
- `title` (optional) - The title of a milestone | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
- `description` (optional) - The description of a milestone | `milestone_id` | integer | yes | The ID of a group milestone |
- `due_date` (optional) - The due date of the milestone | `title` | string | no | The title of a milestone |
- `start_date` (optional) - The start date of the milestone | `description` | string | no | The description of a milestone |
- `state_event` (optional) - The state event of the milestone (close|activate) | `due_date` | date | no | The due date of the milestone, in YYYY-MM-DD format (ISO 8601) |
| `start_date` | date | no | The start date of the milestone, in YYYY-MM-DD format (ISO 8601) |
| `state_event` | string | no | The state event of the milestone _(`close` or `activate`)_ |
## Delete group milestone ## Delete group milestone
...@@ -106,8 +113,10 @@ DELETE /groups/:id/milestones/:milestone_id ...@@ -106,8 +113,10 @@ DELETE /groups/:id/milestones/:milestone_id
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of the group's milestone | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `milestone_id` | integer | yes | The ID of the group's milestone |
## Get all issues assigned to a single milestone ## Get all issues assigned to a single milestone
...@@ -119,8 +128,10 @@ GET /groups/:id/milestones/:milestone_id/issues ...@@ -119,8 +128,10 @@ GET /groups/:id/milestones/:milestone_id/issues
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of a group milestone | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `milestone_id` | integer | yes | The ID of a group milestone |
## Get all merge requests assigned to a single milestone ## Get all merge requests assigned to a single milestone
...@@ -132,8 +143,10 @@ GET /groups/:id/milestones/:milestone_id/merge_requests ...@@ -132,8 +143,10 @@ GET /groups/:id/milestones/:milestone_id/merge_requests
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of a group milestone | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `milestone_id` | integer | yes | The ID of a group milestone |
## Get all burndown chart events for a single milestone **(STARTER)** ## Get all burndown chart events for a single milestone **(STARTER)**
...@@ -147,5 +160,7 @@ GET /groups/:id/milestones/:milestone_id/burndown_events ...@@ -147,5 +160,7 @@ GET /groups/:id/milestones/:milestone_id/burndown_events
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | Attribute | Type | Required | Description |
- `milestone_id` (required) - The ID of a group milestone | --------- | ------ | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `milestone_id` | integer | yes | The ID of a group milestone |
...@@ -70,7 +70,8 @@ Example response: ...@@ -70,7 +70,8 @@ Example response:
"asset_proxy_url": "https://assets.example.com", "asset_proxy_url": "https://assets.example.com",
"asset_proxy_whitelist": ["example.com", "*.example.com", "your-instance.com"], "asset_proxy_whitelist": ["example.com", "*.example.com", "your-instance.com"],
"npm_package_requests_forwarding": true, "npm_package_requests_forwarding": true,
"issues_create_limit": 300 "issues_create_limit": 300,
"raw_blob_request_limit": 300
} }
``` ```
...@@ -158,7 +159,8 @@ Example response: ...@@ -158,7 +159,8 @@ Example response:
"allow_local_requests_from_web_hooks_and_services": true, "allow_local_requests_from_web_hooks_and_services": true,
"allow_local_requests_from_system_hooks": false, "allow_local_requests_from_system_hooks": false,
"npm_package_requests_forwarding": true, "npm_package_requests_forwarding": true,
"issues_create_limit": 300 "issues_create_limit": 300,
"raw_blob_request_limit": 300
} }
``` ```
...@@ -364,4 +366,5 @@ are listed in the descriptions of the relevant settings. ...@@ -364,4 +366,5 @@ are listed in the descriptions of the relevant settings.
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. | | `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `web_ide_clientside_preview_enabled` | boolean | no | Client side evaluation (allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation). | | `web_ide_clientside_preview_enabled` | boolean | no | Client side evaluation (allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation). |
| `snippet_size_limit` | integer | no | Max snippet content size in **bytes**. Default: 52428800 Bytes (50MB).| | `snippet_size_limit` | integer | no | Max snippet content size in **bytes**. Default: 52428800 Bytes (50MB).|
| `issues_create_limit` | integer | no | Max number of issue creation requests allowed per minute per user.| | `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Default: 300. To disable throttling set to 0.|
| `raw_blob_request_limit` | integer | no | Max number of requests per minute for each raw path. Default: 300. To disable throttling set to 0.|
...@@ -1221,7 +1221,7 @@ a helpful link back to how the feature was developed. ...@@ -1221,7 +1221,7 @@ a helpful link back to how the feature was developed.
to the following should be added immediately below the heading as a blockquote: to the following should be added immediately below the heading as a blockquote:
- `> Introduced in GitLab 11.3.`. - `> Introduced in GitLab 11.3.`.
- Whenever possible, version text should have a link to the issue, merge request, or epic that introduced the feature. - Whenever possible, version text should have a link to the _completed_ issue, merge request, or epic that introduced the feature.
An issue is preferred over a merge request, and a merge request is preferred over an epic. For example: An issue is preferred over a merge request, and a merge request is preferred over an epic. For example:
- `> [Introduced](<link-to-issue>) in GitLab 11.3.`. - `> [Introduced](<link-to-issue>) in GitLab 11.3.`.
......
# Beginner's guide to writing end-to-end tests
In this tutorial, you will learn about the creation of end-to-end (_e2e_) tests
for [GitLab Community Edition](https://about.gitlab.com/install/?version=ce) and
[GitLab Enterprise Edition](https://about.gitlab.com/install/).
By the end of this tutorial, you will be able to:
- Determine whether an end-to-end test is needed.
- Understand the directory structure within `qa/`.
- Write a basic end-to-end test that will validate login features.
- Develop any missing [page object](page_objects.md) libraries.
## Before you write a test
Before you write tests, your
[GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit)
must be configured to run the specs. The end-to-end tests:
- Are contained within the `qa/` directory.
- Should be independent and
[idempotent](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning).
- Create [resources](resources.md) (such as project, issue, user) on an ad-hoc basis.
- Test the UI and API interfaces, and use the API to efficiently set up the UI tests.
TIP: **Tip:**
For more information, see [End-to-end testing Best Practices](best_practices.md).
## Determine if end-to-end tests are needed
Check the code coverage of a specific feature before writing end-to-end tests,
for both [GitLab Community Edition](https://gitlab-org.gitlab.io/gitlab-foss/coverage-ruby/#_AllFiles)
and [GitLab Enterprise Edition](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/#_AllFiles) projects.
Does sufficient test coverage exist at the unit, feature, or integration levels?
If you answered *yes*, then you *don't* need an end-to-end test.
For information about the distribution of tests per level in GitLab, see
[Testing Levels](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md).
- See the
[How to test at the correct level?](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md#how-to-test-at-the-correct-level)
section of the [Testing levels](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md) document.
- Review how often the feature changes. Stable features that don't change very often
might not be worth covering with end-to-end tests if they are already covered
in lower level tests.
- Finally, discuss the proposed test with the developer(s) involved in implementing
the feature and the lower-level tests.
CAUTION: **Caution:**
Check both [GitLab Community Edition](https://gitlab-org.gitlab.io/gitlab-foss/coverage-ruby/#_AllFiles) and
[GitLab Enterprise Edition](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/#_AllFiles) coverage projects
for previously-written tests for this feature. For analyzing the code coverage,
you must understand which application files implement specific features.
NOTE: **Note:**
In this tutorial we're writing a login end-to-end test, even though it has been
sufficiently covered by lower-level testing, because it's the first step for most
end-to-end flows, and is easiest to understand.
## Identify the DevOps stage
The GitLab QA end-to-end tests are organized by the different
[stages in the DevOps lifecycle](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features/browser_ui).
Determine where the test should be placed by
[stage](https://about.gitlab.com/handbook/product/categories/#devops-stages),
determine which feature the test will belong to, and then place it in a subdirectory
under the stage.
![DevOps lifecycle by stages](img/gl-devops-lifecycle-by-stage-numbers_V12_10.png)
NOTE: **Note:**
If the test is Enterprise Edition only, the test will be created in the `features/ee`
directory, but follow the same DevOps lifecycle format.
## Create a skeleton test
In the first part of this tutorial we will be testing login, which is owned by the
Manage stage. Inside `qa/specs/features/browser_ui/1_manage/login`, create a
file `basic_login_spec.rb`.
### The outer `context` block
Specs have an outer `context` indicating the DevOps stage.
```ruby
# frozen_string_literal: true
module QA
context 'Manage' do
end
end
```
### The `describe` block
Inside of our outer `context`, describe the feature to test. In this case, `Login`.
```ruby
# frozen_string_literal: true
module QA
context 'Manage' do
describe 'Login' do
end
end
end
```
### The `it` blocks (examples)
Every test suite contains at least one `it` block (example). A good way to start
writing end-to-end tests is to write test case descriptions as `it` blocks:
```ruby
module QA
context 'Manage' do
describe 'Login' do
it 'can login' do
end
it 'can logout' do
end
end
end
end
```
## Write the test
An important question is "What do we test?" and even more importantly, "How do we test?"
Begin by logging in.
```ruby
# frozen_string_literal: true
module QA
context 'Manage' do
describe 'Login' do
it 'can login' do
Flow::Login.sign_in
end
it 'can logout' do
Flow::Login.sign_in
end
end
end
end
```
After [running the spec](#run-the-spec), our test should login and end; then we
should answer the question "What do we test?"
```ruby
# frozen_string_literal: true
module QA
context 'Manage' do
describe 'Login' do
it 'can login' do
Flow::Login.sign_in
Page::Main::Menu.perform do |menu|
expect(menu).to be_signed_in
end
end
it 'can logout' do
Flow::Login.sign_in
Page::Main::Menu.perform do |menu|
menu.sign_out
expect(menu).not_to be_signed_in
end
end
end
end
end
```
**What do we test?**
1. Can we log in?
1. Can we log out?
**How do we test?**
1. Check if the user avatar appears in the top navigation.
1. Check if the user avatar *does not* appear in the top navigation.
NOTE: **Note:**
Behind the scenes, `be_signed_in` is a
[predicate matcher](https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/built-in-matchers/predicate-matchers)
that [implements checking the user avatar](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/page/main/menu.rb#L74).
## De-duplicate your code
Refactor your test to use a `before` block for test setup, since it's duplicating
a call to `sign_in`.
```ruby
# frozen_string_literal: true
module QA
context 'Manage' do
describe 'Login' do
before do
Flow::Login.sign_in
end
it 'can login' do
Page::Main::Menu.perform do |menu|
expect(menu).to be_signed_in
end
end
it 'can logout' do
Page::Main::Menu.perform do |menu|
menu.sign_out
expect(menu).not_to be_signed_in
end
end
end
end
end
```
The `before` block is essentially a `before(:each)` and is run before each example,
ensuring we now log in at the beginning of each test.
## Test setup using resources and page objects
Next, let's test something other than Login. Let's test Issues, which are owned by the Plan
stage, so [create a file](#identify-the-devops-stage) in
`qa/specs/features/browser_ui/3_create/issues` called `issues_spec.rb`.
```ruby
# frozen_string_literal: true
module QA
context 'Plan' do
describe 'Issues' do
let(:issue) do
Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'My issue'
issue.description = 'This is an issue specific to this test'
end
end
before do
Flow::Login.sign_in
issue.visit!
end
it 'can close an issue' do
Page::Project::Issue::Show.perform do |show|
show.click_close_issue_button
expect(show).to be_closed
end
end
end
end
end
```
Note the following important points:
- At the start of our example, we will be at the `page/issue/show.rb` [page](page_objects.md).
- Our test fabricates only what it needs, when it needs it.
- The issue is fabricated through the API to save time.
- GitLab prefers `let()` over instance variables. See
[best practices](../best_practices.md#let-variables).
- `be_closed` is not implemented in `page/project/issue/show.rb` yet, but will be
implemented in the next step.
The issue is fabricated as a [Resource](resources.md), which is a GitLab entity
you can create through the UI or API. Other examples include:
- A [Merge Request](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/merge_request.rb).
- A [User](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/user.rb).
- A [Project](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/project.rb).
- A [Group](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/resource/group.rb).
## Write the page object
A [Page Object](page_objects.md) is a class in our suite that represents a page
within GitLab. The **Login** page would be one example. Since our page object for
the **Issue Show** page already exists, add the `closed?` method.
```ruby
module Page::Project::Issue
class Show
view 'app/views/projects/issues/show.html.haml' do
element :closed_status_box
end
def closed?
has_element?(:closed_status_box)
end
end
end
```
Next, define the element `closed_status_box` within your view, so your Page Object
can see it.
```haml
-#=> app/views/projects/issues/show.html.haml
.issuable-status-box.status-box.status-box-issue-closed{ ..., data: { qa_selector: 'closed_status_box' } }
```
## Run the spec
Before running the spec, confirm:
- The GDK is installed.
- The GDK is running on port 3000 locally.
- No additional [RSpec metadata tags](rspec_metadata_tests.md) have been applied.
- Your working directory is `qa/` within your GDK GitLab installation.
To run the spec, run the following command:
```ruby
bundle exec bin/qa Test::Instance::All http://localhost:3000 -- <test_file>
```
Where `<test_file>` is:
- `qa/specs/features/browser_ui/1_manage/login/login_spec.rb` when running the Login example.
- `qa/specs/features/browser_ui/2_plan/issues/issue_spec.rb` when running the Issue example.
...@@ -180,7 +180,7 @@ instance-level scenarios](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/ ...@@ -180,7 +180,7 @@ instance-level scenarios](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/
Continued reading: Continued reading:
- [Quick Start Guide](quick_start_guide.md) - [Beginner's Guide](beginners_guide.md)
- [Style Guide](style_guide.md) - [Style Guide](style_guide.md)
- [Best Practices](best_practices.md) - [Best Practices](best_practices.md)
- [Testing with feature flags](feature_flags.md) - [Testing with feature flags](feature_flags.md)
......
...@@ -15,26 +15,26 @@ GitLab Rake tasks are performed using: ...@@ -15,26 +15,26 @@ GitLab Rake tasks are performed using:
The following are available Rake tasks: The following are available Rake tasks:
| Tasks | Description | | Tasks | Description |
|:-------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------| |:----------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------|
| [Back up and restore](backup_restore.md) | Back up, restore, and migrate GitLab instances between servers. | | [Back up and restore](backup_restore.md) | Back up, restore, and migrate GitLab instances between servers. |
| [Clean up](cleanup.md) | Clean up unneeded items from GitLab instances. | | [Clean up](cleanup.md) | Clean up unneeded items from GitLab instances. |
| [Development](../development/rake_tasks.md) | Tasks for GitLab contributors. | | [Development](../development/rake_tasks.md) | Tasks for GitLab contributors. |
| [Elasticsearch](../integration/elasticsearch.md#gitlab-elasticsearch-rake-tasks) | Maintain Elasticsearch in a GitLab instance. | | [Elasticsearch](../integration/elasticsearch.md#gitlab-elasticsearch-rake-tasks) **(STARTER ONLY)** | Maintain Elasticsearch in a GitLab instance. |
| [Enable namespaces](features.md) | Enable usernames and namespaces for user projects. | | [Enable namespaces](features.md) | Enable usernames and namespaces for user projects. |
| [General maintenance](../administration/raketasks/maintenance.md) | General maintenance and self-check tasks. | | [General maintenance](../administration/raketasks/maintenance.md) | General maintenance and self-check tasks. |
| [Geo maintenance](../administration/raketasks/geo.md) **(PREMIUM ONLY)** | [Geo](../administration/geo/replication/index.md)-related maintenance. | | [Geo maintenance](../administration/raketasks/geo.md) **(PREMIUM ONLY)** | [Geo](../administration/geo/replication/index.md)-related maintenance. |
| [GitHub import](../administration/raketasks/github_import.md) | Retrieve and import repositories from GitHub. | | [GitHub import](../administration/raketasks/github_import.md) | Retrieve and import repositories from GitHub. |
| [Import repositories](import.md) | Import bare repositories into your GitLab instance. | | [Import repositories](import.md) | Import bare repositories into your GitLab instance. |
| [Import large project exports](../development/import_project.md#importing-via-a-rake-task) | Import large GitLab [project exports](../user/project/settings/import_export.md). | | [Import large project exports](../development/import_project.md#importing-via-a-rake-task) | Import large GitLab [project exports](../user/project/settings/import_export.md). |
| [Integrity checks](../administration/raketasks/check.md) | Check the integrity of repositories, files, and LDAP. | | [Integrity checks](../administration/raketasks/check.md) | Check the integrity of repositories, files, and LDAP. |
| [LDAP maintenance](../administration/raketasks/ldap.md) | [LDAP](../administration/auth/ldap.md)-related tasks. | | [LDAP maintenance](../administration/raketasks/ldap.md) | [LDAP](../administration/auth/ldap.md)-related tasks. |
| [List repositories](list_repos.md) | List of all GitLab-managed Git repositories on disk. | | [List repositories](list_repos.md) | List of all GitLab-managed Git repositories on disk. |
| [Project import/export](../administration/raketasks/project_import_export.md) | Prepare for [project exports and imports](../user/project/settings/import_export.md). | | [Project import/export](../administration/raketasks/project_import_export.md) | Prepare for [project exports and imports](../user/project/settings/import_export.md). |
| [Sample Prometheus data](generate_sample_prometheus_data.md) | Generate sample Prometheus data. | | [Sample Prometheus data](generate_sample_prometheus_data.md) | Generate sample Prometheus data. |
| [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. | | [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. |
| [Uploads migrate](../administration/raketasks/uploads/migrate.md) | Migrate uploads between storage local and object storage. | | [Uploads migrate](../administration/raketasks/uploads/migrate.md) | Migrate uploads between storage local and object storage. |
| [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. | | [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. |
| [User management](user_management.md) | Perform user management tasks. | | [User management](user_management.md) | Perform user management tasks. |
| [Webhooks administration](web_hooks.md) | Maintain project Webhooks. | | [Webhooks administration](web_hooks.md) | Maintain project Webhooks. |
| [X509 signatures](x509_signatures.md) | Update x509 commit signatures, useful if certificate store has changed. | | [X509 signatures](x509_signatures.md) | Update x509 commit signatures, useful if certificate store has changed. |
# Backing up and restoring GitLab # Backing up and restoring GitLab **(CORE ONLY)**
GitLab provides Rake tasks for backing up and restoring GitLab instances. GitLab provides Rake tasks for backing up and restoring GitLab instances.
...@@ -687,7 +687,7 @@ before restoring the backup. ...@@ -687,7 +687,7 @@ before restoring the backup.
You need to have a working GitLab installation before you can perform You need to have a working GitLab installation before you can perform
a restore. This is mainly because the system user performing the a restore. This is mainly because the system user performing the
restore actions (`git`) is usually not allowed to create or delete restore actions (`git`) is usually not allowed to create or delete
the SQL database it needs to import data into ('gitlabhq_production'). the SQL database it needs to import data into (`gitlabhq_production`).
All existing data will be either erased (SQL) or moved to a separate All existing data will be either erased (SQL) or moved to a separate
directory (repositories, uploads). directory (repositories, uploads).
...@@ -713,7 +713,7 @@ more of the following options: ...@@ -713,7 +713,7 @@ more of the following options:
Read what the [backup timestamp is about](#backup-timestamp). Read what the [backup timestamp is about](#backup-timestamp).
- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed, enabling the "Write to authorized_keys file" setting, and updating LDAP providers. - `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed, enabling the "Write to authorized_keys file" setting, and updating LDAP providers.
If you are restoring into directories that are mountpoints you will need to make If you are restoring into directories that are mount points, you will need to make
sure these directories are empty before attempting a restore. Otherwise GitLab sure these directories are empty before attempting a restore. Otherwise GitLab
will attempt to move these directories before restoring the new data and this will attempt to move these directories before restoring the new data and this
would cause an error. would cause an error.
......
# Clean up # Clean up **(CORE ONLY)**
GitLab provides Rake tasks for cleaning up GitLab instances. GitLab provides Rake tasks for cleaning up GitLab instances.
...@@ -7,9 +7,11 @@ GitLab provides Rake tasks for cleaning up GitLab instances. ...@@ -7,9 +7,11 @@ GitLab provides Rake tasks for cleaning up GitLab instances.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/36628) in GitLab 12.10. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/36628) in GitLab 12.10.
DANGER: **Danger:** DANGER: **Danger:**
Do not run this within 12 hours of a GitLab upgrade. This is to ensure that all background migrations have finished, which otherwise may lead to data loss. Do not run this within 12 hours of a GitLab upgrade. This is to ensure that all background migrations
have finished, which otherwise may lead to data loss.
When you remove LFS files from a repository's history, they become orphaned and continue to consume disk space. With this Rake task, you can remove invalid references from the database, which When you remove LFS files from a repository's history, they become orphaned and continue to consume
disk space. With this Rake task, you can remove invalid references from the database, which
will allow garbage collection of LFS files. will allow garbage collection of LFS files.
For example: For example:
......
# Namespaces # Namespaces **(CORE ONLY)**
This Rake task enables [namespaces](../user/group/index.md#namespaces) for projects. This Rake task enables [namespaces](../user/group/index.md#namespaces) for projects.
......
# Generate sample Prometheus data # Generate sample Prometheus data **(CORE ONLY)**
This command will run Prometheus queries for each of the metrics of a specific environment This command will run Prometheus queries for each of the metrics of a specific environment
for a series of time intervals to now: for a series of time intervals to now:
......
# Import bare repositories # Import bare repositories **(CORE ONLY)**
Rake tasks are available to import bare repositories into a GitLab instance. Rake tasks are available to import bare repositories into a GitLab instance.
......
# Listing repository directories # Listing repository directories **(CORE ONLY)**
You can print a list of all Git repositories on disk managed by GitLab. You can print a list of all Git repositories on disk managed by GitLab.
......
# User management # User management **(CORE ONLY)**
GitLab provides Rake tasks for user management. GitLab provides Rake tasks for user management.
......
# X509 signatures # X509 signatures **(CORE ONLY)**
When [signing commits with x509](../user/project/repository/x509_signed_commits/index.md), When [signing commits with x509](../user/project/repository/x509_signed_commits/index.md),
the trust anchor might change and the signatures stored within the database must be updated. the trust anchor might change and the signatures stored within the database must be updated.
......
...@@ -154,6 +154,7 @@ module API ...@@ -154,6 +154,7 @@ module API
optional :snowplow_app_id, type: String, desc: 'The Snowplow site name / application id' optional :snowplow_app_id, type: String, desc: 'The Snowplow site name / application id'
end end
optional :issues_create_limit, type: Integer, desc: "Maximum number of issue creation requests allowed per minute per user. Set to 0 for unlimited requests per minute." optional :issues_create_limit, type: Integer, desc: "Maximum number of issue creation requests allowed per minute per user. Set to 0 for unlimited requests per minute."
optional :raw_blob_request_limit, type: Integer, desc: "Maximum number of requests per minute for each raw path. Set to 0 for unlimited requests per minute."
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction", optional :"#{type}_key_restriction",
......
...@@ -46,7 +46,7 @@ the browser to use. You will need to have Chrome (or Chromium) and ...@@ -46,7 +46,7 @@ the browser to use. You will need to have Chrome (or Chromium) and
### Writing tests ### Writing tests
- [Writing tests from scratch tutorial](../doc/development/testing_guide/end_to_end/quick_start_guide.md) - [Writing tests from scratch tutorial](../doc/development/testing_guide/end_to_end/beginners_guide.md)
- [Best practices](../doc/development/testing_guide/best_practices.md) - [Best practices](../doc/development/testing_guide/best_practices.md)
- [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md) - [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md)
- [Guidelines](../doc/development/testing_guide/index.md) - [Guidelines](../doc/development/testing_guide/index.md)
......
...@@ -126,7 +126,6 @@ describe Resolvers::IssuesResolver do ...@@ -126,7 +126,6 @@ describe Resolvers::IssuesResolver do
context 'when sorting by due date' do context 'when sorting by due date' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:due_issue1) { create(:issue, project: project, due_date: 3.days.from_now) } let_it_be(:due_issue1) { create(:issue, project: project, due_date: 3.days.from_now) }
let_it_be(:due_issue2) { create(:issue, project: project, due_date: nil) } let_it_be(:due_issue2) { create(:issue, project: project, due_date: nil) }
let_it_be(:due_issue3) { create(:issue, project: project, due_date: 2.days.ago) } let_it_be(:due_issue3) { create(:issue, project: project, due_date: 2.days.ago) }
...@@ -143,7 +142,6 @@ describe Resolvers::IssuesResolver do ...@@ -143,7 +142,6 @@ describe Resolvers::IssuesResolver do
context 'when sorting by relative position' do context 'when sorting by relative position' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:relative_issue1) { create(:issue, project: project, relative_position: 2000) } let_it_be(:relative_issue1) { create(:issue, project: project, relative_position: 2000) }
let_it_be(:relative_issue2) { create(:issue, project: project, relative_position: nil) } let_it_be(:relative_issue2) { create(:issue, project: project, relative_position: nil) }
let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) } let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) }
...@@ -156,22 +154,40 @@ describe Resolvers::IssuesResolver do ...@@ -156,22 +154,40 @@ describe Resolvers::IssuesResolver do
context 'when sorting by priority' do context 'when sorting by priority' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
let_it_be(:label_1) { create(:label, project: project, priority: 1) } let_it_be(:priority_label1) { create(:label, project: project, priority: 1) }
let_it_be(:label_2) { create(:label, project: project, priority: 5) } let_it_be(:priority_label2) { create(:label, project: project, priority: 5) }
let_it_be(:issue1) { create(:issue, project: project, labels: [label_1], milestone: late_milestone) } let_it_be(:priority_issue1) { create(:issue, project: project, labels: [priority_label1], milestone: late_milestone) }
let_it_be(:issue2) { create(:issue, project: project, labels: [label_2]) } let_it_be(:priority_issue2) { create(:issue, project: project, labels: [priority_label2]) }
let_it_be(:issue3) { create(:issue, project: project, milestone: early_milestone) } let_it_be(:priority_issue3) { create(:issue, project: project, milestone: early_milestone) }
let_it_be(:issue4) { create(:issue, project: project) } let_it_be(:priority_issue4) { create(:issue, project: project) }
it 'sorts issues ascending' do
expect(resolve_issues(sort: :priority_asc).items).to eq([priority_issue3, priority_issue1, priority_issue2, priority_issue4])
end
it 'sorts issues descending' do
expect(resolve_issues(sort: :priority_desc).items).to eq([priority_issue1, priority_issue3, priority_issue2, priority_issue4])
end
end
context 'when sorting by label priority' do
let_it_be(:project) { create(:project) }
let_it_be(:label1) { create(:label, project: project, priority: 1) }
let_it_be(:label2) { create(:label, project: project, priority: 5) }
let_it_be(:label3) { create(:label, project: project, priority: 10) }
let_it_be(:label_issue1) { create(:issue, project: project, labels: [label1]) }
let_it_be(:label_issue2) { create(:issue, project: project, labels: [label2]) }
let_it_be(:label_issue3) { create(:issue, project: project, labels: [label1, label3]) }
let_it_be(:label_issue4) { create(:issue, project: project) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :priority_asc).items).to eq([issue3, issue1, issue2, issue4]) expect(resolve_issues(sort: :label_priority_asc).items).to eq([label_issue3, label_issue1, label_issue2, label_issue4])
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :priority_desc).items).to eq([issue1, issue3, issue2, issue4]) expect(resolve_issues(sort: :label_priority_desc).items).to eq([label_issue2, label_issue3, label_issue1, label_issue4])
end end
end end
end end
......
...@@ -8,6 +8,8 @@ describe GitlabSchema.types['IssueSort'] do ...@@ -8,6 +8,8 @@ describe GitlabSchema.types['IssueSort'] do
it_behaves_like 'common sort values' it_behaves_like 'common sort values'
it 'exposes all the existing issue sort values' do it 'exposes all the existing issue sort values' do
expect(described_class.values.keys).to include(*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC]) expect(described_class.values.keys).to include(
*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC LABEL_PRIORITY_ASC LABEL_PRIORITY_DESC]
)
end end
end end
...@@ -94,6 +94,14 @@ describe ApplicationSetting do ...@@ -94,6 +94,14 @@ describe ApplicationSetting do
it { is_expected.to allow_value(300).for(:issues_create_limit) } it { is_expected.to allow_value(300).for(:issues_create_limit) }
it { is_expected.not_to allow_value('three').for(:issues_create_limit) } it { is_expected.not_to allow_value('three').for(:issues_create_limit) }
it { is_expected.not_to allow_value(nil).for(:issues_create_limit) } it { is_expected.not_to allow_value(nil).for(:issues_create_limit) }
it { is_expected.not_to allow_value(10.5).for(:issues_create_limit) }
it { is_expected.not_to allow_value(-1).for(:issues_create_limit) }
it { is_expected.to allow_value(0).for(:raw_blob_request_limit) }
it { is_expected.not_to allow_value('abc').for(:raw_blob_request_limit) }
it { is_expected.not_to allow_value(nil).for(:raw_blob_request_limit) }
it { is_expected.not_to allow_value(10.5).for(:raw_blob_request_limit) }
it { is_expected.not_to allow_value(-1).for(:raw_blob_request_limit) }
context 'grafana_url validations' do context 'grafana_url validations' do
before do before do
......
...@@ -252,12 +252,12 @@ describe 'getting an issue list for a project' do ...@@ -252,12 +252,12 @@ describe 'getting an issue list for a project' do
let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) } let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) } let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) }
let_it_be(:label_1) { create(:label, project: sort_project, priority: 1) } let_it_be(:priority_label1) { create(:label, project: sort_project, priority: 1) }
let_it_be(:label_2) { create(:label, project: sort_project, priority: 5) } let_it_be(:priority_label2) { create(:label, project: sort_project, priority: 5) }
let_it_be(:issue1) { create(:issue, project: sort_project, labels: [label_1], milestone: late_milestone) } let_it_be(:priority_issue1) { create(:issue, project: sort_project, labels: [priority_label1], milestone: late_milestone) }
let_it_be(:issue2) { create(:issue, project: sort_project, labels: [label_2]) } let_it_be(:priority_issue2) { create(:issue, project: sort_project, labels: [priority_label2]) }
let_it_be(:issue3) { create(:issue, project: sort_project, milestone: early_milestone) } let_it_be(:priority_issue3) { create(:issue, project: sort_project, milestone: early_milestone) }
let_it_be(:issue4) { create(:issue, project: sort_project) } let_it_be(:priority_issue4) { create(:issue, project: sort_project) }
let_it_be(:params) { 'sort: PRIORITY_ASC' } let_it_be(:params) { 'sort: PRIORITY_ASC' }
...@@ -277,20 +277,20 @@ describe 'getting an issue list for a project' do ...@@ -277,20 +277,20 @@ describe 'getting an issue list for a project' do
context 'when ascending' do context 'when ascending' do
it 'sorts issues' do it 'sorts issues' do
expect(grab_iids).to eq([issue3.iid, issue1.iid, issue2.iid, issue4.iid]) expect(grab_iids).to eq([priority_issue3.iid, priority_issue1.iid, priority_issue2.iid, priority_issue4.iid])
end end
context 'when paginating' do context 'when paginating' do
let(:params) { 'sort: PRIORITY_ASC, first: 2' } let(:params) { 'sort: PRIORITY_ASC, first: 2' }
it 'sorts issues' do it 'sorts issues' do
expect(grab_iids).to eq([issue3.iid, issue1.iid]) expect(grab_iids).to eq([priority_issue3.iid, priority_issue1.iid])
cursored_query = query("sort: PRIORITY_ASC, after: \"#{end_cursor}\"") cursored_query = query("sort: PRIORITY_ASC, after: \"#{end_cursor}\"")
post_graphql(cursored_query, current_user: current_user) post_graphql(cursored_query, current_user: current_user)
response_data = JSON.parse(response.body)['data']['project']['issues']['edges'] response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
expect(grab_iids(response_data)).to eq([issue2.iid, issue4.iid]) expect(grab_iids(response_data)).to eq([priority_issue2.iid, priority_issue4.iid])
end end
end end
end end
...@@ -299,20 +299,90 @@ describe 'getting an issue list for a project' do ...@@ -299,20 +299,90 @@ describe 'getting an issue list for a project' do
let(:params) { 'sort: PRIORITY_DESC' } let(:params) { 'sort: PRIORITY_DESC' }
it 'sorts issues' do it 'sorts issues' do
expect(grab_iids).to eq([issue1.iid, issue3.iid, issue2.iid, issue4.iid]) expect(grab_iids).to eq([priority_issue1.iid, priority_issue3.iid, priority_issue2.iid, priority_issue4.iid])
end end
context 'when paginating' do context 'when paginating' do
let(:params) { 'sort: PRIORITY_DESC, first: 2' } let(:params) { 'sort: PRIORITY_DESC, first: 2' }
it 'sorts issues' do it 'sorts issues' do
expect(grab_iids).to eq([issue1.iid, issue3.iid]) expect(grab_iids).to eq([priority_issue1.iid, priority_issue3.iid])
cursored_query = query("sort: PRIORITY_DESC, after: \"#{end_cursor}\"") cursored_query = query("sort: PRIORITY_DESC, after: \"#{end_cursor}\"")
post_graphql(cursored_query, current_user: current_user) post_graphql(cursored_query, current_user: current_user)
response_data = JSON.parse(response.body)['data']['project']['issues']['edges'] response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
expect(grab_iids(response_data)).to eq([issue2.iid, issue4.iid]) expect(grab_iids(response_data)).to eq([priority_issue2.iid, priority_issue4.iid])
end
end
end
end
context 'when sorting by label priority' do
let_it_be(:sort_project) { create(:project, :public) }
let_it_be(:label1) { create(:label, project: sort_project, priority: 1) }
let_it_be(:label2) { create(:label, project: sort_project, priority: 5) }
let_it_be(:label3) { create(:label, project: sort_project, priority: 10) }
let_it_be(:label_issue1) { create(:issue, project: sort_project, labels: [label1]) }
let_it_be(:label_issue2) { create(:issue, project: sort_project, labels: [label2]) }
let_it_be(:label_issue3) { create(:issue, project: sort_project, labels: [label1, label3]) }
let_it_be(:label_issue4) { create(:issue, project: sort_project) }
let_it_be(:params) { 'sort: LABEL_PRIORITY_ASC' }
def query(issue_params = params)
graphql_query_for(
'project',
{ 'fullPath' => sort_project.full_path },
"issues(#{issue_params}) { pageInfo { endCursor} edges { node { iid dueDate } } }"
)
end
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
context 'when ascending' do
it 'sorts issues' do
expect(grab_iids).to eq [label_issue3.iid, label_issue1.iid, label_issue2.iid, label_issue4.iid]
end
context 'when paginating' do
let(:params) { 'sort: LABEL_PRIORITY_ASC, first: 2' }
it 'sorts issues' do
expect(grab_iids).to eq [label_issue3.iid, label_issue1.iid]
cursored_query = query("sort: LABEL_PRIORITY_ASC, after: \"#{end_cursor}\"")
post_graphql(cursored_query, current_user: current_user)
response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
expect(grab_iids(response_data)).to eq [label_issue2.iid, label_issue4.iid]
end
end
end
context 'when descending' do
let(:params) { 'sort: LABEL_PRIORITY_DESC' }
it 'sorts issues' do
expect(grab_iids).to eq [label_issue2.iid, label_issue3.iid, label_issue1.iid, label_issue4.iid]
end
context 'when paginating' do
let(:params) { 'sort: LABEL_PRIORITY_DESC, first: 2' }
it 'sorts issues' do
expect(grab_iids).to eq [label_issue2.iid, label_issue3.iid]
cursored_query = query("sort: LABEL_PRIORITY_DESC, after: \"#{end_cursor}\"")
post_graphql(cursored_query, current_user: current_user)
response_data = JSON.parse(response.body)['data']['project']['issues']['edges']
expect(grab_iids(response_data)).to eq [label_issue1.iid, label_issue4.iid]
end end
end end
end end
......
...@@ -89,7 +89,8 @@ describe API::Settings, 'Settings' do ...@@ -89,7 +89,8 @@ describe API::Settings, 'Settings' do
push_event_hooks_limit: 2, push_event_hooks_limit: 2,
push_event_activities_limit: 2, push_event_activities_limit: 2,
snippet_size_limit: 5, snippet_size_limit: 5,
issues_create_limit: 300 issues_create_limit: 300,
raw_blob_request_limit: 300
} }
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
...@@ -127,6 +128,7 @@ describe API::Settings, 'Settings' do ...@@ -127,6 +128,7 @@ describe API::Settings, 'Settings' do
expect(json_response['push_event_activities_limit']).to eq(2) expect(json_response['push_event_activities_limit']).to eq(2)
expect(json_response['snippet_size_limit']).to eq(5) expect(json_response['snippet_size_limit']).to eq(5)
expect(json_response['issues_create_limit']).to eq(300) expect(json_response['issues_create_limit']).to eq(300)
expect(json_response['raw_blob_request_limit']).to eq(300)
end end
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册