Commit 7afa0877 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'ee/master' into 2302-environment-specific-variables

* ee/master: (24 commits)
  Changelog
  Fix GeoDynaicBackoff strategy code and added specs
  Enable RSpec/SingleLineHook cop
  Correct RSpec/SingleLineHook cop offenses
  Add a custom RSpec/SingleLineHook cop
  Add docs for personal access tokens
  Fix wrong link to codeclimate diff docs
  Cleaned up helper methods in protected tags/branches specs
  Handle nil GeoNode to prevent spec failures
  Fix failing spec
  Add related issues backend
  Add more descriptive message about NotImplementedError and display it in health check
  Restrict Geo tests to PostgreSQL
  Suggest a way to resolve Geo tracking DB not having all the migrations
  Move License initializer to the front
  Memoized access levels in ProtectedBranch EE specs
  Cleanup protected branch EE specs, removing unnecessary to_sym
  Revert "Fix specs during CE->EE"
  Fix specs during CE->EE
  Merge branch 'winh-ignore-CVE-2017-5029' into 'master'
  ...
parents 3713afa2 9f999549
......@@ -1069,6 +1069,13 @@ RSpec/NotToNot:
RSpec/RepeatedDescription:
Enabled: false
# Ensure RSpec hook blocks are always multi-line.
RSpec/SingleLineHook:
Enabled: true
Exclude:
- 'spec/factories/*'
- 'spec/requests/api/v3/*'
# Checks for stubbed test subjects.
RSpec/SubjectStub:
Enabled: false
......
module Projects
class IssueLinksController < Projects::ApplicationController
before_action :authorize_admin_issue_link!, only: [:create, :destroy]
def index
render json: issues
end
def create
create_params = params.slice(:issue_references)
result = IssueLinks::CreateService.new(issue, current_user, create_params).execute
render json: { message: result[:message], issues: issues }, status: result[:http_status]
end
def destroy
issue_link = IssueLink.find(params[:id])
return render_403 unless can?(current_user, :admin_issue_link, issue_link.target.project)
IssueLinks::DestroyService.new(issue_link, current_user).execute
render json: { issues: issues }
end
private
def issues
IssueLinks::ListService.new(issue, current_user).execute
end
def authorize_admin_issue_link!
render_403 unless can?(current_user, :admin_issue_link, @project)
end
def issue
@issue ||=
IssuesFinder.new(current_user, project_id: @project.id)
.execute
.find_by!(iid: params[:issue_id])
end
end
end
......@@ -6,6 +6,8 @@ class GeoNodeStatus
def health
@health ||= HealthCheck::Utils.process_checks(['geo'])
rescue NotImplementedError => e
@health = e.to_s
end
def healthy?
......
class IssueLink < ActiveRecord::Base
belongs_to :source, class_name: 'Issue'
belongs_to :target, class_name: 'Issue'
validates :source, presence: true
validates :target, presence: true
validates :source, uniqueness: { scope: :target_id, message: 'is already related' }
validate :check_self_relation
private
def check_self_relation
return unless source && target
if source == target
errors.add(:source, 'cannot be related to itself')
end
end
end
......@@ -8,6 +8,7 @@ class License < ActiveRecord::Base
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze
ELASTIC_SEARCH_FEATURE = 'GitLab_ElasticSearch'.freeze
RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze
FEATURE_CODES = {
geo: GEO_FEATURE,
......@@ -15,6 +16,7 @@ class License < ActiveRecord::Base
service_desk: SERVICE_DESK_FEATURE,
object_storage: OBJECT_STORAGE_FEATURE,
elastic_search: ELASTIC_SEARCH_FEATURE,
related_issues: RELATED_ISSUES_FEATURE,
# Features that make sense to Namespace:
deploy_board: DEPLOY_BOARD_FEATURE,
file_lock: FILE_LOCK_FEATURE
......@@ -26,7 +28,8 @@ class License < ActiveRecord::Base
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EES_FEATURES = [
{ ELASTIC_SEARCH_FEATURE => 1 }
{ ELASTIC_SEARCH_FEATURE => 1 },
{ RELATED_ISSUES_FEATURE => 1 }
].freeze
EEP_FEATURES = [
......
......@@ -3,7 +3,7 @@ class SystemNoteMetadata < ActiveRecord::Base
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged
outdated
approved unapproved
approved unapproved relate unrelate
].freeze
validates :note, presence: true
......
......@@ -22,6 +22,11 @@ module EE
cannot! :create_note
cannot! :read_project
end
unless project.feature_available?(:related_issues)
cannot! :read_issue_link
cannot! :admin_issue_link
end
end
end
end
......@@ -55,6 +55,9 @@ class ProjectPolicy < BasePolicy
can! :read_pipeline_schedule
can! :read_build
end
# EE-only
can! :read_issue_link
end
def reporter_access!
......@@ -79,6 +82,9 @@ class ProjectPolicy < BasePolicy
if project.feature_available?(:deploy_board) || Rails.env.development?
can! :read_deploy_board
end
# EE-only
can! :admin_issue_link
end
# Permissions given when an user is team member of a project
......@@ -321,5 +327,8 @@ class ProjectPolicy < BasePolicy
# NOTE: may be overridden by IssuePolicy
can! :read_issue
# EE-only
can! :read_issue_link
end
end
module IssueLinks
class CreateService < BaseService
def initialize(issue, user, params)
@issue, @current_user, @params = issue, user, params.dup
end
def execute
if referenced_issues.blank?
return error('No Issue found for given reference', 401)
end
create_issue_links
success
end
private
def create_issue_links
referenced_issues.each do |referenced_issue|
create_notes(referenced_issue) if relate_issues(referenced_issue)
end
end
def relate_issues(referenced_issue)
IssueLink.create(source: @issue, target: referenced_issue)
end
def create_notes(referenced_issue)
SystemNoteService.relate_issue(@issue, referenced_issue, current_user)
SystemNoteService.relate_issue(referenced_issue, @issue, current_user)
end
def referenced_issues
@referenced_issues ||= begin
issue_references = params[:issue_references]
text = issue_references.join(' ')
extractor = Gitlab::ReferenceExtractor.new(@issue.project, @current_user)
extractor.analyze(text)
extractor.issues.select do |issue|
can?(current_user, :admin_issue_link, issue)
end
end
end
end
end
module IssueLinks
class DestroyService < BaseService
def initialize(issue_link, user)
@issue_link = issue_link
@current_user = user
@issue = issue_link.source
@referenced_issue = issue_link.target
end
def execute
remove_relation
create_notes
success(message: 'Relation was removed')
end
private
def remove_relation
@issue_link.destroy!
end
def create_notes
SystemNoteService.unrelate_issue(@issue, @referenced_issue, current_user)
SystemNoteService.unrelate_issue(@referenced_issue, @issue, current_user)
end
end
end
module IssueLinks
class ListService
include Gitlab::Routing
def initialize(issue, user)
@issue, @current_user, @project = issue, user, issue.project
end
def execute
issues.map do |referenced_issue|
{
id: referenced_issue.id,
title: referenced_issue.title,
state: referenced_issue.state,
reference: referenced_issue.to_reference(@project),
path: namespace_project_issue_path(referenced_issue.project.namespace, referenced_issue.project, referenced_issue.iid),
destroy_relation_path: destroy_relation_path(referenced_issue)
}
end
end
private
def issues
related_issues = Issue
.select(['issues.*', 'issue_links.id AS issue_links_id'])
.joins("INNER JOIN issue_links ON
(issue_links.source_id = issues.id AND issue_links.target_id = #{@issue.id})
OR
(issue_links.target_id = issues.id AND issue_links.source_id = #{@issue.id})")
.preload(project: :namespace)
.reorder('issue_links_id')
Ability.issues_readable_by_user(related_issues, @current_user)
end
def destroy_relation_path(issue)
# Make sure the user can admin both the current issue AND the
# referenced issue projects in order to return the removal link.
if can_destroy_issue_link_on_current_project? && can_destroy_issue_link?(issue.project)
namespace_project_issue_link_path(@project.namespace,
@issue.project,
@issue.iid,
issue.issue_links_id)
end
end
def can_destroy_issue_link_on_current_project?
return @can_destroy_on_current_project if defined?(@can_destroy_on_current_project)
@can_destroy_on_current_project = can_destroy_issue_link?(@project)
end
def can_destroy_issue_link?(project)
Ability.allowed?(@current_user, :admin_issue_link, project)
end
end
end
......@@ -552,6 +552,38 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
#
# noteable - Noteable object
# noteable_ref - Referenced noteable object
# user - User performing reference
#
# Example Note text:
#
# "marked this issue as related to gitlab-ce#9001"
#
# Returns the created Note object
def relate_issue(noteable, noteable_ref, user)
body = "marked this issue as related to #{noteable_ref.to_reference(noteable.project)}"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'relate'))
end
#
# noteable - Noteable object
# noteable_ref - Referenced noteable object
# user - User performing reference
#
# Example Note text:
#
# "removed the relation with gitlab-ce#9001"
#
# Returns the created Note object
def unrelate_issue(noteable, noteable_ref, user)
body = "removed the relation with #{noteable_ref.to_reference(noteable.project)}"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'unrelate'))
end
# Called when the merge request is approved by user
#
# noteable - Noteable object
......
......@@ -11,15 +11,17 @@ module GeoDynamicBackoff
end
end
private
class_methods do
private
def linear_backoff_strategy(count)
rand(1..20) + count
end
def linear_backoff_strategy(count)
rand(1..20) + count
end
def geometric_backoff_strategy(count)
# This strategy is based on the original one from sidekiq
count = count - 30 # we must start counting after 30
(count**4) + 15 + (rand(30) * (count + 1))
def geometric_backoff_strategy(count)
# This strategy is based on the original one from sidekiq
count = count - 30 # we must start counting after 30
(count**4) + 15 + (rand(30) * (count + 1))
end
end
end
---
title: Allows manually adding bi-directional relationships between issues in the issue page (EES feature)
merge_request:
author:
---
title: 'Geo: fixed Dynamic Backoff strategy that was not being used by workers'
merge_request: 2128
author:
......@@ -8,7 +8,7 @@ end
begin
# Avoid using the database if this is run in a Rake task
if Gitlab::Geo.primary_role_enabled?
Gitlab::Geo.current_node.update_clone_url!
Gitlab::Geo.current_node&.update_clone_url!
end
rescue => e
warn "WARNING: Unable to check/update clone_url_prefix for Geo: #{e}"
......
......@@ -3,6 +3,11 @@
en:
hello: "Hello world"
activerecord:
attributes:
issue_link:
source: Source issue
target: Target issue
errors:
messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
......
......@@ -311,6 +311,8 @@ constraints(ProjectUrlConstrainer.new) do
post :bulk_update
post :export_csv
end
resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links'
end
resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
......
class CreateIssueLinksTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :issue_links do |t|
t.integer :source_id, null: false, index: true
t.integer :target_id, null: false, index: true
t.timestamps null: true
end
add_index :issue_links, [:source_id, :target_id], unique: true
add_concurrent_foreign_key :issue_links, :issues, column: :source_id
add_concurrent_foreign_key :issue_links, :issues, column: :target_id
end
def down
drop_table :issue_links
end
end
......@@ -656,6 +656,17 @@ ActiveRecord::Schema.define(version: 20170612150426) do
add_index "issue_assignees", ["issue_id", "user_id"], name: "index_issue_assignees_on_issue_id_and_user_id", unique: true, using: :btree
add_index "issue_assignees", ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree
create_table "issue_links", force: :cascade do |t|
t.integer "source_id", null: false
t.integer "target_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "issue_links", ["source_id", "target_id"], name: "index_issue_links_on_source_id_and_target_id", unique: true, using: :btree
add_index "issue_links", ["source_id"], name: "index_issue_links_on_source_id", using: :btree
add_index "issue_links", ["target_id"], name: "index_issue_links_on_target_id", using: :btree
create_table "issue_metrics", force: :cascade do |t|
t.integer "issue_id", null: false
t.datetime "first_mentioned_in_commit_at"
......@@ -1781,6 +1792,8 @@ ActiveRecord::Schema.define(version: 20170612150426) do
add_foreign_key "geo_repository_updated_events", "projects", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade
add_foreign_key "issue_links", "issues", column: "source_id", name: "fk_c900194ff2", on_delete: :cascade
add_foreign_key "issue_links", "issues", column: "target_id", name: "fk_e71bb44f1f", on_delete: :cascade
add_foreign_key "issue_metrics", "issues", on_delete: :cascade
add_foreign_key "label_priorities", "labels", on_delete: :cascade
add_foreign_key "label_priorities", "projects", on_delete: :cascade
......
......@@ -3,7 +3,7 @@
>**Note:**
>
> - [Introduced][ce-10308] in GitLab 9.1.
> - You need a personal access token in order to retrieve and import GitHub
> - You need a personal access token in order to retrieve and import GitHub
> projects. You can get it from: https://github.com/settings/tokens
> - You also need to pass an username as the second argument to the rake task
> which will become the owner of the project.
......@@ -19,7 +19,7 @@ bundle exec rake import:github[access_token,root,foo/bar] RAILS_ENV=production
```
In this case, `access_token` is your GitHub personal access token, `root`
is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
......
......@@ -56,19 +56,35 @@ following locations:
- [V3 to V4](v3_to_v4.md)
- [Version](version.md)
### Internal CI API
The following documentation is for the [internal CI API](ci/README.md):
- [Builds](ci/builds.md)
- [Runners](ci/runners.md)
## Road to GraphQL
Going forward, we will start on moving to
[GraphQL](http://graphql.org/learn/best-practices/) and deprecate the use of
controller-specific endpoints. GraphQL has a number of benefits:
1. We avoid having to maintain two different APIs.
2. Callers of the API can request only what they need.
3. It is versioned by default.
It will co-exist with the current v4 REST API. If we have a v5 API, this should
be a compatibility layer on top of GraphQL.
## Authentication
Most API requests require authentication via a session cookie or token. For those cases where it is not required, this will be mentioned in the documentation
for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md).
There are three types of tokens available: private tokens, OAuth 2 tokens, and personal
access tokens.
Most API requests require authentication via a session cookie or token. For
those cases where it is not required, this will be mentioned in the documentation
for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md).
There are three types of access tokens available:
1. [OAuth2 tokens](#oauth2-tokens)
1. [Private tokens](#private-tokens)
1. [Personal access tokens](#personal-access-tokens)
If authentication information is invalid or omitted, an error message will be
returned with status code `401`:
......@@ -79,20 +95,13 @@ returned with status code `401`:
}
```
### Session Cookie
### Session cookie
When signing in to GitLab as an ordinary user, a `_gitlab_session` cookie is
set. The API will use this cookie for authentication if it is present, but using
the API to generate a new session cookie is currently not supported.
### Private Tokens
You need to pass a `private_token` parameter via query string or header. If passed as a
header, the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of
an underscore). You can find or reset your private token in your account page
(`/profile/account`).
### OAuth 2 Tokens
### OAuth2 tokens
You can use an OAuth 2 token to authenticate with the API by passing it either in the
`access_token` parameter or in the `Authorization` header.
......@@ -105,30 +114,31 @@ curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api
Read more about [GitLab as an OAuth2 client](oauth2.md).
### Personal Access Tokens
### Private tokens
> [Introduced][ce-3749] in GitLab 8.8.
Private tokens provide full access to the GitLab API. Anyone with access to
them can interact with GitLab as if they were you. You can find or reset your
private token in your account page (`/profile/account`).
You can create as many personal access tokens as you like from your GitLab
profile (`/profile/personal_access_tokens`); perhaps one for each application
that needs access to the GitLab API.
For examples of usage, [read the basic usage section](#basic-usage).
Once you have your token, pass it to the API using either the `private_token`
parameter or the `PRIVATE-TOKEN` header.
### Personal access tokens
> [Introduced][ce-5951] in GitLab 8.15.
Instead of using your private token which grants full access to your account,
personal access tokens could be a better fit because of their granular
permissions.
Personal Access Tokens can be created with one or more scopes that allow various actions
that a given token can perform. Although there are only two scopes available at the
moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
Once you have your token, pass it to the API using either the `private_token`
parameter or the `PRIVATE-TOKEN` header. For examples of usage,
[read the basic usage section](#basic-usage).
At any time you can revoke any personal access token by just clicking **Revoke**.
[Read more about personal access tokens.][pat]
### Impersonation tokens
> [Introduced][ce-9099] in GitLab 9.0. Needs admin permissions.
Impersonation tokens are a type of [Personal Access Token](#personal-access-tokens)
Impersonation tokens are a type of [personal access token][pat]
that can only be created by an admin for a specific user.
They are a better alternative to using the user's password/private token
......@@ -137,9 +147,11 @@ or private token, since the password/token can change over time. Impersonation
tokens are a great fit if you want to build applications or tools which
authenticate with the API as a specific user.
For more information about the usage please refer to the
For more information, refer to the
[users API](users.md#retrieve-user-impersonation-tokens) docs.
For examples of usage, [read the basic usage section](#basic-usage).
### Sudo
> Needs admin permissions.
......@@ -192,11 +204,16 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v4/projects"
```
## Basic Usage
## Basic usage
API requests should be prefixed with `api` and the API version. The API version
is defined in [`lib/api.rb`][lib-api-url].
For endpoints that require [authentication](#authentication), you need to pass
a `private_token` parameter via query string or header. If passed as a header,
the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of
an underscore).
Example of a valid API request:
```
......@@ -209,6 +226,12 @@ Example of a valid API request using cURL and authentication via header:
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
```
Example of a valid API request using cURL and authentication via a query string:
```shell
curl "https://gitlab.example.com/api/v4/projects?private_token=9koXpg98eAheJpvBs5tK"
```
The API uses JSON to serialize data. You don't need to specify `.json` at the
end of an API URL.
......@@ -424,3 +447,4 @@ programming languages. Visit the [GitLab website] for a complete list.
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
[ce-9099]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9099
[pat]: ../user/profile/personal_access_tokens.md
......@@ -134,4 +134,4 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token
```
[personal access tokens]: ./README.md#personal-access-tokens
[personal access tokens]: ../user/profile/personal_access_tokens.md
# Session API
## Deprecation Notice
1. Starting in GitLab 8.11, this feature has been *disabled* for users with two-factor authentication turned on.
2. These users can access the API using [personal access tokens] instead.
---
>**Deprecation notice:**
Starting in GitLab 8.11, this feature has been **disabled** for users with
[two-factor authentication][2fa] turned on. These users can access the API
using [personal access tokens] instead.
You can login with both GitLab and LDAP credentials in order to obtain the
private token.
......@@ -52,4 +50,5 @@ Example response:
}
```
[personal access tokens]: ./README.md#personal-access-tokens
[2fa]: ../user/profile/account/two_factor_authentication.md
[personal access tokens]: ../user/profile/personal_access_tokens.md
......@@ -809,7 +809,7 @@ Example response:
It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform
both API calls and Git reads and writes. The user will not see these tokens in his profile
both API calls and Git reads and writes. The user will not see these tokens in their profile
settings page.
```
......
......@@ -282,9 +282,9 @@ which can be avoided if a different driver is used, for example `overlay`.
> **Notes:**
- This feature requires GitLab 8.8 and GitLab Runner 1.2.
- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
to pass a personal access token instead of your password in order to login to
GitLab's Container Registry.
- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
to pass a [personal access token][pat] instead of your password in order to
login to GitLab's Container Registry.
Once you've built a Docker image, you can push it up to the built-in
[GitLab Container Registry](../../user/project/container_registry.md). For example,
......@@ -409,3 +409,5 @@ Some things you should be aware of when using the Container Registry:
[docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/
[docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
[2fa]: ../../user/profile/account/two_factor_authentication.md
[pat]: ../../user/profile/personal_access_tokens.md
......@@ -27,7 +27,7 @@ download and analyze the report artifact in JSON format.
For GitLab [Enterprise Edition Starter][ee] users, this information can be automatically
extracted and shown right in the merge request widget. [Learn more on code quality
diffs in merge requests](http://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.md).
diffs in merge requests](../../user/project/merge_requests/code_quality_diff.md).
[cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
......
......@@ -125,23 +125,14 @@ applications and U2F devices.
## Personal access tokens
When 2FA is enabled, you can no longer use your normal account password to
authenticate with Git over HTTPS on the command line, you must use a personal
access token instead.
1. Log in to your GitLab account.
1. Go to your **Profile Settings**.
1. Go to **Access Tokens**.
1. Choose a name and expiry date for the token.
1. Click on **Create Personal Access Token**.
1. Save the personal access token somewhere safe.
When using Git over HTTPS on the command line, enter the personal access token
into the password field.
authenticate with Git over HTTPS on the command line or when using
[GitLab's API][api], you must use a [personal access token][pat] instead.
## Recovery options
To disable two-factor authentication on your account (for example, if you
have lost your code generation device) you can:
* [Use a saved recovery code](#use-a-saved-recovery-code)
* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-ssh)
* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account)
......@@ -154,8 +145,9 @@ codes. If you saved these codes, you can use one of them to sign in.
To use a recovery code, enter your username/email and password on the GitLab
sign-in page. When prompted for a two-factor code, enter the recovery code.
> **Note:** Once you use a recovery code, you cannot re-use it. You can still
use the other recovery codes you saved.
>**Note:**
Once you use a recovery code, you cannot re-use it. You can still use the other
recovery codes you saved.
### Generate new recovery codes using SSH
......@@ -190,11 +182,14 @@ a new set of recovery codes with SSH.
two-factor code. Then, visit your Profile Settings and add a new device
so you do not lose access to your account again.
```
3. Go to the GitLab sign-in page and enter your username/email and password. When prompted for a two-factor code, enter one of the recovery codes obtained
from the command-line output.
> **Note:** After signing in, visit your **Profile Settings -> Account** immediately to set up two-factor authentication with a new
device.
3. Go to the GitLab sign-in page and enter your username/email and password.
When prompted for a two-factor code, enter one of the recovery codes obtained
from the command-line output.
>**Note:**
After signing in, visit your **Profile settings > Account** immediately to set
up two-factor authentication with a new device.
### Ask a GitLab administrator to disable two-factor authentication on your account
......@@ -206,23 +201,23 @@ Sign in and re-enable two-factor authentication as soon as possible.
## Note to GitLab administrators
- You need to take special care to that 2FA keeps working after
[restoring a GitLab backup](../../../raketasks/backup_restore.md).
[restoring a GitLab backup](../../../raketasks/backup_restore.md).
- To ensure 2FA authorizes correctly with TOTP server, you may want to ensure
your GitLab server's time is synchronized via a service like NTP. Otherwise,
you may have cases where authorization always fails because of time differences.
[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
[FreeOTP]: https://freeotp.github.io/
[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
your GitLab server's time is synchronized via a service like NTP. Otherwise,
you may have cases where authorization always fails because of time differences.
- The GitLab U2F implementation does _not_ work when the GitLab instance is accessed from
multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at
the time of registration, and cannot be used for other hostnames/FQDNs.
multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at
the time of registration, and cannot be used for other hostnames/FQDNs.
For example, if a user is trying to access a GitLab instance from `first.host.xyz` and `second.host.xyz`:
- The user logs in via `first.host.xyz` and registers their U2F key.
- The user logs out and attempts to log in via `first.host.xyz` - U2F authentication suceeds.
- The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
- The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
the U2F key has only been registered on `first.host.xyz`.
[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
[FreeOTP]: https://freeotp.github.io/
[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
[api]: ../../../api/README.md
[pat]: ../personal_access_tokens.md
# Personal access tokens
> [Introduced][ce-3749] in GitLab 8.8.
Personal access tokens are useful if you need access to the [GitLab API][api].
Instead of using your private token which grants full access to your account,
personal access tokens could be a better fit because of their
[granular permissions](#limiting-scopes-of-a-personal-access-token).
You can also use them to authenticate against Git over HTTP. They are the only
accepted method of authentication when you have
[Two-Factor Authentication (2FA)][2fa] enabled.
Once you have your token, [pass it to the API][usage] using either the
`private_token` parameter or the `PRIVATE-TOKEN` header.
## Creating a personal access token
You can create as many personal access tokens as you like from your GitLab
profile.
1. Log in to your GitLab account.
1. Go to your **Profile settings**.
1. Go to **Access tokens**.
1. Choose a name and optionally an expiry date for the token.
1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token).
1. Click on **Create personal access token**.
1. Save the personal access token somewhere safe. Once you leave or refresh
the page, you won't be able to access it again.
![Personal access tokens page](img/personal_access_tokens.png)
## Revoking a personal access token
At any time, you can revoke any personal access token by just clicking the
respective **Revoke** button under the 'Active personal access tokens' area.
## Limiting scopes of a personal access token
Personal access tokens can be created with one or more scopes that allow various
actions that a given token can perform. The available scopes are depicted in
the following table.
| Scope | Description |
| ----- | ----------- |
|`read_user` | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed ([introduced][ce-5951] in GitLab 8.15). |
| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
[2fa]: ../account/two_factor_authentication.md
[api]: ../../api/README.md
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
[container registry]: ../project/container_registry.md
[users]: ../../api/users.md
[usage]: ../../api/README.md#basic-usage
......@@ -8,8 +8,8 @@
Registry across your GitLab instance, visit the
[administrator documentation](../../administration/container_registry.md).
- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
to pass a personal access token instead of your password in order to login to
GitLab's Container Registry.
to pass a [personal access token][pat] instead of your password in order to
login to GitLab's Container Registry.
- Multiple level image names support was added in GitLab 9.1
With the Docker Container Registry integrated into GitLab, every project can
......@@ -106,12 +106,11 @@ and [Using the GitLab Container Registry documentation](../../ci/docker/using_do
## Using with private projects
If a project is private, credentials will need to be provided for authorization.
The preferred way to do this, is by using personal access tokens, which can be
created under `/profile/personal_access_tokens`. The minimal scope needed is:
`read_registry`.
> [Introduced][ce-11845] in GitLab 9.3.
This feature was introduced in GitLab 9.3.
If a project is private, credentials will need to be provided for authorization.
The preferred way to do this, is by using [personal access tokens][pat].
The minimal scope needed is `read_registry`.
## Troubleshooting the GitLab Container Registry
......@@ -256,4 +255,6 @@ The solution: check the [IAM permissions again](https://docs.docker.com/registry
Once the right permissions were set, the error will go away.
[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
[pat]: ../profile/personal_access_tokens.md
......@@ -212,9 +212,9 @@ Container Registries for private projects.
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 with GitLab 8.12, if you have 2FA enabled in your account, you need
to pass a personal access token instead of your password in order to login to
GitLab's Container Registry.
- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
to pass a [personal access token][pat] instead of your password in order to
login to GitLab's 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
......@@ -239,3 +239,5 @@ test:
[update-docs]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
[workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
[jobenv]: ../../ci/variables/README.md#predefined-variables-environment-variables
[2fa]: ../profile/account/two_factor_authentication.md
[pat]: ../profile/personal_access_tokens.md
......@@ -11,8 +11,10 @@ Capybara.register_driver :poltergeist do |app|
js_errors: true,
timeout: timeout,
window_size: [1366, 768],
url_whitelist: %w[localhost 127.0.0.1],
url_blacklist: %w[.mp4 .png .gif .avi .bmp .jpg .jpeg],
phantomjs_options: [
'--load-images=no'
'--load-images=yes'
]
)
end
......
......@@ -2,6 +2,8 @@ module Gitlab
module Geo
class HealthCheck
def self.perform_checks
raise NotImplementedError.new('Geo is only compatible with PostgreSQL') unless Gitlab::Database.postgresql?
return '' unless Gitlab::Geo.secondary?
return 'The Geo secondary role is disabled.' unless Gitlab::Geo.secondary_role_enabled?
return 'The Geo database configuration file is missing.' unless self.geo_database_configured?
......@@ -11,7 +13,8 @@ module Gitlab
migration_version = self.get_migration_version.to_i
if database_version != migration_version
"Current Geo database version (#{database_version}) does not match latest migration (#{migration_version})."
"Current Geo database version (#{database_version}) does not match latest migration (#{migration_version}).\n"\
"You may have to run `gitlab-rake geo:db:migrate` as root on the secondary."
else
''
end
......
module RuboCop
module Cop
module RSpec
# This cop checks for single-line hook blocks
#
# @example
#
# # bad
# before { do_something }
# after(:each) { undo_something }
#
# # good
# before do
# do_something
# end
#
# after(:each) do
# undo_something
# end
class SingleLineHook < RuboCop::Cop::RSpec::Cop
MESSAGE = "Don't use single-line hook blocks.".freeze
def_node_search :rspec_hook?, <<~PATTERN
(send nil {:after :around :before} ...)
PATTERN
def on_block(node)
return unless rspec_hook?(node)
return unless node.single_line?
add_offense(node, :expression, MESSAGE)
end
end
end
end
end
......@@ -11,3 +11,4 @@ require_relative 'cop/migration/remove_concurrent_index'
require_relative 'cop/migration/remove_index'
require_relative 'cop/migration/reversible_add_column_with_default'
require_relative 'cop/migration/update_column_in_batches'
require_relative 'cop/rspec/single_line_hook'
require 'spec_helper'
describe Admin::GeoNodesController do
describe Admin::GeoNodesController, :postgresql do
shared_examples 'unlicensed geo action' do
it 'redirects to the license page' do
expect(response).to redirect_to(admin_license_path)
......
......@@ -2,7 +2,10 @@ require 'spec_helper'
describe Admin::IdentitiesController do
let(:admin) { create(:admin) }
before { sign_in(admin) }
before do
sign_in(admin)
end
describe 'UPDATE identity' do
let(:user) { create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=myuser,ou=people,dc=example,dc=com') }
......
......@@ -2,7 +2,10 @@ require 'spec_helper'
describe Admin::LicensesController do
let(:admin) { create(:admin) }
before { sign_in(admin) }
before do
sign_in(admin)
end
describe 'Upload license' do
it 'redirects back when no license is entered/uploaded' do
......
......@@ -3,7 +3,9 @@ require 'spec_helper'
describe Admin::ServicesController do
let(:admin) { create(:admin) }
before { sign_in(admin) }
before do
sign_in(admin)
end
describe 'GET #edit' do
let!(:project) { create(:empty_project) }
......
......@@ -240,7 +240,9 @@ describe AutocompleteController do
end
context 'skip_users parameter included' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'skips the user IDs passed' do
get(:users, skip_users: [user, user2].map(&:id))
......
......@@ -16,10 +16,14 @@ describe Groups::GroupMembersController do
describe 'POST create' do
let(:group_user) { create(:user) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when user does not have enough rights' do
before { group.add_developer(user) }
before do
group.add_developer(user)
end
it 'returns 403' do
post :create, group_id: group,
......@@ -32,7 +36,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
before { group.add_owner(user) }
before do
group.add_owner(user)
end
it 'adds user to members' do
post :create, group_id: group,
......@@ -59,7 +65,9 @@ describe Groups::GroupMembersController do
describe 'DELETE destroy' do
let(:member) { create(:group_member, :developer, group: group) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 403' do
......@@ -71,7 +79,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before { group.add_developer(user) }
before do
group.add_developer(user)
end
it 'returns 403' do
delete :destroy, group_id: group, id: member
......@@ -82,7 +92,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
before { group.add_owner(user) }
before do
group.add_owner(user)
end
it '[HTML] removes user from members' do
delete :destroy, group_id: group, id: member
......@@ -103,7 +115,9 @@ describe Groups::GroupMembersController do
end
describe 'DELETE leave' do
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 404' do
......@@ -115,7 +129,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'and is not an owner' do
before { group.add_developer(user) }
before do
group.add_developer(user)
end
it 'removes user from members' do
delete :leave, group_id: group
......@@ -127,7 +143,9 @@ describe Groups::GroupMembersController do
end
context 'and is an owner' do
before { group.add_owner(user) }
before do
group.add_owner(user)
end
it 'cannot removes himself from the group' do
delete :leave, group_id: group
......@@ -137,7 +155,9 @@ describe Groups::GroupMembersController do
end
context 'and is a requester' do
before { group.request_access(user) }
before do
group.request_access(user)
end
it 'removes user from members' do
delete :leave, group_id: group
......@@ -152,7 +172,9 @@ describe Groups::GroupMembersController do
end
describe 'POST request_access' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'creates a new GroupMember that is not a team member' do
post :request_access, group_id: group
......@@ -167,7 +189,9 @@ describe Groups::GroupMembersController do
describe 'POST approve_access_request' do
let(:member) { create(:group_member, :access_request, group: group) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 403' do
......@@ -179,7 +203,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before { group.add_developer(user) }
before do
group.add_developer(user)
end
it 'returns 403' do
post :approve_access_request, group_id: group, id: member
......@@ -190,7 +216,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
before { group.add_owner(user) }
before do
group.add_owner(user)
end
it 'adds user to members' do
post :approve_access_request, group_id: group, id: member
......
......@@ -94,7 +94,10 @@ describe NotificationSettingsController do
context 'not authorized' do
let(:private_project) { create(:empty_project, :private) }
before { sign_in(user) }
before do
sign_in(user)
end
it 'returns 404' do
post :create,
......@@ -120,7 +123,9 @@ describe NotificationSettingsController do
end
context 'when authorized' do
before{ sign_in(user) }
before do
sign_in(user)
end
it 'returns success' do
put :update,
......@@ -152,7 +157,9 @@ describe NotificationSettingsController do
context 'not authorized' do
let(:other_user) { create(:user) }
before { sign_in(other_user) }
before do
sign_in(other_user)
end
it 'returns 404' do
put :update,
......
......@@ -4,7 +4,9 @@ describe Profiles::PersonalAccessTokensController do
let(:user) { create(:user) }
let(:token_attributes) { attributes_for(:personal_access_token) }
before { sign_in(user) }
before do
sign_in(user)
end
describe '#create' do
def created_token
......@@ -38,7 +40,9 @@ describe Profiles::PersonalAccessTokensController do
let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
before { get :index }
before do
get :index
end
it "retrieves active personal access tokens" do
expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token)
......
......@@ -281,7 +281,9 @@ describe Projects::CommitController do
end
context 'when the path does not exist in the diff' do
before { diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ) }
before do
diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ)
end
it 'returns a 404' do
expect(response).to have_http_status(404)
......@@ -302,7 +304,9 @@ describe Projects::CommitController do
end
context 'when the commit does not exist' do
before { diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path) }
before do
diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path)
end
it 'returns a 404' do
expect(response).to have_http_status(404)
......
......@@ -128,7 +128,9 @@ describe Projects::CompareController do
end
context 'when the path does not exist in the diff' do
before { diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ) }
before do
diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ)
end
it 'returns a 404' do
expect(response).to have_http_status(404)
......@@ -149,7 +151,9 @@ describe Projects::CompareController do
end
context 'when the from ref does not exist' do
before { diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path) }
before do
diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path)
end
it 'returns a 404' do
expect(response).to have_http_status(404)
......@@ -157,7 +161,9 @@ describe Projects::CompareController do
end
context 'when the to ref does not exist' do
before { diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path) }
before do
diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path)
end
it 'returns a 404' do
expect(response).to have_http_status(404)
......
......@@ -14,7 +14,9 @@ describe Projects::ForksController do
end
context 'when fork is public' do
before { forked_project.update_attribute(:visibility_level, Project::PUBLIC) }
before do
forked_project.update_attribute(:visibility_level, Project::PUBLIC)
end
it 'is visible for non logged in users' do
get_forks
......@@ -35,7 +37,9 @@ describe Projects::ForksController do
end
context 'when user is logged in' do
before { sign_in(project.creator) }
before do
sign_in(project.creator)
end
context 'when user is not a Project member neither a group member' do
it 'does not see the Project listed' do
......@@ -46,7 +50,9 @@ describe Projects::ForksController do
end
context 'when user is a member of the Project' do
before { forked_project.team << [project.creator, :developer] }
before do
forked_project.team << [project.creator, :developer]
end
it 'sees the project listed' do
get_forks
......@@ -56,7 +62,9 @@ describe Projects::ForksController do
end
context 'when user is a member of the Group' do
before { forked_project.group.add_developer(project.creator) }
before do
forked_project.group.add_developer(project.creator)
end
it 'sees the project listed' do
get_forks
......
......@@ -22,7 +22,10 @@ describe Projects::GroupLinksController do
end
context 'when user has access to group he want to link project to' do
before { group.add_developer(user) }
before do
group.add_developer(user)
end
include_context 'link project to group'
it 'links project with selected group' do
......
......@@ -212,7 +212,9 @@ describe Projects::IssuesController do
let(:another_project) { create(:empty_project, :private) }
context 'when user has access to move issue' do
before { another_project.team << [user, :reporter] }
before do
another_project.team << [user, :reporter]
end
it 'moves issue to another project' do
move_issue
......@@ -250,14 +252,18 @@ describe Projects::IssuesController do
end
context 'when an issue is identified as spam' do
before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) }
before do
allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
end
context 'when captcha is not verified' do
def update_spam_issue
update_issue(title: 'Spam Title', description: 'Spam lives here')
end
before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) }
before do
allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false)
end
it 'rejects an issue recognized as a spam' do
expect { update_spam_issue }.not_to change{ issue.reload.title }
......@@ -619,14 +625,18 @@ describe Projects::IssuesController do
end
context 'when an issue is identified as spam' do
before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) }
before do
allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
end
context 'when captcha is not verified' do
def post_spam_issue
post_new_issue(title: 'Spam Title', description: 'Spam lives here')
end
before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) }
before do
allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false)
end
it 'rejects an issue recognized as a spam' do
expect { post_spam_issue }.not_to change(Issue, :count)
......@@ -738,7 +748,10 @@ describe Projects::IssuesController do
describe "DELETE #destroy" do
context "when the user is a developer" do
before { sign_in(user) }
before do
sign_in(user)
end
it "rejects a developer to destroy an issue" do
delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid
expect(response).to have_http_status(404)
......@@ -750,7 +763,9 @@ describe Projects::IssuesController do
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:empty_project, namespace: namespace) }
before { sign_in(owner) }
before do
sign_in(owner)
end
it "deletes the issue" do
delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid
......
......@@ -73,7 +73,7 @@ describe Projects::JobsController do
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { get_index }
expect(recorded.count).to be_within(5).of(8)
expect(recorded.count).to be_within(6).of(8)
end
def create_build(name, status)
......
......@@ -16,10 +16,14 @@ describe Projects::ProjectMembersController do
describe 'POST create' do
let(:project_user) { create(:user) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when user does not have enough rights' do
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
it 'returns 404' do
post :create, namespace_id: project.namespace,
......@@ -33,7 +37,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
before { project.team << [user, :master] }
before do
project.team << [user, :master]
end
it 'adds user to members' do
expect_any_instance_of(Members::CreateService).to receive(:execute).and_return(status: :success)
......@@ -64,7 +70,9 @@ describe Projects::ProjectMembersController do
describe 'DELETE destroy' do
let(:member) { create(:project_member, :developer, project: project) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 404' do
......@@ -78,7 +86,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
it 'returns 404' do
delete :destroy, namespace_id: project.namespace,
......@@ -91,7 +101,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
before { project.team << [user, :master] }
before do
project.team << [user, :master]
end
it '[HTML] removes user from members' do
delete :destroy, namespace_id: project.namespace,
......@@ -117,7 +129,9 @@ describe Projects::ProjectMembersController do
end
describe 'DELETE leave' do
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 404' do
......@@ -130,7 +144,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'and is not an owner' do
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
......@@ -145,7 +161,9 @@ describe Projects::ProjectMembersController do
context 'and is an owner' do
let(:project) { create(:empty_project, namespace: user.namespace) }
before { project.team << [user, :master] }
before do
project.team << [user, :master]
end
it 'does not remove himself from the project' do
delete :leave, namespace_id: project.namespace,
......@@ -156,7 +174,9 @@ describe Projects::ProjectMembersController do
end
context 'and is a requester' do
before { project.request_access(user) }
before do
project.request_access(user)
end
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
......@@ -172,7 +192,9 @@ describe Projects::ProjectMembersController do
end
describe 'POST request_access' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'creates a new ProjectMember that is not a team member' do
post :request_access, namespace_id: project.namespace,
......@@ -190,7 +212,9 @@ describe Projects::ProjectMembersController do
describe 'POST approve' do
let(:member) { create(:project_member, :access_request, project: project) }
before { sign_in(user) }
before do
sign_in(user)
end
context 'when member is not found' do
it 'returns 404' do
......@@ -204,7 +228,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
it 'returns 404' do
post :approve_access_request, namespace_id: project.namespace,
......@@ -217,7 +243,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
before { project.team << [user, :master] }
before do
project.team << [user, :master]
end
it 'adds user to members' do
post :approve_access_request, namespace_id: project.namespace,
......@@ -252,7 +280,10 @@ describe Projects::ProjectMembersController do
end
context 'when user can access source project members' do
before { another_project.team << [user, :guest] }
before do
another_project.team << [user, :guest]
end
include_context 'import applied'
it 'imports source project members' do
......
......@@ -46,7 +46,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as the author' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'renders the snippet' do
get :index, namespace_id: project.namespace, project_id: project
......@@ -57,7 +59,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as a project member' do
before { sign_in(user2) }
before do
sign_in(user2)
end
it 'renders the snippet' do
get :index, namespace_id: project.namespace, project_id: project
......@@ -317,7 +321,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as the author' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'renders the snippet' do
get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param
......@@ -328,7 +334,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as a project member' do
before { sign_in(user2) }
before do
sign_in(user2)
end
it 'renders the snippet' do
get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param
......@@ -349,7 +357,9 @@ describe Projects::SnippetsController do
end
context 'when signed in' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'responds with status 404' do
get action, namespace_id: project.namespace, project_id: project, id: 42
......
......@@ -6,7 +6,9 @@ describe Projects::TagsController do
let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') }
describe 'GET index' do
before { get :index, namespace_id: project.namespace.to_param, project_id: project }
before do
get :index, namespace_id: project.namespace.to_param, project_id: project
end
it 'returns the tags for the page' do
expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0'])
......@@ -19,7 +21,9 @@ describe Projects::TagsController do
end
describe 'GET show' do
before { get :show, namespace_id: project.namespace.to_param, project_id: project, id: id }
before do
get :show, namespace_id: project.namespace.to_param, project_id: project, id: id
end
context "valid tag" do
let(:id) { 'v1.0.0' }
......
......@@ -29,7 +29,9 @@ describe ProjectsController do
describe "GET show" do
context "user not project member" do
before { sign_in(user) }
before do
sign_in(user)
end
context "user does not have access to project" do
let(:private_project) { create(:empty_project, :private) }
......@@ -108,7 +110,9 @@ describe ProjectsController do
context "project with empty repo" do
let(:empty_project) { create(:project_empty_repo, :public) }
before { sign_in(user) }
before do
sign_in(user)
end
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
......@@ -128,7 +132,9 @@ describe ProjectsController do
context "project with broken repo" do
let(:empty_project) { create(:project_broken_repo, :public) }
before { sign_in(user) }
before do
sign_in(user)
end
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
......
......@@ -18,7 +18,9 @@ describe SearchController do
context 'on restricted projects' do
context 'when signed out' do
before { sign_out(user) }
before do
sign_out(user)
end
it "doesn't expose comments on issues" do
project = create(:empty_project, :public, :issues_private)
......
......@@ -14,7 +14,9 @@ describe SentNotificationsController, type: :controller do
describe 'GET unsubscribe' do
context 'when the user is not logged in' do
context 'when the force param is passed' do
before { get(:unsubscribe, id: sent_notification.reply_key, force: true) }
before do
get(:unsubscribe, id: sent_notification.reply_key, force: true)
end
it 'unsubscribes the user' do
expect(issue.subscribed?(user, project)).to be_falsey
......@@ -30,7 +32,9 @@ describe SentNotificationsController, type: :controller do
end
context 'when the force param is not passed' do
before { get(:unsubscribe, id: sent_notification.reply_key) }
before do
get(:unsubscribe, id: sent_notification.reply_key)
end
it 'does not unsubscribe the user' do
expect(issue.subscribed?(user, project)).to be_truthy
......@@ -47,10 +51,14 @@ describe SentNotificationsController, type: :controller do
end
context 'when the user is logged in' do
before { sign_in(user) }
before do
sign_in(user)
end
context 'when the ID passed does not exist' do
before { get(:unsubscribe, id: sent_notification.reply_key.reverse) }
before do
get(:unsubscribe, id: sent_notification.reply_key.reverse)
end
it 'does not unsubscribe the user' do
expect(issue.subscribed?(user, project)).to be_truthy
......@@ -66,7 +74,9 @@ describe SentNotificationsController, type: :controller do
end
context 'when the force param is passed' do
before { get(:unsubscribe, id: sent_notification.reply_key, force: true) }
before do
get(:unsubscribe, id: sent_notification.reply_key, force: true)
end
it 'unsubscribes the user' do
expect(issue.subscribed?(user, project)).to be_falsey
......@@ -89,7 +99,10 @@ describe SentNotificationsController, type: :controller do
end
end
let(:sent_notification) { create(:sent_notification, project: project, noteable: merge_request, recipient: user) }
before { get(:unsubscribe, id: sent_notification.reply_key) }
before do
get(:unsubscribe, id: sent_notification.reply_key)
end
it 'unsubscribes the user' do
expect(merge_request.subscribed?(user, project)).to be_falsey
......
......@@ -142,7 +142,9 @@ describe SessionsController do
end
context 'when OTP is invalid' do
before { authenticate_2fa(otp_attempt: 'invalid') }
before do
authenticate_2fa(otp_attempt: 'invalid')
end
it 'does not authenticate' do
expect(subject.current_user).not_to eq user
......@@ -169,7 +171,9 @@ describe SessionsController do
end
context 'when OTP is invalid' do
before { authenticate_2fa(otp_attempt: 'invalid') }
before do
authenticate_2fa(otp_attempt: 'invalid')
end
it 'does not authenticate' do
expect(subject.current_user).not_to eq user
......
......@@ -437,7 +437,9 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
before { get :raw, id: personal_snippet.to_param }
before do
get :raw, id: personal_snippet.to_param
end
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(personal_snippet)
......
......@@ -43,7 +43,9 @@ describe UsersController do
end
context 'when logged in' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'renders show' do
get :show, username: user.username
......@@ -62,7 +64,9 @@ describe UsersController do
end
context 'when logged in' do
before { sign_in(user) }
before do
sign_in(user)
end
it 'renders 404' do
get :show, username: 'nonexistent'
......
FactoryGirl.define do
factory :issue_link do
source factory: :issue
target factory: :issue
end
end
......@@ -134,7 +134,10 @@ describe "Admin Runners" do
describe 'runners registration token' do
let!(:token) { current_application_settings.runners_registration_token }
before { visit admin_runners_path }
before do
visit admin_runners_path
end
it 'has a registration token' do
expect(page).to have_content("Registration token is #{token}")
......
......@@ -12,7 +12,9 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
find(".table.inactive-tokens")
end
before { login_as(admin) }
before do
login_as(admin)
end
describe "token creation" do
it "allows creation of a token" do
......
......@@ -124,7 +124,10 @@ describe "Admin::Users", feature: true do
describe 'Impersonation' do
let(:another_user) { create(:user) }
before { visit admin_user_path(another_user) }
before do
visit admin_user_path(another_user)
end
context 'before impersonating' do
it 'shows impersonate button for other users' do
......@@ -149,7 +152,9 @@ describe "Admin::Users", feature: true do
end
context 'when impersonating' do
before { click_link 'Impersonate' }
before do
click_link 'Impersonate'
end
it 'logs in as the user when impersonate is clicked' do
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
......
......@@ -12,7 +12,9 @@ describe 'Dashboard Merge Requests' do
end
describe 'new merge request dropdown' do
before { visit merge_requests_dashboard_path }
before do
visit merge_requests_dashboard_path
end
it 'shows projects only with merge requests feature enabled', js: true do
find('.new-project-item-select-button').trigger('click')
......
......@@ -17,19 +17,25 @@ feature 'Project member activity', feature: true, js: true do
subject { page.find(".event-title").text }
context 'when a user joins the project' do
before { visit_activities_and_wait_with_event(Event::JOINED) }
before do
visit_activities_and_wait_with_event(Event::JOINED)
end
it { is_expected.to eq("#{user.name} joined project") }
end
context 'when a user leaves the project' do
before { visit_activities_and_wait_with_event(Event::LEFT) }
before do
visit_activities_and_wait_with_event(Event::LEFT)
end
it { is_expected.to eq("#{user.name} left project") }
end
context 'when a users membership expires for the project' do
before { visit_activities_and_wait_with_event(Event::EXPIRED) }
before do
visit_activities_and_wait_with_event(Event::EXPIRED)
end
it "presents the correct message" do
message = "#{user.name} removed due to membership expiration from project"
......
......@@ -140,7 +140,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do
end
context 'reloading the page' do
before { refresh }
before do
refresh
end
it 'collapses the large diff by default' do
expect(large_diff).not_to have_selector('.code')
......
......@@ -52,9 +52,14 @@ feature 'Edit group settings', feature: true do
given!(:project) { create(:project, group: group, path: 'project') }
given(:old_project_full_path) { "/#{group.path}/#{project.path}" }
given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
before(:context) { TestEnv.clean_test_path }
after(:example) { TestEnv.clean_test_path }
before(:context) do
TestEnv.clean_test_path
end
after(:example) do
TestEnv.clean_test_path
end
scenario 'the project is accessible via the new path' do
update_path(new_group_path)
......
......@@ -12,7 +12,9 @@ feature 'Group', feature: true do
end
describe 'create a group' do
before { visit new_group_path }
before do
visit new_group_path
end
describe 'with space in group path' do
it 'renders new group form with validation errors' do
......@@ -138,7 +140,9 @@ feature 'Group', feature: true do
let(:path) { edit_group_path(group) }
let(:new_name) { 'new-name' }
before { visit path }
before do
visit path
end
it 'saves new settings' do
fill_in 'group_name', with: new_name
......
......@@ -34,7 +34,7 @@ describe 'Help Pages', feature: true do
end
end
context 'in a production environment with version check enabled', js: true do
context 'in a production environment with version check enabled', :js do
before do
allow(Rails.env).to receive(:production?) { true }
allow(current_application_settings).to receive(:version_check_enabled) { true }
......@@ -44,18 +44,12 @@ describe 'Help Pages', feature: true do
visit help_path
end
it 'should display a version check image' do
expect(find('.js-version-status-badge')).to be_visible
it 'has a version check image' do
expect(find('.js-version-status-badge', visible: false)['src']).to end_with('/version-check-url')
end
it 'should have a src url' do
expect(find('.js-version-status-badge')['src']).to match(/\/version-check-url/)
end
it 'should hide the version check image if the image request fails' do
# We use '--load-images=no' with poltergeist so we must trigger manually
execute_script("$('.js-version-status-badge').trigger('error');")
it 'hides the version check image if the image request fails' do
# We use '--load-images=yes' with poltergeist so the image fails to load
expect(find('.js-version-status-badge', visible: false)).not_to be_visible
end
end
......
......@@ -153,7 +153,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do
context 'when the sort in the URL is id_desc' do
let(:issuable_type) { :issue }
before { visit_issues(project, sort: 'id_desc') }
before do
visit_issues(project, sort: 'id_desc')
end
it 'shows the sort order as last created' do
expect(find('.issues-other-filters')).to have_content('Last created')
......@@ -165,7 +167,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do
context 'when the sort in the URL is id_asc' do
let(:issuable_type) { :issue }
before { visit_issues(project, sort: 'id_asc') }
before do
visit_issues(project, sort: 'id_asc')
end
it 'shows the sort order as oldest created' do
expect(find('.issues-other-filters')).to have_content('Oldest created')
......
......@@ -8,7 +8,9 @@ describe 'Issues csv', feature: true do
let(:feature_label) { create(:label, project: project, title: 'Feature') }
let!(:issue) { create(:issue, project: project, author: user) }
before { login_as(user) }
before do
login_as(user)
end
def request_csv(params = {})
visit namespace_project_issues_path(project.namespace, project, params)
......
......@@ -257,7 +257,10 @@ describe 'Issues', feature: true do
context 'with a filter on labels' do
let(:label) { create(:label, project: project) }
before { create(:label_link, label: label, target: foo) }
before do
create(:label_link, label: label, target: foo)
end
it 'sorts by least recently due date by excluding nil due dates' do
bar.update(due_date: nil)
......
......@@ -202,10 +202,12 @@ feature 'Login', feature: true do
# TODO: otp_grace_period_started_at
context 'global setting' do
before(:each) { stub_application_setting(require_two_factor_authentication: true) }
before do
stub_application_setting(require_two_factor_authentication: true)
end
context 'with grace period defined' do
before(:each) do
before do
stub_application_setting(two_factor_grace_period: 48)
login_with(user)
end
......@@ -242,7 +244,7 @@ feature 'Login', feature: true do
end
context 'without grace period defined' do
before(:each) do
before do
stub_application_setting(two_factor_grace_period: 0)
login_with(user)
end
......@@ -265,7 +267,7 @@ feature 'Login', feature: true do
end
context 'with grace period defined' do
before(:each) do
before do
stub_application_setting(two_factor_grace_period: 48)
login_with(user)
end
......@@ -306,7 +308,7 @@ feature 'Login', feature: true do
end
context 'without grace period defined' do
before(:each) do
before do
stub_application_setting(two_factor_grace_period: 0)
login_with(user)
end
......
......@@ -85,14 +85,18 @@ feature 'Merge request conflict resolution', js: true, feature: true do
context 'the conflicts are resolvable' do
let(:merge_request) { create_merge_request('conflict-resolvable') }
before { visit namespace_project_merge_request_path(project.namespace, project, merge_request) }
before do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
it 'shows a link to the conflict resolution page' do
expect(page).to have_link('conflicts', href: /\/conflicts\Z/)
end
context 'in Inline view mode' do
before { click_link('conflicts', href: /\/conflicts\Z/) }
before do
click_link('conflicts', href: /\/conflicts\Z/)
end
include_examples "conflicts are resolved in Interactive mode"
include_examples "conflicts are resolved in Edit inline mode"
......
......@@ -18,7 +18,9 @@ feature 'Merge immediately', :feature, :js do
sha: project.repository.commit('master').id)
end
before { project.team << [user, :master] }
before do
project.team << [user, :master]
end
context 'when there is active pipeline for merge request' do
background do
......
......@@ -31,8 +31,13 @@ feature 'Profile > Account', feature: true do
given(:new_project_path) { "/#{new_username}/#{project.path}" }
given(:old_project_path) { "/#{user.username}/#{project.path}" }
before(:context) { TestEnv.clean_test_path }
after(:example) { TestEnv.clean_test_path }
before(:context) do
TestEnv.clean_test_path
end
after(:example) do
TestEnv.clean_test_path
end
scenario 'the project is accessible via the new path' do
update_username(new_username)
......
......@@ -39,6 +39,7 @@ feature 'Artifact file', :js, feature: true do
context 'JPG file' do
before do
page.driver.browser.url_blacklist = []
visit_file('rails_sample.jpg')
wait_for_requests
......
......@@ -47,7 +47,9 @@ describe 'Pipeline', :feature, :js do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
before do
visit namespace_project_pipeline_path(project.namespace, project, pipeline)
end
it 'shows the pipeline graph' do
expect(page).to have_selector('.pipeline-visualization')
......@@ -164,7 +166,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_content('retried') }
context 'when retrying' do
before { find('.js-retry-button').trigger('click') }
before do
find('.js-retry-button').trigger('click')
end
it { expect(page).not_to have_content('Retry') }
end
......@@ -174,7 +178,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_selector('.ci-canceled') }
context 'when canceling' do
before { click_on 'Cancel running' }
before do
click_on 'Cancel running'
end
it { expect(page).not_to have_content('Cancel running') }
end
......@@ -226,7 +232,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_content('retried') }
context 'when retrying' do
before { find('.js-retry-button').trigger('click') }
before do
find('.js-retry-button').trigger('click')
end
it { expect(page).not_to have_content('Retry') }
end
......@@ -236,7 +244,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_selector('.ci-canceled') }
context 'when canceling' do
before { click_on 'Cancel running' }
before do
click_on 'Cancel running'
end
it { expect(page).not_to have_content('Cancel running') }
end
......
......@@ -149,7 +149,9 @@ describe 'Pipelines', :feature, :js do
create(:ci_pipeline, :invalid, project: project)
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it 'contains badge that indicates errors' do
expect(page).to have_content 'yaml invalid'
......@@ -171,7 +173,9 @@ describe 'Pipelines', :feature, :js do
commands: 'test')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it 'has a dropdown with play button' do
expect(page).to have_selector('.dropdown-toggle.btn.btn-default .icon-play')
......@@ -204,7 +208,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it 'is cancelable' do
expect(page).to have_selector('.js-pipelines-cancel-button')
......@@ -215,7 +221,9 @@ describe 'Pipelines', :feature, :js do
end
context 'when canceling' do
before { find('.js-pipelines-cancel-button').trigger('click') }
before do
find('.js-pipelines-cancel-button').trigger('click')
end
it 'indicates that pipeline was canceled' do
expect(page).not_to have_selector('.js-pipelines-cancel-button')
......@@ -255,7 +263,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it 'has artifats' do
expect(page).to have_selector('.build-artifacts')
......@@ -284,7 +294,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it { expect(page).not_to have_selector('.build-artifacts') }
end
......@@ -297,7 +309,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it { expect(page).not_to have_selector('.build-artifacts') }
end
......@@ -310,7 +324,9 @@ describe 'Pipelines', :feature, :js do
name: 'build')
end
before { visit_project_pipelines }
before do
visit_project_pipelines
end
it 'should render a mini pipeline graph' do
expect(page).to have_selector('.js-mini-pipeline-graph')
......@@ -437,7 +453,9 @@ describe 'Pipelines', :feature, :js do
end
context 'with gitlab-ci.yml' do
before { stub_ci_pipeline_to_return_yaml_file }
before do
stub_ci_pipeline_to_return_yaml_file
end
it 'creates a new pipeline' do
expect { click_on 'Create pipeline' }
......@@ -448,7 +466,9 @@ describe 'Pipelines', :feature, :js do
end
context 'without gitlab-ci.yml' do
before { click_on 'Create pipeline' }
before do
click_on 'Create pipeline'
end
it { expect(page).to have_content('Missing .gitlab-ci.yml file') }
end
......
......@@ -58,8 +58,13 @@ describe 'Edit Project Settings', feature: true do
# Not using empty project because we need a repo to exist
let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
before(:context) { TestEnv.clean_test_path }
after(:example) { TestEnv.clean_test_path }
before(:context) do
TestEnv.clean_test_path
end
after(:example) do
TestEnv.clean_test_path
end
specify 'the project is accessible via the new path' do
rename_project(project, path: 'bar')
......@@ -96,9 +101,17 @@ describe 'Edit Project Settings', feature: true do
let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
let!(:group) { create(:group) }
before(:context) { TestEnv.clean_test_path }
before(:example) { group.add_owner(user) }
after(:example) { TestEnv.clean_test_path }
before(:context) do
TestEnv.clean_test_path
end
before(:example) do
group.add_owner(user)
end
after(:example) do
TestEnv.clean_test_path
end
specify 'the project is accessible via the new path' do
transfer_project(project, group)
......
......@@ -6,7 +6,9 @@ feature 'Protected Branches', feature: true, js: true do
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
before { login_as(user) }
before do
login_as(user)
end
describe "explicit protected branches" do
it "allows creating explicit protected branches" do
......
......@@ -4,7 +4,9 @@ feature 'Projected Tags', feature: true, js: true do
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
before { login_as(user) }
before do
login_as(user)
end
def set_allowed_to(operation, option = 'Masters', form: '.new-protected-tag')
within form do
......
......@@ -4,7 +4,10 @@ describe "Runners" do
include GitlabRoutingHelper
let(:user) { create(:user) }
before { login_as(user) }
before do
login_as(user)
end
describe "specific runners" do
before do
......@@ -127,7 +130,9 @@ describe "Runners" do
end
context 'when runner has tags' do
before { runner.update_attribute(:tag_list, ['tag']) }
before do
runner.update_attribute(:tag_list, ['tag'])
end
scenario 'user wants to prevent runner from running untagged job' do
visit runners_path(project)
......
......@@ -83,7 +83,9 @@ describe "Search", feature: true do
let(:project) { create(:project, :repository) }
let(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'Bug here') }
before { note.update_attributes(commit_id: 12345678) }
before do
note.update_attributes(commit_id: 12345678)
end
it 'finds comment' do
visit namespace_project_path(project.namespace, project)
......
......@@ -358,7 +358,9 @@ describe "Internal Project Access", feature: true do
subject { namespace_project_jobs_path(project.namespace, project) }
context "when allowed for public and internal" do
before { project.update(public_builds: true) }
before do
project.update(public_builds: true)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -373,7 +375,9 @@ describe "Internal Project Access", feature: true do
end
context "when disallowed for public and internal" do
before { project.update(public_builds: false) }
before do
project.update(public_builds: false)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -394,7 +398,9 @@ describe "Internal Project Access", feature: true do
subject { namespace_project_job_path(project.namespace, project, build.id) }
context "when allowed for public and internal" do
before { project.update(public_builds: true) }
before do
project.update(public_builds: true)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -409,7 +415,9 @@ describe "Internal Project Access", feature: true do
end
context "when disallowed for public and internal" do
before { project.update(public_builds: false) }
before do
project.update(public_builds: false)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......
......@@ -165,7 +165,9 @@ describe "Public Project Access", feature: true do
subject { namespace_project_jobs_path(project.namespace, project) }
context "when allowed for public" do
before { project.update(public_builds: true) }
before do
project.update(public_builds: true)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -180,7 +182,9 @@ describe "Public Project Access", feature: true do
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
before do
project.update(public_builds: false)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -201,7 +205,9 @@ describe "Public Project Access", feature: true do
subject { namespace_project_job_path(project.namespace, project, build.id) }
context "when allowed for public" do
before { project.update(public_builds: true) }
before do
project.update(public_builds: true)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......@@ -216,7 +222,9 @@ describe "Public Project Access", feature: true do
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
before do
project.update(public_builds: false)
end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:auditor) }
......
......@@ -3,7 +3,9 @@ require 'spec_helper'
feature 'Signup', feature: true do
describe 'signup with no errors' do
context "when sending confirmation email" do
before { stub_application_setting(send_user_confirmation_email: true) }
before do
stub_application_setting(send_user_confirmation_email: true)
end
it 'creates the user account and sends a confirmation email' do
user = build(:user)
......@@ -23,7 +25,9 @@ feature 'Signup', feature: true do
end
context "when not sending confirmation email" do
before { stub_application_setting(send_user_confirmation_email: false) }
before do
stub_application_setting(send_user_confirmation_email: false)
end
it 'creates the user account and goes to dashboard' do
user = build(:user)
......
......@@ -144,7 +144,9 @@ feature 'Task Lists', feature: true do
describe 'nested tasks', js: true do
let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
before { visit_issue(project, issue) }
before do
visit_issue(project, issue)
end
it 'renders' do
expect(page).to have_selector('ul.task-list', count: 2)
......
......@@ -8,7 +8,9 @@ describe "Dashboard > User sorts todos", feature: true do
let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
let(:label_3) { create(:label, title: 'label_3', project: project, priority: 3) }
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
context 'sort options' do
let(:issue_1) { create(:issue, title: 'issue_1', project: project) }
......
......@@ -5,7 +5,10 @@ feature 'Triggers', feature: true, js: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:guest_user) { create(:user) }
before { login_as(user) }
before do
login_as(user)
end
before do
@project = create(:empty_project)
......
require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
before do
allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true)
end
def manage_two_factor_authentication
click_on 'Manage two-factor authentication'
......@@ -28,7 +30,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
end
describe 'when 2FA via OTP is disabled' do
before { user.update_attribute(:otp_required_for_login, false) }
before do
user.update_attribute(:otp_required_for_login, false)
end
it 'does not allow registering a new device' do
visit profile_account_path
......
......@@ -56,7 +56,9 @@ describe 'Unsubscribe links', feature: true do
end
context 'when logged in' do
before { login_as(recipient) }
before do
login_as(recipient)
end
it 'unsubscribes from the issue when visiting the link from the email body' do
visit body_link
......
......@@ -45,7 +45,9 @@ feature 'Users', feature: true, js: true do
end
describe 'redirect alias routes' do
before { user }
before do
expect(user).to be_persisted
end
scenario '/u/user1 redirects to user page' do
visit '/u/user1'
......
......@@ -164,7 +164,9 @@ describe IssuesFinder do
let(:params) { { label_name: [label.title, label2.title].join(',') } }
let(:label2) { create(:label, project: project2) }
before { create(:label_link, label: label2, target: issue2) }
before do
create(:label_link, label: label2, target: issue2)
end
it 'returns the unique issues with any of those labels' do
expect(issues).to contain_exactly(issue2)
......
......@@ -25,49 +25,65 @@ describe PersonalAccessTokensFinder do
end
describe 'without impersonation' do
before { params[:impersonation] = false }
before do
params[:impersonation] = false
end
it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) }
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_personal_access_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
end
end
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_impersonation_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
end
end
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it do
is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
......@@ -81,7 +97,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to be_nil }
end
......@@ -93,7 +111,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to be_nil }
end
......@@ -109,7 +129,9 @@ describe PersonalAccessTokensFinder do
let!(:other_user_expired_impersonation_token) { create(:personal_access_token, :expired, :impersonation, user: user2) }
let!(:other_user_revoked_impersonation_token) { create(:personal_access_token, :revoked, :impersonation, user: user2) }
before { params[:user] = user }
before do
params[:user] = user
end
it do
is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
......@@ -118,49 +140,65 @@ describe PersonalAccessTokensFinder do
end
describe 'without impersonation' do
before { params[:impersonation] = false }
before do
params[:impersonation] = false
end
it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) }
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_personal_access_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
end
end
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_impersonation_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
end
end
describe 'with active state' do
before { params[:state] = 'active' }
before do
params[:state] = 'active'
end
it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) }
end
describe 'with inactive state' do
before { params[:state] = 'inactive' }
before do
params[:state] = 'inactive'
end
it do
is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
......@@ -174,7 +212,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to be_nil }
end
......@@ -186,7 +226,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
before { params[:impersonation] = true }
before do
params[:impersonation] = true
end
it { is_expected.to be_nil }
end
......
......@@ -32,7 +32,9 @@ describe PersonalProjectsFinder do
end
context 'external' do
before { current_user.update_attributes(external: true) }
before do
current_user.update_attributes(external: true)
end
it { is_expected.to eq([private_project, public_project]) }
end
......
......@@ -6,7 +6,9 @@ describe TodosFinder do
let(:project) { create(:empty_project) }
let(:finder) { described_class }
before { project.team << [user, :developer] }
before do
project.team << [user, :developer]
end
describe '#sort' do
context 'by date' do
......
......@@ -259,7 +259,9 @@ describe ProjectsHelper do
end
context "when project is private" do
before { project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
it "shows only allowed options" do
helper.instance_variable_set(:@project, project)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment