Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
66690c55
Commit
66690c55
authored
Oct 01, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
566a3846
dda21b59
Changes
30
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
432 additions
and
95 deletions
+432
-95
GITALY_SERVER_VERSION
GITALY_SERVER_VERSION
+1
-1
GITLAB_WORKHORSE_VERSION
GITLAB_WORKHORSE_VERSION
+1
-1
app/assets/javascripts/incidents/components/incidents_list.vue
...ssets/javascripts/incidents/components/incidents_list.vue
+7
-1
app/assets/javascripts/incidents/constants.js
app/assets/javascripts/incidents/constants.js
+1
-0
app/assets/javascripts/lib/utils/common_utils.js
app/assets/javascripts/lib/utils/common_utils.js
+1
-0
app/assets/javascripts/pages/projects/incidents/show/index.js
...assets/javascripts/pages/projects/incidents/show/index.js
+7
-0
app/assets/javascripts/repository/index.js
app/assets/javascripts/repository/index.js
+42
-14
app/assets/javascripts/repository/queries/path_last_commit.query.graphql
...scripts/repository/queries/path_last_commit.query.graphql
+9
-0
app/assets/javascripts/sidebar/mount_sidebar.js
app/assets/javascripts/sidebar/mount_sidebar.js
+3
-3
app/controllers/projects/incidents_controller.rb
app/controllers/projects/incidents_controller.rb
+44
-0
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+1
-1
app/helpers/nav_helper.rb
app/helpers/nav_helper.rb
+2
-1
app/helpers/startupjs_helper.rb
app/helpers/startupjs_helper.rb
+13
-0
app/models/issue.rb
app/models/issue.rb
+1
-0
app/views/layouts/_startup_js.html.haml
app/views/layouts/_startup_js.html.haml
+21
-1
app/views/projects/incidents/_new_branch.html.haml
app/views/projects/incidents/_new_branch.html.haml
+1
-0
app/views/projects/incidents/show.html.haml
app/views/projects/incidents/show.html.haml
+1
-0
app/views/projects/tree/show.html.haml
app/views/projects/tree/show.html.haml
+2
-0
changelogs/unreleased/alipniagov-update-wh-to-8-49.yml
changelogs/unreleased/alipniagov-update-wh-to-8-49.yml
+5
-0
config/feature_flags/development/issues_incident_details.yml
config/feature_flags/development/issues_incident_details.yml
+7
-0
config/routes/project.rb
config/routes/project.rb
+2
-0
doc/administration/reference_architectures/25k_users.md
doc/administration/reference_architectures/25k_users.md
+1
-1
lib/gitlab/graphql/pagination/keyset/order_info.rb
lib/gitlab/graphql/pagination/keyset/order_info.rb
+7
-0
spec/controllers/projects/incidents_controller_spec.rb
spec/controllers/projects/incidents_controller_spec.rb
+98
-13
spec/features/incidents/incident_details_spec.rb
spec/features/incidents/incident_details_spec.rb
+32
-0
spec/frontend/incidents/components/incidents_list_spec.js
spec/frontend/incidents/components/incidents_list_spec.js
+61
-50
spec/helpers/startupjs_helper_spec.rb
spec/helpers/startupjs_helper_spec.rb
+20
-0
spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
+11
-0
spec/requests/api/graphql/project/issues_spec.rb
spec/requests/api/graphql/project/issues_spec.rb
+28
-7
spec/support/helpers/graphql_helpers.rb
spec/support/helpers/graphql_helpers.rb
+2
-1
No files found.
GITALY_SERVER_VERSION
View file @
66690c55
3f5e218def93024f3aafe590c22cd1b29f744105
cf1ceffbf8056281864432c8f472140fb83f5949
GITLAB_WORKHORSE_VERSION
View file @
66690c55
8.4
8
.0
8.4
9
.0
app/assets/javascripts/incidents/components/incidents_list.vue
View file @
66690c55
...
...
@@ -19,6 +19,7 @@ import Api from '~/api';
import
TimeAgoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
FilteredSearchBar
from
'
~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
'
;
import
AuthorToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/author_token.vue
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
convertToSnakeCase
}
from
'
~/lib/utils/text_utility
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
urlParamsToObject
}
from
'
~/lib/utils/common_utils
'
;
...
...
@@ -40,6 +41,7 @@ import {
TH_CREATED_AT_TEST_ID
,
TH_SEVERITY_TEST_ID
,
TH_PUBLISHED_TEST_ID
,
INCIDENT_DETAILS_PATH
,
}
from
'
../constants
'
;
const
tdClass
=
...
...
@@ -111,6 +113,7 @@ export default {
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
glFeatureFlagsMixin
()],
inject
:
[
'
projectPath
'
,
'
newIssuePath
'
,
...
...
@@ -332,7 +335,10 @@ export default {
return
Boolean
(
assignees
.
nodes
?.
length
);
},
navigateToIncidentDetails
({
iid
})
{
return
visitUrl
(
joinPaths
(
this
.
issuePath
,
iid
));
const
path
=
this
.
glFeatures
.
issuesIncidentDetails
?
joinPaths
(
this
.
issuePath
,
INCIDENT_DETAILS_PATH
)
:
this
.
issuePath
;
return
visitUrl
(
joinPaths
(
path
,
iid
));
},
handlePageChange
(
page
)
{
const
{
startCursor
,
endCursor
}
=
this
.
incidents
.
pageInfo
;
...
...
app/assets/javascripts/incidents/constants.js
View file @
66690c55
...
...
@@ -38,3 +38,4 @@ export const DEFAULT_PAGE_SIZE = 20;
export
const
TH_CREATED_AT_TEST_ID
=
{
'
data-testid
'
:
'
incident-management-created-at-sort
'
};
export
const
TH_SEVERITY_TEST_ID
=
{
'
data-testid
'
:
'
incident-management-severity-sort
'
};
export
const
TH_PUBLISHED_TEST_ID
=
{
'
data-testid
'
:
'
incident-management-published-sort
'
};
export
const
INCIDENT_DETAILS_PATH
=
'
incident
'
;
app/assets/javascripts/lib/utils/common_utils.js
View file @
66690c55
...
...
@@ -44,6 +44,7 @@ export const checkPageAndAction = (page, action) => {
return
pagePath
===
page
&&
actionPath
===
action
;
};
export
const
isInIncidentPage
=
()
=>
checkPageAndAction
(
'
issues
'
,
'
incident
'
);
export
const
isInIssuePage
=
()
=>
checkPageAndAction
(
'
issues
'
,
'
show
'
);
export
const
isInMRPage
=
()
=>
checkPageAndAction
(
'
merge_requests
'
,
'
show
'
);
export
const
isInEpicPage
=
()
=>
checkPageAndAction
(
'
epics
'
,
'
show
'
);
...
...
app/assets/javascripts/pages/projects/incidents/show/index.js
0 → 100644
View file @
66690c55
import
initRelatedIssues
from
'
~/related_issues
'
;
import
initShow
from
'
../../issues/show
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
initShow
();
initRelatedIssues
();
});
app/assets/javascripts/repository/index.js
View file @
66690c55
...
...
@@ -12,12 +12,17 @@ import { setTitle } from './utils/title';
import
{
updateFormAction
}
from
'
./utils/dom
'
;
import
{
convertObjectPropsToCamelCase
,
parseBoolean
}
from
'
../lib/utils/common_utils
'
;
import
{
__
}
from
'
../locale
'
;
import
PathLastCommitQuery
from
'
./queries/path_last_commit.query.graphql
'
;
export
default
function
setupVueRepositoryList
()
{
const
el
=
document
.
getElementById
(
'
js-tree-list
'
);
const
{
dataset
}
=
el
;
const
{
projectPath
,
projectShortPath
,
ref
,
escapedRef
,
fullName
}
=
dataset
;
const
router
=
createRouter
(
projectPath
,
escapedRef
);
const
pathRegex
=
/-
\/
tree
\/[^/]
+
\/(
.+$
)
/
;
const
matches
=
window
.
location
.
href
.
match
(
pathRegex
);
const
currentRoutePath
=
matches
?
matches
[
1
]
:
''
;
apolloProvider
.
clients
.
defaultClient
.
cache
.
writeData
({
data
:
{
...
...
@@ -29,6 +34,43 @@ export default function setupVueRepositoryList() {
},
});
const
initLastCommitApp
=
()
=>
new
Vue
({
el
:
document
.
getElementById
(
'
js-last-commit
'
),
router
,
apolloProvider
,
render
(
h
)
{
return
h
(
LastCommit
,
{
props
:
{
currentPath
:
this
.
$route
.
params
.
path
,
},
});
},
});
if
(
window
.
gl
.
startup_graphql_calls
)
{
const
query
=
window
.
gl
.
startup_graphql_calls
.
find
(
call
=>
call
.
operationName
===
'
pathLastCommit
'
,
);
query
.
fetchCall
.
then
(
res
=>
res
.
json
())
.
then
(
res
=>
{
apolloProvider
.
clients
.
defaultClient
.
writeQuery
({
query
:
PathLastCommitQuery
,
data
:
res
.
data
,
variables
:
{
projectPath
,
ref
,
path
:
currentRoutePath
,
},
});
})
.
catch
(()
=>
{})
.
finally
(()
=>
initLastCommitApp
());
}
else
{
initLastCommitApp
();
}
router
.
afterEach
(({
params
:
{
path
}
})
=>
{
setTitle
(
path
,
ref
,
fullName
);
});
...
...
@@ -77,20 +119,6 @@ export default function setupVueRepositoryList() {
});
}
// eslint-disable-next-line no-new
new
Vue
({
el
:
document
.
getElementById
(
'
js-last-commit
'
),
router
,
apolloProvider
,
render
(
h
)
{
return
h
(
LastCommit
,
{
props
:
{
currentPath
:
this
.
$route
.
params
.
path
,
},
});
},
});
const
treeHistoryLinkEl
=
document
.
getElementById
(
'
js-tree-history-link
'
);
const
{
historyLink
}
=
treeHistoryLinkEl
.
dataset
;
...
...
app/assets/javascripts/repository/queries/path_last_commit.query.graphql
View file @
66690c55
query
pathLastCommit
(
$projectPath
:
ID
!,
$path
:
String
,
$ref
:
String
!)
{
project
(
fullPath
:
$projectPath
)
{
__typename
repository
{
__typename
tree
(
path
:
$path
,
ref
:
$ref
)
{
__typename
lastCommit
{
__typename
sha
title
titleHtml
...
...
@@ -13,15 +17,20 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
authorName
authorGravatar
author
{
__typename
name
avatarUrl
webPath
}
signatureHtml
pipelines
(
ref
:
$ref
,
first
:
1
)
{
__typename
edges
{
__typename
node
{
__typename
detailedStatus
{
__typename
detailsPath
icon
tooltip
...
...
app/assets/javascripts/sidebar/mount_sidebar.js
View file @
66690c55
...
...
@@ -14,7 +14,7 @@ import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptio
import
SidebarSeverity
from
'
./components/severity/sidebar_severity.vue
'
;
import
Translate
from
'
../vue_shared/translate
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
{
isInIssuePage
,
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
isInIssuePage
,
isInIncidentPage
,
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
createFlash
from
'
~/flash
'
;
import
{
__
}
from
'
~/locale
'
;
import
labelsSelectModule
from
'
~/vue_shared/components/sidebar/labels_select_vue/store
'
;
...
...
@@ -51,7 +51,7 @@ function mountAssigneesComponent(mediator) {
projectPath
:
fullPath
,
field
:
el
.
dataset
.
field
,
signedIn
:
el
.
hasAttribute
(
'
data-signed-in
'
),
issuableType
:
isInIssuePage
()
?
'
issue
'
:
'
merge_request
'
,
issuableType
:
isInIssuePage
()
||
isInIncidentPage
()
?
'
issue
'
:
'
merge_request
'
,
},
}),
});
...
...
@@ -158,7 +158,7 @@ function mountLockComponent() {
const
initialData
=
JSON
.
parse
(
dataNode
.
innerHTML
);
let
importStore
;
if
(
isInIssuePage
())
{
if
(
isInIssuePage
()
||
isInIncidentPage
()
)
{
importStore
=
import
(
/* webpackChunkName: 'notesStore' */
'
~/notes/stores
'
).
then
(
({
store
})
=>
store
,
);
...
...
app/controllers/projects/incidents_controller.rb
View file @
66690c55
# frozen_string_literal: true
class
Projects::IncidentsController
<
Projects
::
ApplicationController
include
IssuableActions
include
Gitlab
::
Utils
::
StrongMemoize
before_action
:authorize_read_issue!
before_action
:check_feature_flag
,
only:
[
:show
]
before_action
:load_incident
,
only:
[
:show
]
before_action
do
push_frontend_feature_flag
(
:issues_incident_details
,
@project
)
end
def
index
end
private
def
incident
strong_memoize
(
:incident
)
do
incident_finder
.
execute
.
inc_relations_for_view
.
iid_in
(
params
[
:id
])
.
without_order
.
first
end
end
def
load_incident
@issue
=
incident
# needed by rendered view
return
render_404
unless
can?
(
current_user
,
:read_issue
,
incident
)
@noteable
=
incident
@note
=
incident
.
project
.
notes
.
new
(
noteable:
issuable
)
end
alias_method
:issuable
,
:incident
def
incident_finder
IssuesFinder
.
new
(
current_user
,
project_id:
@project
.
id
,
issue_types: :incident
)
end
def
serializer
IssueSerializer
.
new
(
current_user:
current_user
,
project:
incident
.
project
)
end
def
check_feature_flag
render_404
unless
Feature
.
enabled?
(
:issues_incident_details
,
@project
)
end
end
app/controllers/projects/issues_controller.rb
View file @
66690c55
...
...
@@ -239,7 +239,7 @@ class Projects::IssuesController < Projects::ApplicationController
return
@issue
if
defined?
(
@issue
)
# The Sortable default scope causes performance issues when used with find_by
@issuable
=
@noteable
=
@issue
||=
@project
.
issues
.
inc
ludes
(
author: :status
).
where
(
iid:
params
[
:id
]).
reorder
(
nil
)
.
take!
@issuable
=
@noteable
=
@issue
||=
@project
.
issues
.
inc
_relations_for_view
.
iid_in
(
params
[
:id
]).
without_order
.
take!
@note
=
@project
.
notes
.
new
(
noteable:
@issuable
)
return
render_404
unless
can?
(
current_user
,
:read_issue
,
@issue
)
...
...
app/helpers/nav_helper.rb
View file @
66690c55
...
...
@@ -55,7 +55,8 @@ module NavHelper
current_path?
(
'projects/merge_requests/conflicts#show'
)
||
current_path?
(
'issues#show'
)
||
current_path?
(
'milestones#show'
)
||
current_path?
(
'issues#designs'
)
current_path?
(
'issues#designs'
)
||
current_path?
(
'incidents#show'
)
end
def
admin_monitoring_nav_links
...
...
app/helpers/startupjs_helper.rb
0 → 100644
View file @
66690c55
# frozen_string_literal: true
module
StartupjsHelper
def
page_startup_graphql_calls
@graphql_startup_calls
end
def
add_page_startup_graphql_call
(
query
,
variables
=
{})
@graphql_startup_calls
||=
[]
query_str
=
File
.
read
(
File
.
join
(
Rails
.
root
,
"app/assets/javascripts/
#{
query
}
.query.graphql"
))
@graphql_startup_calls
<<
{
query:
query_str
,
variables:
variables
}
end
end
app/models/issue.rb
View file @
66690c55
...
...
@@ -126,6 +126,7 @@ class Issue < ApplicationRecord
scope
:counts_by_state
,
->
{
reorder
(
nil
).
group
(
:state_id
).
count
}
scope
:service_desk
,
->
{
where
(
author:
::
User
.
support_bot
)
}
scope
:inc_relations_for_view
,
->
{
includes
(
author: :status
)
}
# An issue can be uniquely identified by project_id and iid
# Takes one or more sets of composite IDs, expressed as hash-like records of
...
...
app/views/layouts/_startup_js.html.haml
View file @
66690c55
-
return
unless
page_startup_api_calls
.
present?
-
return
unless
page_startup_api_calls
.
present?
||
page_startup_graphql_calls
.
present?
=
javascript_tag
nonce:
true
do
:plain
var gl = window.gl || {};
gl.startup_calls =
#{
page_startup_api_calls
.
to_json
}
;
gl.startup_graphql_calls =
#{
page_startup_graphql_calls
.
to_json
}
;
if (gl.startup_calls && window.fetch) {
Object.keys(gl.startup_calls).forEach(apiCall => {
// fetch won’t send cookies in older browsers, unless you set the credentials init option.
...
...
@@ -14,3 +16,21 @@
};
});
}
if (gl.startup_graphql_calls && window.fetch) {
const url = `
#{
api_graphql_url
}
`
const opts = {
method: "POST",
headers: { "Content-Type": "application/json", 'X-CSRF-Token': "
#{
form_authenticity_token
}
" },
};
gl.startup_graphql_calls = gl.startup_graphql_calls.map(call => ({
operationName: call.query.match(/^query (.+)\(/)[1],
fetchCall: fetch(url, {
...opts,
credentials: 'same-origin',
body: JSON.stringify(call)
})
}))
}
app/views/projects/incidents/_new_branch.html.haml
0 → 100644
View file @
66690c55
=
render
'projects/issues/new_branch'
app/views/projects/incidents/show.html.haml
0 → 100644
View file @
66690c55
=
render
template:
'projects/issues/show'
app/views/projects/tree/show.html.haml
View file @
66690c55
-
current_route_path
=
request
.
fullpath
.
match
(
/-\/tree\/[^\/]+\/(.+$)/
).
to_a
[
1
]
-
add_page_startup_graphql_call
(
'repository/queries/path_last_commit'
,
{
projectPath:
@project
.
full_path
,
ref:
current_ref
,
currentRoutePath:
current_route_path
})
-
breadcrumb_title
_
(
"Repository"
)
-
@content_class
=
"limit-container-width"
unless
fluid_layout
...
...
changelogs/unreleased/alipniagov-update-wh-to-8-49.yml
0 → 100644
View file @
66690c55
---
title
:
Update GitLab Workhorse to v8.49.0
merge_request
:
43999
author
:
type
:
other
config/feature_flags/development/issues_incident_details.yml
0 → 100644
View file @
66690c55
---
name
:
issues_incident_details
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43459
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/257842
type
:
development
group
:
group::health
default_enabled
:
false
config/routes/project.rb
View file @
66690c55
...
...
@@ -311,6 +311,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources
:incidents
,
only:
[
:index
]
get
'issues/incident/:id'
=>
'incidents#show'
,
as: :issues_incident
namespace
:error_tracking
do
resources
:projects
,
only: :index
end
...
...
doc/administration/reference_architectures/25k_users.md
View file @
66690c55
...
...
@@ -21,7 +21,7 @@ full list of reference architectures, see
| Consul | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
| PostgreSQL | 3 | 8 vCPU, 30 GB memory | n1-standard-8 | m5.2xlarge | D8s v3 |
| PgBouncer | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
| Internal load balancing node | 1 |
2 vCPU, 1.8 GB memory | n1-highcpu-2
| c5.large | F2s v2 |
| Internal load balancing node | 1 |
4 vCPU, 3.6GB memory | n1-highcpu-4
| c5.large | F2s v2 |
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
...
...
lib/gitlab/graphql/pagination/keyset/order_info.rb
View file @
66690c55
...
...
@@ -94,6 +94,8 @@ module Gitlab
[
order_value
.
expr
.
expressions
[
0
].
name
.
to_s
,
order_value
.
direction
,
order_value
.
expr
]
elsif
ordering_by_similarity?
(
order_value
)
[
'similarity'
,
order_value
.
direction
,
order_value
.
expr
]
elsif
ordering_by_case?
(
order_value
)
[
order_value
.
expr
.
case
.
name
.
to_s
,
order_value
.
direction
,
order_value
.
expr
]
else
[
order_value
.
expr
.
name
,
order_value
.
direction
,
nil
]
end
...
...
@@ -108,6 +110,11 @@ module Gitlab
def
ordering_by_similarity?
(
order_value
)
Gitlab
::
Database
::
SimilarityScore
.
order_by_similarity?
(
order_value
)
end
# determine if ordering using CASE
def
ordering_by_case?
(
order_value
)
order_value
.
expr
.
is_a?
(
Arel
::
Nodes
::
Case
)
end
end
end
end
...
...
spec/controllers/projects/incidents_controller_spec.rb
View file @
66690c55
...
...
@@ -3,42 +3,127 @@
require
'spec_helper'
RSpec
.
describe
Projects
::
IncidentsController
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
_with_refind
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:developer
)
{
create
(
:user
)
}
let_it_be
(
:guest
)
{
create
(
:user
)
}
let_it_be
(
:anonymous
)
{
nil
}
before_all
do
project
.
add_guest
(
guest
)
project
.
add_developer
(
developer
)
end
before
do
sign_in
(
user
)
if
user
end
subject
{
make_request
}
shared_examples
'not found'
do
include_examples
'returning response status'
,
:not_found
end
shared_examples
'login required'
do
it
'redirects to the login page'
do
subject
expect
(
response
).
to
redirect_to
(
new_user_session_path
)
end
end
describe
'GET #index'
do
def
make_request
get
:index
,
params:
{
namespace_id:
project
.
namespace
,
project_id:
project
}
get
:index
,
params:
project_params
end
it
'shows the page for users with guest role'
do
sign_in
(
guest
)
make_request
let
(
:user
)
{
developer
}
it
'shows the page'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
render_template
(
:index
)
end
it
'shows the page for users with developer role'
do
sign_in
(
developer
)
make_request
context
'when user is unauthorized'
do
let
(
:user
)
{
anonymous
}
it_behaves_like
'login required'
end
context
'when user is a guest'
do
let
(
:user
)
{
guest
}
it
'shows the page'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
render_template
(
:index
)
end
end
end
describe
'GET #show'
do
def
make_request
get
:show
,
params:
project_params
(
id:
resource
)
end
let_it_be
(
:resource
)
{
create
(
:incident
,
project:
project
)
}
let
(
:user
)
{
developer
}
it
'renders incident page'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
render_template
(
:index
)
expect
(
response
).
to
render_template
(
:show
)
expect
(
assigns
(
:incident
)).
to
be_present
expect
(
assigns
(
:incident
).
author
.
association
(
:status
)).
to
be_loaded
expect
(
assigns
(
:issue
)).
to
be_present
expect
(
assigns
(
:noteable
)).
to
eq
(
assigns
(
:incident
))
end
context
'when user is unauthorized'
do
it
'redirects to the login page'
do
make_request
context
'with feature flag disabled'
do
before
do
stub_feature_flags
(
issues_incident_details:
false
)
end
it_behaves_like
'not found'
end
context
'with non existing id'
do
let
(
:resource
)
{
non_existing_record_id
}
it_behaves_like
'not found'
end
expect
(
response
).
to
redirect_to
(
new_user_session_path
)
context
'for issue'
do
let_it_be
(
:resource
)
{
create
(
:issue
,
project:
project
)
}
it_behaves_like
'not found'
end
context
'when user is a guest'
do
let
(
:user
)
{
guest
}
it
'shows the page'
do
subject
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
response
).
to
render_template
(
:show
)
end
end
context
'when unauthorized'
do
let
(
:user
)
{
anonymous
}
it_behaves_like
'login required'
end
end
private
def
project_params
(
opts
=
{})
opts
.
reverse_merge
(
namespace_id:
project
.
namespace
,
project_id:
project
)
end
end
spec/features/incidents/incident_details_spec.rb
0 → 100644
View file @
66690c55
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
'Incident details'
,
:js
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:developer
)
{
create
(
:user
)
}
let_it_be
(
:incident
)
{
create
(
:incident
,
project:
project
,
author:
developer
)
}
before_all
do
project
.
add_developer
(
developer
)
end
before
do
sign_in
(
developer
)
visit
project_issues_incident_path
(
project
,
incident
)
wait_for_requests
end
context
'when a developer+ displays the incident'
do
it
'shows the incident'
do
page
.
within
(
'.issuable-details'
)
do
expect
(
find
(
'h2'
)).
to
have_content
(
incident
.
title
)
end
end
it
'does not show design management'
do
expect
(
page
).
not_to
have_selector
(
'.js-design-management'
)
end
end
end
spec/frontend/incidents/components/incidents_list_spec.js
View file @
66690c55
...
...
@@ -28,10 +28,10 @@ import mockFilters from '../mocks/incidents_filter.json';
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
visitUrl
:
jest
.
fn
().
mockName
(
'
visitUrlMock
'
),
joinPaths
:
jest
.
fn
()
.
mockName
(
'
joinPaths
'
)
,
mergeUrlParams
:
jest
.
fn
()
.
mockName
(
'
mergeUrlParams
'
)
,
setUrlParams
:
jest
.
fn
()
.
mockName
(
'
setUrlParams
'
)
,
updateHistory
:
jest
.
fn
()
.
mockName
(
'
updateHistory
'
)
,
joinPaths
:
jest
.
fn
(),
mergeUrlParams
:
jest
.
fn
(),
setUrlParams
:
jest
.
fn
(),
updateHistory
:
jest
.
fn
(),
}));
describe
(
'
Incidents List
'
,
()
=>
{
...
...
@@ -81,12 +81,13 @@ describe('Incidents List', () => {
newIssuePath
,
incidentTemplateName
,
incidentType
,
issuePath
:
'
/project/iss
s
ues
'
,
issuePath
:
'
/project/issues
'
,
publishedAvailable
:
true
,
emptyListSvgPath
,
textQuery
:
''
,
authorUsernamesQuery
:
''
,
assigneeUsernamesQuery
:
''
,
issuesIncidentDetails
:
false
,
},
stubs
:
{
GlButton
:
true
,
...
...
@@ -182,13 +183,6 @@ describe('Incidents List', () => {
expect
(
src
).
toBe
(
avatarUrl
);
});
it
(
'
contains a link to the issue details
'
,
()
=>
{
findTableRows
()
.
at
(
0
)
.
trigger
(
'
click
'
);
expect
(
visitUrl
).
toHaveBeenCalledWith
(
joinPaths
(
`/project/isssues/`
,
mockIncidents
[
0
].
iid
));
});
it
(
'
renders a closed icon for closed incidents
'
,
()
=>
{
expect
(
findClosedIcon
().
length
).
toBe
(
mockIncidents
.
filter
(({
state
})
=>
state
===
'
closed
'
).
length
,
...
...
@@ -199,6 +193,30 @@ describe('Incidents List', () => {
it
(
'
renders severity per row
'
,
()
=>
{
expect
(
findSeverity
().
length
).
toBe
(
mockIncidents
.
length
);
});
it
(
'
contains a link to the issue details page
'
,
()
=>
{
findTableRows
()
.
at
(
0
)
.
trigger
(
'
click
'
);
expect
(
visitUrl
).
toHaveBeenCalledWith
(
joinPaths
(
`/project/issues/`
,
mockIncidents
[
0
].
iid
));
});
it
(
'
contains a link to the incident details page
'
,
async
()
=>
{
beforeEach
(()
=>
mountComponent
({
data
:
{
incidents
:
{
list
:
mockIncidents
},
incidentsCount
:
{}
},
loading
:
false
,
provide
:
{
glFeatures
:
{
issuesIncidentDetails
:
true
}
},
}),
);
findTableRows
()
.
at
(
0
)
.
trigger
(
'
click
'
);
expect
(
visitUrl
).
toHaveBeenCalledWith
(
joinPaths
(
`/project/issues/incident`
,
mockIncidents
[
0
].
iid
),
);
});
});
describe
(
'
Create Incident
'
,
()
=>
{
...
...
@@ -218,11 +236,10 @@ describe('Incidents List', () => {
);
});
it
(
'
sets button loading on click
'
,
()
=>
{
it
(
'
sets button loading on click
'
,
async
()
=>
{
findCreateIncidentBtn
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
findCreateIncidentBtn
().
attributes
(
'
loading
'
)).
toBe
(
'
true
'
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findCreateIncidentBtn
().
attributes
(
'
loading
'
)).
toBe
(
'
true
'
);
});
it
(
"
doesn't show the button when list is empty
"
,
()
=>
{
...
...
@@ -254,51 +271,47 @@ describe('Incidents List', () => {
});
describe
(
'
prevPage
'
,
()
=>
{
it
(
'
returns prevPage button
'
,
()
=>
{
it
(
'
returns prevPage button
'
,
async
()
=>
{
findPagination
().
vm
.
$emit
(
'
input
'
,
3
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findPagination
()
.
findAll
(
'
.page-item
'
)
.
at
(
0
)
.
text
(),
).
toBe
(
'
Prev
'
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findPagination
()
.
findAll
(
'
.page-item
'
)
.
at
(
0
)
.
text
(),
).
toBe
(
'
Prev
'
);
});
it
(
'
returns prevPage number
'
,
()
=>
{
it
(
'
returns prevPage number
'
,
async
()
=>
{
findPagination
().
vm
.
$emit
(
'
input
'
,
3
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
prevPage
).
toBe
(
2
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
prevPage
).
toBe
(
2
);
});
it
(
'
returns 0 when it is the first page
'
,
()
=>
{
it
(
'
returns 0 when it is the first page
'
,
async
()
=>
{
findPagination
().
vm
.
$emit
(
'
input
'
,
1
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
prevPage
).
toBe
(
0
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
prevPage
).
toBe
(
0
);
});
});
describe
(
'
nextPage
'
,
()
=>
{
it
(
'
returns nextPage button
'
,
()
=>
{
it
(
'
returns nextPage button
'
,
async
()
=>
{
findPagination
().
vm
.
$emit
(
'
input
'
,
3
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findPagination
()
.
findAll
(
'
.page-item
'
)
.
at
(
1
)
.
text
(),
).
toBe
(
'
Next
'
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findPagination
()
.
findAll
(
'
.page-item
'
)
.
at
(
1
)
.
text
(),
).
toBe
(
'
Next
'
);
});
it
(
'
returns nextPage number
'
,
()
=>
{
it
(
'
returns nextPage number
'
,
async
()
=>
{
mountComponent
({
data
:
{
incidents
:
{
...
...
@@ -312,17 +325,15 @@ describe('Incidents List', () => {
});
findPagination
().
vm
.
$emit
(
'
input
'
,
1
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
nextPage
).
toBe
(
2
);
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
nextPage
).
toBe
(
2
);
});
it
(
'
returns `null` when currentPage is already last page
'
,
()
=>
{
it
(
'
returns `null` when currentPage is already last page
'
,
async
()
=>
{
findStatusTabs
().
vm
.
$emit
(
'
input
'
,
1
);
findPagination
().
vm
.
$emit
(
'
input
'
,
1
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
nextPage
).
toBeNull
();
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
nextPage
).
toBeNull
();
});
});
...
...
spec/helpers/startupjs_helper_spec.rb
0 → 100644
View file @
66690c55
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
StartupjsHelper
do
describe
'#page_startup_graphql_calls'
do
let
(
:query_location
)
{
'repository/queries/path_last_commit'
}
let
(
:query_content
)
do
File
.
read
(
File
.
join
(
Rails
.
root
,
'app/assets/javascripts'
,
"
#{
query_location
}
.query.graphql"
))
end
it
'returns an array containing GraphQL Page Startup Calls'
do
helper
.
add_page_startup_graphql_call
(
query_location
,
{
ref:
'foo'
})
startup_graphql_calls
=
helper
.
page_startup_graphql_calls
expect
(
startup_graphql_calls
).
to
include
({
query:
query_content
,
variables:
{
ref:
'foo'
}
})
end
end
end
spec/lib/gitlab/graphql/pagination/keyset/order_info_spec.rb
View file @
66690c55
...
...
@@ -63,6 +63,17 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::OrderInfo do
expect
(
order_list
.
first
.
sort_direction
).
to
eq
:desc
end
end
context
'when ordering by CASE'
,
:aggregate_failuers
do
let
(
:relation
)
{
Project
.
order
(
Arel
::
Nodes
::
Case
.
new
(
Project
.
arel_table
[
:pending_delete
]).
when
(
true
).
then
(
100
).
else
(
1000
).
asc
)
}
it
'assigns the right attribute name, named function, and direction'
do
expect
(
order_list
.
count
).
to
eq
1
expect
(
order_list
.
first
.
attribute_name
).
to
eq
'pending_delete'
expect
(
order_list
.
first
.
named_function
).
to
be_kind_of
(
Arel
::
Nodes
::
Case
)
expect
(
order_list
.
first
.
sort_direction
).
to
eq
:asc
end
end
end
describe
'#validate_ordering'
do
...
...
spec/requests/api/graphql/project/issues_spec.rb
View file @
66690c55
...
...
@@ -53,16 +53,37 @@ RSpec.describe 'getting an issue list for a project' do
context
'when limiting the number of results'
do
let
(
:query
)
do
graphql_query_for
(
'project'
,
{
'fullPath'
=>
project
.
full_path
},
"issues(first: 1) {
#{
fields
}
}"
)
<<~
GQL
query($path: ID!, $n: Int) {
project(fullPath: $path) {
issues(first: $n) {
#{
fields
}
}
}
}
GQL
end
let
(
:issue_limit
)
{
1
}
let
(
:variables
)
do
{
path:
project
.
full_path
,
n:
issue_limit
}
end
it_behaves_like
'a working graphql query'
do
before
do
post_graphql
(
query
,
current_user:
current_user
)
post_graphql
(
query
,
current_user:
current_user
,
variables:
variables
)
end
it
'only returns N issues'
do
expect
(
issues_data
.
size
).
to
eq
(
issue_limit
)
end
end
context
'no limit is provided'
do
let
(
:issue_limit
)
{
nil
}
it
'returns all issues'
do
post_graphql
(
query
,
current_user:
current_user
,
variables:
variables
)
expect
(
issues_data
.
size
).
to
be
>
1
end
end
...
...
@@ -71,7 +92,7 @@ RSpec.describe 'getting an issue list for a project' do
# Newest first, we only want to see the newest checked
expect
(
Ability
).
not_to
receive
(
:allowed?
).
with
(
current_user
,
:read_issue
,
issues
.
first
)
post_graphql
(
query
,
current_user:
current_user
)
post_graphql
(
query
,
current_user:
current_user
,
variables:
variables
)
end
end
...
...
spec/support/helpers/graphql_helpers.rb
View file @
66690c55
...
...
@@ -234,7 +234,8 @@ module GraphqlHelpers
end
def
post_graphql
(
query
,
current_user:
nil
,
variables:
nil
,
headers:
{})
post
api
(
'/'
,
current_user
,
version:
'graphql'
),
params:
{
query:
query
,
variables:
variables
},
headers:
headers
params
=
{
query:
query
,
variables:
variables
&
.
to_json
}
post
api
(
'/'
,
current_user
,
version:
'graphql'
),
params:
params
,
headers:
headers
end
def
post_graphql_mutation
(
mutation
,
current_user:
nil
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment