Commit 7f7e71df authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into rc/ce-to-ee-monday

parents d2fb7f89 b596dd8f
...@@ -222,7 +222,7 @@ rake db:seed_fu: ...@@ -222,7 +222,7 @@ rake db:seed_fu:
paths: paths:
- log/development.log - log/development.log
karma: rake karma:
cache: cache:
paths: paths:
- vendor/ruby - vendor/ruby
...@@ -369,7 +369,7 @@ pages: ...@@ -369,7 +369,7 @@ pages:
<<: *dedicated-runner <<: *dedicated-runner
dependencies: dependencies:
- coverage - coverage
- karma - rake karma
- lint:javascript:report - lint:javascript:report
script: script:
- mv public/ .public/ - mv public/ .public/
......
...@@ -34,8 +34,12 @@ gem 'omniauth-saml', '~> 1.7.0' ...@@ -34,8 +34,12 @@ gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
<<<<<<< HEAD
gem 'gssapi', group: :kerberos gem 'gssapi', group: :kerberos
gem 'omniauth-authentiq', '~> 0.2.0' gem 'omniauth-authentiq', '~> 0.2.0'
=======
gem 'omniauth-authentiq', '~> 0.3.0'
>>>>>>> b596dd8fedd9dc8f9487e1e67a52a7b211bd956b
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6' gem 'jwt', '~> 1.5.6'
......
...@@ -472,7 +472,7 @@ GEM ...@@ -472,7 +472,7 @@ GEM
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1) omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.1)
omniauth-authentiq (0.2.2) omniauth-authentiq (0.3.0)
omniauth-oauth2 (~> 1.3, >= 1.3.1) omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6) omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0) jwt (~> 1.0)
...@@ -955,7 +955,7 @@ DEPENDENCIES ...@@ -955,7 +955,7 @@ DEPENDENCIES
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.3.2) omniauth (~> 1.3.2)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0) omniauth-authentiq (~> 0.3.0)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2) omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
......
...@@ -173,7 +173,7 @@ ...@@ -173,7 +173,7 @@
tokens.forEach((token) => { tokens.forEach((token) => {
const condition = gl.FilteredSearchTokenKeys const condition = gl.FilteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase()); .searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key); const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key; const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = ''; let tokenPath = '';
......
require('./filtered_search_token_keys');
(() => { (() => {
class FilteredSearchTokenizer { class FilteredSearchTokenizer {
static processTokens(input) { static processTokens(input) {
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)` // Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single) // Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = /(\w+):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\S+))/g; const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = []; const tokens = [];
let lastToken = null; let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => { const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
......
...@@ -148,16 +148,11 @@ header { ...@@ -148,16 +148,11 @@ header {
} }
.header-logo { .header-logo {
position: absolute; display: inline-block;
left: 50%; margin: 0 8px 0 3px;
position: relative;
top: 7px; top: 7px;
transition-duration: .3s; transition-duration: .3s;
z-index: 999;
#logo {
position: relative;
left: -50%;
}
svg, svg,
img { img {
...@@ -167,15 +162,6 @@ header { ...@@ -167,15 +162,6 @@ header {
&:hover { &:hover {
cursor: pointer; cursor: pointer;
} }
@media (max-width: $screen-xs-max) {
right: 20px;
left: auto;
#logo {
left: auto;
}
}
} }
.title { .title {
...@@ -183,7 +169,7 @@ header { ...@@ -183,7 +169,7 @@ header {
padding-right: 20px; padding-right: 20px;
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
max-width: 385px; max-width: 450px;
display: inline-block; display: inline-block;
line-height: $header-height; line-height: $header-height;
font-weight: normal; font-weight: normal;
...@@ -193,10 +179,6 @@ header { ...@@ -193,10 +179,6 @@ header {
vertical-align: top; vertical-align: top;
white-space: nowrap; white-space: nowrap;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
max-width: 300px;
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
max-width: 190px; max-width: 190px;
} }
......
@mixin fade($gradient-direction, $gradient-color) { @mixin fade($gradient-direction, $gradient-color) {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
z-index: 2; z-index: 1;
position: absolute; position: absolute;
bottom: 12px; bottom: 12px;
width: 43px; width: 43px;
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
.fa { .fa {
position: relative; position: relative;
top: 5px; top: 6px;
font-size: 18px; font-size: 18px;
} }
} }
...@@ -79,7 +79,6 @@ ...@@ -79,7 +79,6 @@
} }
&.sub-nav { &.sub-nav {
text-align: center;
background-color: $gray-normal; background-color: $gray-normal;
.container-fluid { .container-fluid {
...@@ -287,7 +286,6 @@ ...@@ -287,7 +286,6 @@
background: $gray-light; background: $gray-light;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
text-align: center;
.container-fluid { .container-fluid {
position: relative; position: relative;
...@@ -353,7 +351,7 @@ ...@@ -353,7 +351,7 @@
right: -5px; right: -5px;
.fa { .fa {
right: -7px; right: -28px;
} }
} }
...@@ -383,7 +381,7 @@ ...@@ -383,7 +381,7 @@
left: 0; left: 0;
.fa { .fa {
left: 10px; left: -4px;
} }
} }
} }
......
...@@ -222,6 +222,11 @@ ...@@ -222,6 +222,11 @@
} }
} }
.dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
.dropdown-toggle, .dropdown-toggle,
.dropdown-menu { .dropdown-menu {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
......
...@@ -268,6 +268,13 @@ ...@@ -268,6 +268,13 @@
} }
} }
.project-repo-buttons {
.project-action-button .dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
}
.split-one { .split-one {
display: inline-table; display: inline-table;
margin-right: 12px; margin-right: 12px;
...@@ -645,30 +652,24 @@ pre.light-well { ...@@ -645,30 +652,24 @@ pre.light-well {
} }
} }
.project-last-commit { .container-fluid.project-stats-container {
@media (min-width: $screen-sm-min) { @media (max-width: $screen-xs-max) {
margin-top: $gl-padding; padding: 12px 0;
} }
}
&.container-fluid { .project-last-commit {
padding-top: 12px;
padding-bottom: 12px;
background-color: $gray-light; background-color: $gray-light;
padding: 12px $gl-padding;
border: 1px solid $border-color; border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
border-right-width: 1px; margin-top: $gl-padding;
border-left-width: 1px;
}
} }
&.container-limited { @media (min-width: $screen-sm-min) {
@media (min-width: 1281px) {
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
}
.ci-status { .ci-status {
margin-right: $gl-padding; margin-right: $gl-padding;
......
...@@ -104,22 +104,14 @@ module CreatesCommit ...@@ -104,22 +104,14 @@ module CreatesCommit
if can?(current_user, :push_code, @project) if can?(current_user, :push_code, @project)
# Edit file in this project # Edit file in this project
@mr_source_project = @project @mr_source_project = @project
if @project.forked?
# Merge request from this project to fork origin
@mr_target_project = @project.forked_from_project
@mr_target_branch = @mr_target_project.repository.root_ref
else
# Merge request to this project
@mr_target_project = @project
@mr_target_branch = @ref || @target_branch
end
else else
# Merge request from fork to this project # Merge request from fork to this project
@mr_source_project = current_user.fork_of(@project) @mr_source_project = current_user.fork_of(@project)
end
# Merge request to this project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch = @ref || @target_branch @mr_target_branch = @ref || @target_branch
end
@mr_source_branch = guess_mr_source_branch @mr_source_branch = guess_mr_source_branch
end end
......
...@@ -78,6 +78,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -78,6 +78,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth handle_omniauth
end end
<<<<<<< HEAD
def kerberos_spnego def kerberos_spnego
# The internal kerberos_spnego provider is a replacement for # The internal kerberos_spnego provider is a replacement for
# omniauth-kerberos. Here we re-use the 'kerberos' provider name to ease # omniauth-kerberos. Here we re-use the 'kerberos' provider name to ease
...@@ -85,6 +86,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -85,6 +86,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# omniauth-kerberos gem and rename the internal 'kerberos_spnego' # omniauth-kerberos gem and rename the internal 'kerberos_spnego'
# provider to plain 'kerberos' and remove this special method. # provider to plain 'kerberos' and remove this special method.
oauth['provider'] = 'kerberos' oauth['provider'] = 'kerberos'
=======
def authentiq
if params['sid']
handle_service_ticket oauth['provider'], params['sid']
end
>>>>>>> b596dd8fedd9dc8f9487e1e67a52a7b211bd956b
handle_omniauth handle_omniauth
end end
......
...@@ -22,6 +22,10 @@ module Spammable ...@@ -22,6 +22,10 @@ module Spammable
delegate :ip_address, :user_agent, to: :user_agent_detail, allow_nil: true delegate :ip_address, :user_agent, to: :user_agent_detail, allow_nil: true
end end
def submittable_as_spam_by?(current_user)
current_user && current_user.admin? && submittable_as_spam?
end
def submittable_as_spam? def submittable_as_spam?
if user_agent_detail if user_agent_detail
user_agent_detail.submittable? && current_application_settings.akismet_enabled user_agent_detail.submittable? && current_application_settings.akismet_enabled
......
.page-with-sidebar{ class: page_gutter_class } .page-with-sidebar{ class: page_gutter_class }
- if defined?(nav) && nav - if defined?(nav) && nav
.layout-nav .layout-nav
.container-fluid %div{ class: container_class }
= render "layouts/nav/#{nav}" = render "layouts/nav/#{nav}"
.content-wrapper{ class: "#{layout_nav_class}" } .content-wrapper{ class: "#{layout_nav_class}" }
= yield :sub_nav = yield :sub_nav
......
...@@ -65,12 +65,12 @@ ...@@ -65,12 +65,12 @@
%div %div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%h1.title= title
.header-logo .header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
= brand_header_logo = brand_header_logo
%h1.title= title
= yield :header_content = yield :header_content
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li %li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue) = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- if @issue.submittable_as_spam? && current_user.admin? - if @issue.submittable_as_spam_by?(current_user)
%li %li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam' = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
- if can?(current_user, :update_issue, @issue) - if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam? && current_user.admin? - if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
......
...@@ -16,7 +16,8 @@ ...@@ -16,7 +16,8 @@
= render "home_panel" = render "home_panel"
- if current_user && can?(current_user, :download_code, @project) - if current_user && can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } .project-stats-container{ class: container_class }
%nav.project-stats
%ul.nav %ul.nav
%li %li
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
...@@ -77,7 +78,7 @@ ...@@ -77,7 +78,7 @@
Set up auto deploy Set up auto deploy
- if @repository.commit - if @repository.commit
.project-last-commit{ class: container_class } .project-last-commit
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
%div{ class: container_class } %div{ class: container_class }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
- if can?(current_user, :create_project_snippet, @project) - if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do
New snippet New snippet
- if @snippet.submittable_as_spam? && current_user.admin? - if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' = link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.visible-xs-block.dropdown .visible-xs-block.dropdown
...@@ -31,6 +31,6 @@ ...@@ -31,6 +31,6 @@
%li %li
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
Edit Edit
- if @snippet.submittable_as_spam? && current_user.admin? - if @snippet.submittable_as_spam_by?(current_user)
%li %li
= link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post = link_to 'Submit as spam', mark_as_spam_namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :post
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
Delete Delete
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do
New snippet New snippet
- if @snippet.submittable_as_spam? && current_user.admin? - if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' = link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
.visible-xs-block.dropdown .visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
...@@ -28,6 +28,6 @@ ...@@ -28,6 +28,6 @@
%li %li
= link_to edit_snippet_path(@snippet) do = link_to edit_snippet_path(@snippet) do
Edit Edit
- if @snippet.submittable_as_spam? && current_user.admin? - if @snippet.submittable_as_spam_by?(current_user)
%li %li
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post = link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post
---
title: Set dropdown height fixed to 250px and make it scrollable
merge_request: 9063
author:
---
title: Unify issues search behavior by always filtering when ALL labels matches
merge_request: 8849
author:
---
title: Left align navigation
merge_request:
author:
---
title: Pick up option from GDK to disable webpack dev server livereload
merge_request:
author:
---
title: Allow searching issues for strings containing colons
merge_request:
author:
---
title: Adds remote logout functionality to the Authentiq OAuth provider
merge_request: 9381
author: Alexandros Keramidas
---
title: 'API: Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`'
merge_request: 9328
author: Robert Schilling
---
title: Added documentation for permalinks to most recent build artifacts.
merge_request: 8934
author: Christian Godenschwager
---
title: GitHub Importer - Find users based on GitHub email address
merge_request: 8958
author:
---
title: Update GitLab Pages to v0.3.1
merge_request:
author:
...@@ -240,6 +240,17 @@ Devise.setup do |config| ...@@ -240,6 +240,17 @@ Devise.setup do |config|
true true
end end
end end
if provider['name'] == 'authentiq'
provider['args'][:remote_sign_out_handler] = lambda do |request|
authentiq_session = request.params['sid']
if Gitlab::OAuth::Session.valid?(:authentiq, authentiq_session)
Gitlab::OAuth::Session.destroy(:authentiq, authentiq_session)
true
else
false
end
end
end
if provider['name'] == 'shibboleth' if provider['name'] == 'shibboleth'
provider['args'][:fail_with_empty_uid] = true provider['args'][:fail_with_empty_uid] = true
......
...@@ -10,6 +10,7 @@ var ROOT_PATH = path.resolve(__dirname, '..'); ...@@ -10,6 +10,7 @@ var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production'; var IS_PRODUCTION = process.env.NODE_ENV === 'production';
var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1; var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808; var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var config = { var config = {
context: path.join(ROOT_PATH, 'app/assets/javascripts'), context: path.join(ROOT_PATH, 'app/assets/javascripts'),
...@@ -115,6 +116,7 @@ if (IS_DEV_SERVER) { ...@@ -115,6 +116,7 @@ if (IS_DEV_SERVER) {
port: DEV_SERVER_PORT, port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' }, headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only', stats: 'errors-only',
inline: DEV_SERVER_LIVERELOAD
}; };
config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath; config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath;
} }
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexToUserAgentDetail < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def change
add_concurrent_index(:user_agent_details, [:subject_id, :subject_type])
end
end
...@@ -1402,6 +1402,8 @@ ActiveRecord::Schema.define(version: 20170215200045) do ...@@ -1402,6 +1402,8 @@ ActiveRecord::Schema.define(version: 20170215200045) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
add_index "user_agent_details", ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false t.string "encrypted_password", default: "", null: false
......
...@@ -54,7 +54,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t ...@@ -54,7 +54,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits. 5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq#scopes-and-redirect-uri-configuration) for more information on scopes and modifiers. See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq#scopes-and-redirect-uri-configuration) for more information on scopes and modifiers.
6. Change 'YOUR_CLIENT_ID' and 'YOUR_CLIENT_SECRET' to the Client credentials you received in step 1. 6. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
7. Save the configuration file. 7. Save the configuration file.
......
...@@ -30,7 +30,7 @@ GET /issues?milestone=1.0.0&state=opened ...@@ -30,7 +30,7 @@ GET /issues?milestone=1.0.0&state=opened
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`| | `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title | | `milestone` | string| no | The milestone title |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
...@@ -190,7 +190,7 @@ GET /projects/:id/issues?milestone=1.0.0&state=opened ...@@ -190,7 +190,7 @@ GET /projects/:id/issues?milestone=1.0.0&state=opened
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `iid` | integer | no | Return the issue having the given `iid` | | `iid` | integer | no | Return the issue having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`| | `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title | | `milestone` | string| no | The milestone title |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
......
...@@ -616,7 +616,7 @@ Example response: ...@@ -616,7 +616,7 @@ Example response:
Unstars a given project. Returns status code `304` if the project is not starred. Unstars a given project. Returns status code `304` if the project is not starred.
``` ```
DELETE /projects/:id/star POST /projects/:id/unstar
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -624,7 +624,7 @@ DELETE /projects/:id/star ...@@ -624,7 +624,7 @@ DELETE /projects/:id/star
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | | `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
```bash ```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star" curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unstar"
``` ```
Example response: Example response:
...@@ -1202,6 +1202,7 @@ Parameters: ...@@ -1202,6 +1202,7 @@ Parameters:
| `query` | string | yes | A string contained in the project name | | `query` | string | yes | A string contained in the project name |
| `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields | | `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order | | `sort` | string | no | Return requests sorted in `asc` or `desc` order |
<<<<<<< HEAD
## Push Rules (EE only) ## Push Rules (EE only)
...@@ -1290,3 +1291,5 @@ Parameters: ...@@ -1290,3 +1291,5 @@ Parameters:
Note the JSON response differs if the push rule is available or not. If the project push rule Note the JSON response differs if the push rule is available or not. If the project push rule
is available before it is returned in the JSON response or an empty response is returned. is available before it is returned in the JSON response or an empty response is returned.
=======
>>>>>>> b596dd8fedd9dc8f9487e1e67a52a7b211bd956b
...@@ -13,6 +13,7 @@ changes are in V4: ...@@ -13,6 +13,7 @@ changes are in V4:
- Project snippets do not return deprecated field `expires_at` - Project snippets do not return deprecated field `expires_at`
- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) - Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`)
- Status 409 returned for POST `project/:id/members` when a member already exists - Status 409 returned for POST `project/:id/members` when a member already exists
- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`
- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) - Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix)
- `/licences` - `/licences`
- `/licences/:key` - `/licences/:key`
...@@ -27,3 +28,5 @@ changes are in V4: ...@@ -27,3 +28,5 @@ changes are in V4:
- Return pagination headers for all endpoints that return an array - Return pagination headers for all endpoints that return an array
- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead - Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead
- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)`
- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR)
...@@ -64,6 +64,10 @@ Libraries with the following licenses are unacceptable for use: ...@@ -64,6 +64,10 @@ Libraries with the following licenses are unacceptable for use:
- [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects. - [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
- [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU]. - [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU].
## Requesting Approval for Licenses
Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please create an issue in the [Organization Repository][Org-Repo] and cc `@gl-legal`. After a decision has been made, the original requestor is responsible for updating this document.
## Notes ## Notes
Decisions regarding the GNU GPL licenses are based on information provided by [The GNU Project][GNU-GPL-FAQ], as well as [the Open Source Initiative][OSI-GPL], which both state that linking GPL libraries makes the program itself GPL. Decisions regarding the GNU GPL licenses are based on information provided by [The GNU Project][GNU-GPL-FAQ], as well as [the Open Source Initiative][OSI-GPL], which both state that linking GPL libraries makes the program itself GPL.
...@@ -96,3 +100,6 @@ Gems which are included only in the "development" or "test" groups by Bundler ar ...@@ -96,3 +100,6 @@ Gems which are included only in the "development" or "test" groups by Bundler ar
[OSI-GPL]: https://opensource.org/faq#linking-proprietary-code [OSI-GPL]: https://opensource.org/faq#linking-proprietary-code
[OSL]: https://opensource.org/licenses/OSL-3.0 [OSL]: https://opensource.org/licenses/OSL-3.0
[OSL-GNU]: https://www.gnu.org/licenses/license-list.en.html#OSL [OSL-GNU]: https://www.gnu.org/licenses/license-list.en.html#OSL
[Org-Repo]: https://gitlab.com/gitlab-com/organization
[Acceptable-Licenses]: #acceptable-licenses
[Unacceptable-Licenses]: #unacceptable-licenses
...@@ -95,6 +95,25 @@ so we need to set some guidelines for their use going forward: ...@@ -95,6 +95,25 @@ so we need to set some guidelines for their use going forward:
[lets-not]: https://robots.thoughtbot.com/lets-not [lets-not]: https://robots.thoughtbot.com/lets-not
### Time-sensitive tests
[Timecop](https://github.com/travisjeffery/timecop) is available in our
Ruby-based tests for verifying things that are time-sensitive. Any test that
exercises or verifies something time-sensitive should make use of Timecop to
prevent transient test failures.
Example:
```ruby
it 'is overdue' do
issue = build(:issue, due_date: Date.tomorrow)
Timecop.freeze(3.days.from_now) do
expect(issue).to be_overdue
end
end
```
### Test speed ### Test speed
GitLab has a massive test suite that, without parallelization, can take more GitLab has a massive test suite that, without parallelization, can take more
......
This diff is collapsed.
...@@ -90,18 +90,43 @@ inside GitLab that make that possible. ...@@ -90,18 +90,43 @@ inside GitLab that make that possible.
It is possible to download the latest artifacts of a job via a well known URL It is possible to download the latest artifacts of a job via a well known URL
so you can use it for scripting purposes. so you can use it for scripting purposes.
The structure of the URL is the following: The structure of the URL to download the whole artifacts archive is the following:
``` ```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<job_name> https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<job_name>
``` ```
For example, to download the latest artifacts of the job named `rspec 6 20` of To download a single file from the artifacts use the following URL:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/file/<path_to_file>?job=<job_name>
```
For example, to download the latest artifacts of the job named `coverage` of
the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org` the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org`
namespace, the URL would be: namespace, the URL would be:
``` ```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=rspec+6+20 https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=coverage
```
To download the file `coverage/index.html` from the same
artifacts use the following URL:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/file/coverage/index.html?job=coverage
```
There is also a URL to browse the latest job artifacts:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/browse?job=<job_name>
```
For example:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/browse?job=coverage
``` ```
The latest builds are also exposed in the UI in various places. Specifically, The latest builds are also exposed in the UI in various places. Specifically,
......
...@@ -5,6 +5,7 @@ Feature: Revert Merge Requests ...@@ -5,6 +5,7 @@ Feature: Revert Merge Requests
And I am signed in as a developer of the project And I am signed in as a developer of the project
And I am on the Merge Request detail page And I am on the Merge Request detail page
And I click on Accept Merge Request And I click on Accept Merge Request
And I am on the Merge Request detail page
@javascript @javascript
Scenario: I revert a merge request Scenario: I revert a merge request
......
...@@ -10,17 +10,9 @@ module API ...@@ -10,17 +10,9 @@ module API
args.delete(:id) args.delete(:id)
args[:milestone_title] = args.delete(:milestone) args[:milestone_title] = args.delete(:milestone)
args[:label_name] = args.delete(:labels)
match_all_labels = args.delete(:match_all_labels) issues = IssuesFinder.new(current_user, args).execute
labels = args.delete(:labels)
args[:label_name] = labels if match_all_labels
issues = IssuesFinder.new(current_user, args).execute.inc_notes_with_associations
# TODO: Remove in 9.0 pass `label_name: args.delete(:labels)` to IssuesFinder
if !match_all_labels && labels.present?
issues = issues.includes(:labels).where('labels.title' => labels.split(','))
end
issues.reorder(args[:order_by] => args[:sort]) issues.reorder(args[:order_by] => args[:sort])
end end
...@@ -79,7 +71,7 @@ module API ...@@ -79,7 +71,7 @@ module API
get ":id/issues" do get ":id/issues" do
group = find_group!(params[:id]) group = find_group!(params[:id])
issues = find_issues(group_id: group.id, state: params[:state] || 'opened', match_all_labels: true) issues = find_issues(group_id: group.id, state: params[:state] || 'opened')
present paginate(issues), with: Entities::Issue, current_user: current_user present paginate(issues), with: Entities::Issue, current_user: current_user
end end
......
...@@ -272,7 +272,7 @@ module API ...@@ -272,7 +272,7 @@ module API
desc 'Unstar a project' do desc 'Unstar a project' do
success Entities::Project success Entities::Project
end end
delete ':id/star' do post ':id/unstar' do
if current_user.starred?(user_project) if current_user.starred?(user_project)
current_user.toggle_star(user_project) current_user.toggle_star(user_project)
user_project.reload user_project.reload
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class BaseFormatter class BaseFormatter
attr_reader :formatter, :project, :raw_data attr_reader :client, :formatter, :project, :raw_data
def initialize(project, raw_data) def initialize(project, raw_data, client = nil)
@project = project @project = project
@raw_data = raw_data @raw_data = raw_data
@client = client
@formatter = Gitlab::ImportFormatter.new @formatter = Gitlab::ImportFormatter.new
end end
...@@ -18,19 +19,6 @@ module Gitlab ...@@ -18,19 +19,6 @@ module Gitlab
def url def url
raw_data.url || '' raw_data.url || ''
end end
private
def gitlab_user_id(github_id)
User.joins(:identities).
find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s).
try(:id)
end
def gitlab_author_id
return @gitlab_author_id if defined?(@gitlab_author_id)
@gitlab_author_id = gitlab_user_id(raw_data.user.id)
end
end end
end end
end end
...@@ -10,6 +10,7 @@ module Gitlab ...@@ -10,6 +10,7 @@ module Gitlab
@access_token = access_token @access_token = access_token
@host = host.to_s.sub(%r{/+\z}, '') @host = host.to_s.sub(%r{/+\z}, '')
@api_version = api_version @api_version = api_version
@users = {}
if access_token if access_token
::Octokit.auto_paginate = false ::Octokit.auto_paginate = false
...@@ -64,6 +65,13 @@ module Gitlab ...@@ -64,6 +65,13 @@ module Gitlab
api.respond_to?(method) || super api.respond_to?(method) || super
end end
def user(login)
return nil unless login.present?
return @users[login] if @users.key?(login)
@users[login] = api.user(login)
end
private private
def api_endpoint def api_endpoint
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class CommentFormatter < BaseFormatter class CommentFormatter < BaseFormatter
attr_writer :author_id
def attributes def attributes
{ {
project: project, project: project,
...@@ -17,11 +19,11 @@ module Gitlab ...@@ -17,11 +19,11 @@ module Gitlab
private private
def author def author
raw_data.user.login @author ||= UserFormatter.new(client, raw_data.user)
end end
def author_id def author_id
gitlab_author_id || project.creator_id author.gitlab_id || project.creator_id
end end
def body def body
...@@ -52,10 +54,10 @@ module Gitlab ...@@ -52,10 +54,10 @@ module Gitlab
end end
def note def note
if gitlab_author_id if author.gitlab_id
body body
else else
formatter.author_line(author) + body formatter.author_line(author.login) + body
end end
end end
......
...@@ -110,7 +110,7 @@ module Gitlab ...@@ -110,7 +110,7 @@ module Gitlab
def import_issues def import_issues
fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues| fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues|
issues.each do |raw| issues.each do |raw|
gh_issue = IssueFormatter.new(project, raw) gh_issue = IssueFormatter.new(project, raw, client)
begin begin
issuable = issuable =
...@@ -131,7 +131,8 @@ module Gitlab ...@@ -131,7 +131,8 @@ module Gitlab
def import_pull_requests def import_pull_requests
fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests| fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests|
pull_requests.each do |raw| pull_requests.each do |raw|
gh_pull_request = PullRequestFormatter.new(project, raw) gh_pull_request = PullRequestFormatter.new(project, raw, client)
next unless gh_pull_request.valid? next unless gh_pull_request.valid?
begin begin
...@@ -209,13 +210,15 @@ module Gitlab ...@@ -209,13 +210,15 @@ module Gitlab
ActiveRecord::Base.no_touching do ActiveRecord::Base.no_touching do
comments.each do |raw| comments.each do |raw|
begin begin
comment = CommentFormatter.new(project, raw) comment = CommentFormatter.new(project, raw, client)
# GH does not return info about comment's parent, so we guess it by checking its URL! # GH does not return info about comment's parent, so we guess it by checking its URL!
*_, parent, iid = URI(raw.html_url).path.split('/') *_, parent, iid = URI(raw.html_url).path.split('/')
if parent == 'issues'
issuable = Issue.find_by(project_id: project.id, iid: iid) issuable = if parent == 'issues'
Issue.find_by(project_id: project.id, iid: iid)
else else
issuable = MergeRequest.find_by(target_project_id: project.id, iid: iid) MergeRequest.find_by(target_project_id: project.id, iid: iid)
end end
next unless issuable next unless issuable
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class IssuableFormatter < BaseFormatter class IssuableFormatter < BaseFormatter
attr_writer :assignee_id, :author_id
def project_association def project_association
raise NotImplementedError raise NotImplementedError
end end
...@@ -23,18 +25,24 @@ module Gitlab ...@@ -23,18 +25,24 @@ module Gitlab
raw_data.assignee.present? raw_data.assignee.present?
end end
def assignee_id def author
if assigned? @author ||= UserFormatter.new(client, raw_data.user)
gitlab_user_id(raw_data.assignee.id)
end end
def author_id
@author_id ||= author.gitlab_id || project.creator_id
end end
def author def assignee
raw_data.user.login if assigned?
@assignee ||= UserFormatter.new(client, raw_data.assignee)
end
end end
def author_id def assignee_id
gitlab_author_id || project.creator_id return @assignee_id if defined?(@assignee_id)
@assignee_id = assignee.try(:gitlab_id)
end end
def body def body
...@@ -42,10 +50,10 @@ module Gitlab ...@@ -42,10 +50,10 @@ module Gitlab
end end
def description def description
if gitlab_author_id if author.gitlab_id
body body
else else
formatter.author_line(author) + body formatter.author_line(author.login) + body
end end
end end
......
module Gitlab
module GithubImport
class UserFormatter
attr_reader :client, :raw
delegate :id, :login, to: :raw, allow_nil: true
def initialize(client, raw)
@client = client
@raw = raw
end
def gitlab_id
return @gitlab_id if defined?(@gitlab_id)
@gitlab_id = find_by_external_uid || find_by_email
end
private
def email
@email ||= client.user(raw.login).try(:email)
end
def find_by_email
return nil unless email
User.find_by_any_email(email)
.try(:id)
end
def find_by_external_uid
return nil unless id
identities = ::Identity.arel_table
User.select(:id)
.joins(:identities).where(identities[:provider].eq(:github)
.and(identities[:extern_uid].eq(id)))
.first
.try(:id)
end
end
end
end
...@@ -86,18 +86,32 @@ describe Projects::BlobController do ...@@ -86,18 +86,32 @@ describe Projects::BlobController do
end end
context 'when user has forked project' do context 'when user has forked project' do
let(:guest) { create(:user) } let(:forked_project_link) { create(:forked_project_link, forked_from_project: project) }
let!(:forked_project) { Projects::ForkService.new(project, guest).execute } let!(:forked_project) { forked_project_link.forked_to_project }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: "fork-test-1", target_branch: "master") } let(:guest) { forked_project.owner }
before { sign_in(guest) } before do
sign_in(guest)
end
context 'when editing on the fork' do
before do
default_params[:namespace_id] = forked_project.namespace.to_param
default_params[:project_id] = forked_project.to_param
end
it 'redirects to blob' do
put :update, default_params
expect(response).to redirect_to(namespace_project_blob_path(forked_project.namespace, forked_project, 'master/CHANGELOG'))
end
end
context 'when editing on the original repository' do
it "redirects to forked project new merge request" do it "redirects to forked project new merge request" do
default_params[:target_branch] = "fork-test-1" default_params[:target_branch] = "fork-test-1"
default_params[:create_merge_request] = 1 default_params[:create_merge_request] = 1
allow_any_instance_of(Files::UpdateService).to receive(:commit).and_return(:success)
put :update, default_params put :update, default_params
expect(response).to redirect_to( expect(response).to redirect_to(
...@@ -115,4 +129,5 @@ describe Projects::BlobController do ...@@ -115,4 +129,5 @@ describe Projects::BlobController do
end end
end end
end end
end
end end
...@@ -99,6 +99,29 @@ require('~/filtered_search/filtered_search_tokenizer'); ...@@ -99,6 +99,29 @@ require('~/filtered_search/filtered_search_tokenizer');
expect(results.tokens[2].value).toBe('Doing'); expect(results.tokens[2].value).toBe('Doing');
expect(results.tokens[2].symbol).toBe('~'); expect(results.tokens[2].symbol).toBe('~');
}); });
it('returns search value for invalid tokens', () => {
const results = gl.FilteredSearchTokenizer.processTokens('fake:token');
expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token');
expect(results.tokens.length).toEqual(0);
});
it('returns search value and token for mix of valid and invalid tokens', () => {
const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token');
expect(results.tokens.length).toEqual(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('real');
expect(results.tokens[0].symbol).toBe('');
expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token');
});
it('returns search value for invalid symbols', () => {
const results = gl.FilteredSearchTokenizer.processTokens('std::includes');
expect(results.lastToken).toBe('std::includes');
expect(results.searchToken).toBe('std::includes');
});
}); });
}); });
})(); })();
require 'spec_helper' require 'spec_helper'
describe Gitlab::GithubImport::CommentFormatter, lib: true do describe Gitlab::GithubImport::CommentFormatter, lib: true do
let(:client) { double }
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:octocat) { double(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') }
let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') }
let(:base) do let(:base) do
...@@ -16,7 +17,11 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do ...@@ -16,7 +17,11 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
} }
end end
subject(:comment) { described_class.new(project, raw)} subject(:comment) { described_class.new(project, raw, client) }
before do
allow(client).to receive(:user).and_return(octocat)
end
describe '#attributes' do describe '#attributes' do
context 'when do not reference a portion of the diff' do context 'when do not reference a portion of the diff' do
...@@ -69,8 +74,15 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do ...@@ -69,8 +74,15 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
context 'when author is a GitLab user' do context 'when author is a GitLab user' do
let(:raw) { double(base.merge(user: octocat)) } let(:raw) { double(base.merge(user: octocat)) }
it 'returns GitLab user id as author_id' do it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat.email)
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
end end
......
...@@ -44,6 +44,7 @@ describe Gitlab::GithubImport::Importer, lib: true do ...@@ -44,6 +44,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound)
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error)
allow_any_instance_of(Octokit::Client).to receive(:user).and_return(octocat)
allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2])
allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone])
allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2])
...@@ -53,7 +54,8 @@ describe Gitlab::GithubImport::Importer, lib: true do ...@@ -53,7 +54,8 @@ describe Gitlab::GithubImport::Importer, lib: true do
allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil }))
allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
end end
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:label1) do let(:label1) do
...@@ -125,6 +127,7 @@ describe Gitlab::GithubImport::Importer, lib: true do ...@@ -125,6 +127,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
) )
end end
let!(:user) { create(:user, email: octocat.email) }
let(:repository) { double(id: 1, fork: false) } let(:repository) { double(id: 1, fork: false) }
let(:source_sha) { create(:commit, project: project).id } let(:source_sha) { create(:commit, project: project).id }
let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) } let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) }
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GithubImport::IssueFormatter, lib: true do describe Gitlab::GithubImport::IssueFormatter, lib: true do
let(:client) { double }
let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) } let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { double(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
...@@ -23,7 +24,11 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -23,7 +24,11 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
} }
end end
subject(:issue) { described_class.new(project, raw_data) } subject(:issue) { described_class.new(project, raw_data, client) }
before do
allow(client).to receive(:user).and_return(octocat)
end
shared_examples 'Gitlab::GithubImport::IssueFormatter#attributes' do shared_examples 'Gitlab::GithubImport::IssueFormatter#attributes' do
context 'when issue is open' do context 'when issue is open' do
...@@ -75,11 +80,17 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -75,11 +80,17 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
expect(issue.attributes.fetch(:assignee_id)).to be_nil expect(issue.attributes.fetch(:assignee_id)).to be_nil
end end
it 'returns GitLab user id as assignee_id when is a GitLab user' do it 'returns GitLab user id associated with GitHub id as assignee_id' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id
end end
it 'returns GitLab user id associated with GitHub email as assignee_id' do
gl_user = create(:user, email: octocat.email)
expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id
end
end end
context 'when it has a milestone' do context 'when it has a milestone' do
...@@ -100,16 +111,22 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -100,16 +111,22 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
context 'when author is a GitLab user' do context 'when author is a GitLab user' do
let(:raw_data) { double(base_data.merge(user: octocat)) } let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do it 'returns project creator_id as author_id when is not a GitLab user' do
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
end end
it 'returns GitLab user id as author_id when is a GitLab user' do it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
end end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat.email)
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns description without created at tag line' do it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat.id, provider: 'github') create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GithubImport::PullRequestFormatter, lib: true do describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:client) { double }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:source_sha) { create(:commit, project: project).id } let(:source_sha) { create(:commit, project: project).id }
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
...@@ -10,7 +11,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -10,7 +11,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:target_repo) { repository } let(:target_repo) { repository }
let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha) } let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha) }
let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') } let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') }
let(:octocat) { double(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do let(:base_data) do
...@@ -32,7 +33,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -32,7 +33,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
} }
end end
subject(:pull_request) { described_class.new(project, raw_data) } subject(:pull_request) { described_class.new(project, raw_data, client) }
before do
allow(client).to receive(:user).and_return(octocat)
end
shared_examples 'Gitlab::GithubImport::PullRequestFormatter#attributes' do shared_examples 'Gitlab::GithubImport::PullRequestFormatter#attributes' do
context 'when pull request is open' do context 'when pull request is open' do
...@@ -121,26 +126,38 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -121,26 +126,38 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.attributes.fetch(:assignee_id)).to be_nil expect(pull_request.attributes.fetch(:assignee_id)).to be_nil
end end
it 'returns GitLab user id as assignee_id when is a GitLab user' do it 'returns GitLab user id associated with GitHub id as assignee_id' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id
end end
it 'returns GitLab user id associated with GitHub email as assignee_id' do
gl_user = create(:user, email: octocat.email)
expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id
end
end end
context 'when author is a GitLab user' do context 'when author is a GitLab user' do
let(:raw_data) { double(base_data.merge(user: octocat)) } let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do it 'returns project creator_id as author_id when is not a GitLab user' do
expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id
end end
it 'returns GitLab user id as author_id when is a GitLab user' do it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat.email)
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns description without created at tag line' do it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat.id, provider: 'github') create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
......
require 'spec_helper'
describe Gitlab::GithubImport::UserFormatter, lib: true do
let(:client) { double }
let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
subject(:user) { described_class.new(client, octocat) }
before do
allow(client).to receive(:user).and_return(octocat)
end
describe '#gitlab_id' do
context 'when GitHub user is a GitLab user' do
it 'return GitLab user id when user associated their account with GitHub' do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when user primary email matches GitHub email' do
gl_user = create(:user, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when any of user linked emails matches GitHub email' do
gl_user = create(:user, email: 'johndoe@example.com')
create(:email, user: gl_user, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
end
end
it 'returns nil when GitHub user is not a GitLab user' do
expect(user.gitlab_id).to be_nil
end
end
end
...@@ -14,8 +14,9 @@ describe Issue, 'Spammable' do ...@@ -14,8 +14,9 @@ describe Issue, 'Spammable' do
end end
describe 'InstanceMethods' do describe 'InstanceMethods' do
let(:issue) { build(:issue, spam: true) }
it 'should be invalid if spam' do it 'should be invalid if spam' do
issue = build(:issue, spam: true)
expect(issue.valid?).to be_falsey expect(issue.valid?).to be_falsey
end end
...@@ -29,5 +30,20 @@ describe Issue, 'Spammable' do ...@@ -29,5 +30,20 @@ describe Issue, 'Spammable' do
expect(issue.check_for_spam?).to eq(false) expect(issue.check_for_spam?).to eq(false)
end end
end end
describe '#submittable_as_spam_by?' do
let(:admin) { build(:admin) }
let(:user) { build(:user) }
before do
allow(issue).to receive(:submittable_as_spam?).and_return(true)
end
it 'tests if the user can submit spam' do
expect(issue.submittable_as_spam_by?(admin)).to be(true)
expect(issue.submittable_as_spam_by?(user)).to be(false)
expect(issue.submittable_as_spam_by?(nil)).to be_nil
end
end
end end
end end
...@@ -21,10 +21,6 @@ describe API::CommitStatuses, api: true do ...@@ -21,10 +21,6 @@ describe API::CommitStatuses, api: true do
let!(:master) { project.pipelines.create(sha: commit.id, ref: 'master') } let!(:master) { project.pipelines.create(sha: commit.id, ref: 'master') }
let!(:develop) { project.pipelines.create(sha: commit.id, ref: 'develop') } let!(:develop) { project.pipelines.create(sha: commit.id, ref: 'develop') }
it_behaves_like 'a paginated resources' do
let(:request) { get api(get_url, reporter) }
end
context "reporter user" do context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } } let(:statuses_id) { json_response.map { |status| status['id'] } }
...@@ -45,6 +41,7 @@ describe API::CommitStatuses, api: true do ...@@ -45,6 +41,7 @@ describe API::CommitStatuses, api: true do
it 'returns latest commit statuses' do it 'returns latest commit statuses' do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id) expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id)
json_response.sort_by!{ |status| status['id'] } json_response.sort_by!{ |status| status['id'] }
......
...@@ -14,10 +14,6 @@ describe API::Deployments, api: true do ...@@ -14,10 +14,6 @@ describe API::Deployments, api: true do
describe 'GET /projects/:id/deployments' do describe 'GET /projects/:id/deployments' do
context 'as member of the project' do context 'as member of the project' do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/deployments", user) }
end
it 'returns projects deployments' do it 'returns projects deployments' do
get api("/projects/#{project.id}/deployments", user) get api("/projects/#{project.id}/deployments", user)
......
...@@ -14,10 +14,6 @@ describe API::Environments, api: true do ...@@ -14,10 +14,6 @@ describe API::Environments, api: true do
describe 'GET /projects/:id/environments' do describe 'GET /projects/:id/environments' do
context 'as member of the project' do context 'as member of the project' do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/environments", user) }
end
it 'returns project environments' do it 'returns project environments' do
get api("/projects/#{project.id}/environments", user) get api("/projects/#{project.id}/environments", user)
......
...@@ -117,14 +117,20 @@ describe API::Issues, api: true do ...@@ -117,14 +117,20 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label.title]) expect(json_response.first['labels']).to eq([label.title])
end end
it 'returns an array of labeled issues when at least one label matches' do it 'returns an array of labeled issues when all labels matches' do
get api("/issues?labels=#{label.title},foo,bar", user) label_b = create(:label, title: 'foo', project: project)
label_c = create(:label, title: 'bar', project: project)
create(:label_link, label: label_b, target: issue)
create(:label_link, label: label_c, target: issue)
get api("/issues", user), labels: "#{label.title},#{label_b.title},#{label_c.title}"
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.length).to eq(1) expect(json_response.length).to eq(1)
expect(json_response.first['labels']).to eq([label.title]) expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
end end
it 'returns an empty array if no issue matches labels' do it 'returns an empty array if no issue matches labels' do
...@@ -356,6 +362,21 @@ describe API::Issues, api: true do ...@@ -356,6 +362,21 @@ describe API::Issues, api: true do
expect(json_response.length).to eq(0) expect(json_response.length).to eq(0)
end end
it 'returns an array of labeled issues when all labels matches' do
label_b = create(:label, title: 'foo', project: group_project)
label_c = create(:label, title: 'bar', project: group_project)
create(:label_link, label: label_b, target: group_issue)
create(:label_link, label: label_c, target: group_issue)
get api("#{base_url}", user), labels: "#{group_label.title},#{label_b.title},#{label_c.title}"
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
end
it 'returns an empty array if no group issue matches labels' do it 'returns an empty array if no group issue matches labels' do
get api("#{base_url}?labels=foo,bar", user) get api("#{base_url}?labels=foo,bar", user)
...@@ -549,14 +570,28 @@ describe API::Issues, api: true do ...@@ -549,14 +570,28 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label.title]) expect(json_response.first['labels']).to eq([label.title])
end end
it 'returns an array of labeled project issues where all labels match' do it 'returns an array of labeled issues when all labels matches' do
get api("#{base_url}/issues?labels=#{label.title},foo,bar", user) label_b = create(:label, title: 'foo', project: project)
label_c = create(:label, title: 'bar', project: project)
create(:label_link, label: label_b, target: issue)
create(:label_link, label: label_c, target: issue)
get api("#{base_url}/issues", user), labels: "#{label.title},#{label_b.title},#{label_c.title}"
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.length).to eq(1) expect(json_response.length).to eq(1)
expect(json_response.first['labels']).to eq([label.title]) expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
end
it 'returns an empty array if not all labels matches' do
get api("#{base_url}/issues?labels=#{label.title},foo", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end end
it 'returns an empty array if no project issue matches labels' do it 'returns an empty array if no project issue matches labels' do
......
...@@ -32,10 +32,6 @@ describe API::Notes, api: true do ...@@ -32,10 +32,6 @@ describe API::Notes, api: true do
before { project.team << [user, :reporter] } before { project.team << [user, :reporter] }
describe "GET /projects/:id/noteable/:noteable_id/notes" do describe "GET /projects/:id/noteable/:noteable_id/notes" do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) }
end
context "when noteable is an Issue" do context "when noteable is an Issue" do
it "returns an array of issue notes" do it "returns an array of issue notes" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
......
...@@ -15,15 +15,12 @@ describe API::Pipelines, api: true do ...@@ -15,15 +15,12 @@ describe API::Pipelines, api: true do
before { project.team << [user, :master] } before { project.team << [user, :master] }
describe 'GET /projects/:id/pipelines ' do describe 'GET /projects/:id/pipelines ' do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/pipelines", user) }
end
context 'authorized user' do context 'authorized user' do
it 'returns project pipelines' do it 'returns project pipelines' do
get api("/projects/#{project.id}/pipelines", user) get api("/projects/#{project.id}/pipelines", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.first['sha']).to match /\A\h{40}\z/ expect(json_response.first['sha']).to match /\A\h{40}\z/
expect(json_response.first['id']).to eq pipeline.id expect(json_response.first['id']).to eq pipeline.id
......
...@@ -1244,7 +1244,7 @@ describe API::Projects, api: true do ...@@ -1244,7 +1244,7 @@ describe API::Projects, api: true do
end end
end end
describe 'DELETE /projects/:id/star' do describe 'POST /projects/:id/unstar' do
context 'on a starred project' do context 'on a starred project' do
before do before do
user.toggle_star(project) user.toggle_star(project)
...@@ -1252,16 +1252,16 @@ describe API::Projects, api: true do ...@@ -1252,16 +1252,16 @@ describe API::Projects, api: true do
end end
it 'unstars the project' do it 'unstars the project' do
expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1) expect { post api("/projects/#{project.id}/unstar", user) }.to change { project.reload.star_count }.by(-1)
expect(response).to have_http_status(200) expect(response).to have_http_status(201)
expect(json_response['star_count']).to eq(0) expect(json_response['star_count']).to eq(0)
end end
end end
context 'on an unstarred project' do context 'on an unstarred project' do
it 'does not modify the star count' do it 'does not modify the star count' do
expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } expect { post api("/projects/#{project.id}/unstar", user) }.not_to change { project.reload.star_count }
expect(response).to have_http_status(304) expect(response).to have_http_status(304)
end end
......
...@@ -1105,14 +1105,14 @@ describe API::Users, api: true do ...@@ -1105,14 +1105,14 @@ describe API::Users, api: true do
end end
context "as a user than can see the event's project" do context "as a user than can see the event's project" do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/users/#{user.id}/events", user) }
end
context 'joined event' do context 'joined event' do
it 'returns the "joined" event' do it 'returns the "joined" event' do
get api("/users/#{user.id}/events", user) get api("/users/#{user.id}/events", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
comment_event = json_response.find { |e| e['action_name'] == 'commented on' } comment_event = json_response.find { |e| e['action_name'] == 'commented on' }
expect(comment_event['project_id'].to_i).to eq(project.id) expect(comment_event['project_id'].to_i).to eq(project.id)
......
# Specs for paginated resources.
#
# Requires an API request:
# let(:request) { get api("/projects/#{project.id}/repository/branches", user) }
shared_examples 'a paginated resources' do
before do
# Fires the request
request
end
it 'has pagination headers' do
expect(response.headers).to include('X-Total')
expect(response.headers).to include('X-Total-Pages')
expect(response.headers).to include('X-Per-Page')
expect(response.headers).to include('X-Page')
expect(response.headers).to include('X-Next-Page')
expect(response.headers).to include('X-Prev-Page')
expect(response.headers).to include('Link')
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment