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
3029720c
Commit
3029720c
authored
Nov 02, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
24423a9a
c0c28d30
Changes
36
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
476 additions
and
185 deletions
+476
-185
app/assets/javascripts/diffs/components/diff_file_row.vue
app/assets/javascripts/diffs/components/diff_file_row.vue
+1
-0
app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
...ascripts/pages/projects/pipeline_schedules/index/index.js
+21
-13
app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
...chedules/shared/components/pipeline_schedules_callout.vue
+4
-5
app/assets/javascripts/vue_shared/components/file_row.vue
app/assets/javascripts/vue_shared/components/file_row.vue
+11
-3
app/assets/javascripts/vue_shared/components/file_row_header.vue
...ets/javascripts/vue_shared/components/file_row_header.vue
+5
-9
app/assets/javascripts/whats_new/components/app.vue
app/assets/javascripts/whats_new/components/app.vue
+54
-18
app/assets/javascripts/whats_new/store/actions.js
app/assets/javascripts/whats_new/store/actions.js
+29
-4
app/assets/javascripts/whats_new/store/mutation_types.js
app/assets/javascripts/whats_new/store/mutation_types.js
+4
-1
app/assets/javascripts/whats_new/store/mutations.js
app/assets/javascripts/whats_new/store/mutations.js
+11
-2
app/assets/javascripts/whats_new/store/state.js
app/assets/javascripts/whats_new/store/state.js
+6
-1
app/assets/javascripts/whats_new/utils/get_drawer_body_height.js
...ets/javascripts/whats_new/utils/get_drawer_body_height.js
+6
-0
app/assets/stylesheets/components/whats_new.scss
app/assets/stylesheets/components/whats_new.scss
+5
-0
app/controllers/whats_new_controller.rb
app/controllers/whats_new_controller.rb
+21
-2
app/helpers/whats_new_helper.rb
app/helpers/whats_new_helper.rb
+2
-4
app/views/projects/pipeline_schedules/index.html.haml
app/views/projects/pipeline_schedules/index.html.haml
+1
-1
changelogs/unreleased/ph-25973-mergeRequestSidebarLongPathTruncating.yml
...leased/ph-25973-mergeRequestSidebarLongPathTruncating.yml
+5
-0
doc/.vale/gitlab/AlertBoxCaution.yml
doc/.vale/gitlab/AlertBoxCaution.yml
+1
-0
doc/.vale/gitlab/AlertBoxDanger.yml
doc/.vale/gitlab/AlertBoxDanger.yml
+1
-0
doc/.vale/gitlab/AlertBoxNoteTip.yml
doc/.vale/gitlab/AlertBoxNoteTip.yml
+1
-0
doc/.vale/gitlab/CurrentStatus.yml
doc/.vale/gitlab/CurrentStatus.yml
+1
-1
doc/.vale/gitlab/FutureTense.yml
doc/.vale/gitlab/FutureTense.yml
+1
-1
doc/.vale/gitlab/RelativeLinks.yml
doc/.vale/gitlab/RelativeLinks.yml
+1
-1
doc/.vale/gitlab/VersionText.yml
doc/.vale/gitlab/VersionText.yml
+3
-12
lib/gitlab/whats_new.rb
lib/gitlab/whats_new.rb
+19
-7
spec/fixtures/whats_new/20201225_01_01.yml
spec/fixtures/whats_new/20201225_01_01.yml
+0
-0
spec/fixtures/whats_new/20201225_01_02.yml
spec/fixtures/whats_new/20201225_01_02.yml
+0
-0
spec/fixtures/whats_new/20201225_01_05.yml
spec/fixtures/whats_new/20201225_01_05.yml
+1
-0
spec/frontend/diffs/components/tree_list_spec.js
spec/frontend/diffs/components/tree_list_spec.js
+3
-3
spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
...dules/shared/components/pipeline_schedule_callout_spec.js
+47
-59
spec/frontend/vue_shared/components/__snapshots__/file_row_header_spec.js.snap
...red/components/__snapshots__/file_row_header_spec.js.snap
+12
-12
spec/frontend/whats_new/components/app_spec.js
spec/frontend/whats_new/components/app_spec.js
+62
-3
spec/frontend/whats_new/store/actions_spec.js
spec/frontend/whats_new/store/actions_spec.js
+18
-3
spec/frontend/whats_new/store/mutations_spec.js
spec/frontend/whats_new/store/mutations_spec.js
+31
-4
spec/frontend/whats_new/utils/get_drawer_body_height_spec.js
spec/frontend/whats_new/utils/get_drawer_body_height_spec.js
+38
-0
spec/helpers/whats_new_helper_spec.rb
spec/helpers/whats_new_helper_spec.rb
+18
-9
spec/requests/whats_new_controller_spec.rb
spec/requests/whats_new_controller_spec.rb
+32
-7
No files found.
app/assets/javascripts/diffs/components/diff_file_row.vue
View file @
3029720c
...
...
@@ -62,6 +62,7 @@ export default {
v-bind=
"$attrs"
:class=
"
{ 'is-active': isActive }"
class="diff-file-row"
truncate-middle
:file-classes="fileClasses"
v-on="$listeners"
>
...
...
app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
View file @
3029720c
import
Vue
from
'
vue
'
;
import
PipelineSchedulesCallout
from
'
../shared/components/pipeline_schedules_callout.vue
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
new
Vue
({
el
:
'
#pipeline-schedules-callout
'
,
components
:
{
'
pipeline-schedules-callout
'
:
PipelineSchedulesCallout
,
},
render
(
createElement
)
{
return
createElement
(
'
pipeline-schedules-callout
'
);
},
}),
);
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
const
el
=
document
.
getElementById
(
'
pipeline-schedules-callout
'
);
if
(
!
el
)
{
return
;
}
const
{
docsUrl
,
illustrationUrl
}
=
el
.
dataset
;
// eslint-disable-next-line no-new
new
Vue
({
el
,
render
(
createElement
)
{
return
createElement
(
PipelineSchedulesCallout
);
},
provide
:
{
docsUrl
,
illustrationUrl
,
},
});
});
app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
View file @
3029720c
...
...
@@ -14,10 +14,9 @@ export default {
components
:
{
GlButton
,
},
inject
:
[
'
docsUrl
'
,
'
illustrationUrl
'
],
data
()
{
return
{
docsUrl
:
document
.
getElementById
(
'
pipeline-schedules-callout
'
).
dataset
.
docsUrl
,
imageUrl
:
document
.
getElementById
(
'
pipeline-schedules-callout
'
).
dataset
.
imageUrl
,
calloutDismissed
:
parseBoolean
(
Cookies
.
get
(
cookieKey
)),
};
},
...
...
@@ -31,7 +30,7 @@ export default {
</
script
>
<
template
>
<div
v-if=
"!calloutDismissed"
class=
"pipeline-schedules-user-callout user-callout"
>
<div
class=
"bordered-box landing content-block"
>
<div
class=
"bordered-box landing content-block"
data-testid=
"innerContent"
>
<gl-button
category=
"tertiary"
icon=
"close"
...
...
@@ -39,8 +38,8 @@ export default {
class=
"gl-absolute gl-top-2 gl-right-2"
@
click=
"dismissCallout"
/>
<div
class=
"svg-cont
ainer
"
>
<img
:src=
"i
mage
Url"
/>
<div
class=
"svg-cont
ent
"
>
<img
:src=
"i
llustration
Url"
/>
</div>
<div
class=
"user-callout-copy"
>
<h4>
{{
__
(
'
Scheduling Pipelines
'
)
}}
</h4>
...
...
app/assets/javascripts/vue_shared/components/file_row.vue
View file @
3029720c
<
script
>
import
{
GlTruncate
}
from
'
@gitlab/ui
'
;
import
FileHeader
from
'
~/vue_shared/components/file_row_header.vue
'
;
import
FileIcon
from
'
~/vue_shared/components/file_icon.vue
'
;
import
{
escapeFileUrl
}
from
'
~/lib/utils/url_utility
'
;
...
...
@@ -8,6 +9,7 @@ export default {
components
:
{
FileHeader
,
FileIcon
,
GlTruncate
,
},
props
:
{
file
:
{
...
...
@@ -28,6 +30,11 @@ export default {
required
:
false
,
default
:
''
,
},
truncateMiddle
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
computed
:
{
isTree
()
{
...
...
@@ -134,9 +141,9 @@ export default {
<span
ref=
"textOutput"
:style=
"levelIndentation"
class=
"file-row-name
str-truncated
"
class=
"file-row-name"
data-qa-selector=
"file_name_content"
:class=
"
fileClasses
"
:class=
"
[fileClasses,
{ 'str-truncated': !truncateMiddle, 'gl-min-w-0': truncateMiddle }]
"
>
<file-icon
class=
"file-row-icon"
...
...
@@ -147,7 +154,8 @@ export default {
:opened="file.opened"
:size="16"
/>
{{
file
.
name
}}
<gl-truncate
v-if=
"truncateMiddle"
:text=
"file.name"
position=
"middle"
class=
"gl-pr-7"
/>
<template
v-else
>
{{
file
.
name
}}
</
template
>
</span>
<slot></slot>
</div>
...
...
app/assets/javascripts/vue_shared/components/file_row_header.vue
View file @
3029720c
<
script
>
import
{
truncatePathMiddleToLength
}
from
'
~/lib/utils/text_utility
'
;
const
MAX_PATH_LENGTH
=
40
;
import
{
GlTruncate
}
from
'
@gitlab/ui
'
;
export
default
{
components
:
{
GlTruncate
,
},
props
:
{
path
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
truncatedPath
()
{
return
truncatePathMiddleToLength
(
this
.
path
,
MAX_PATH_LENGTH
);
},
},
};
</
script
>
<
template
>
<div
class=
"file-row-header bg-white sticky-top p-2 js-file-row-header"
:title=
"path"
>
<
span
class=
"bold"
>
{{
truncatedPath
}}
</span
>
<
gl-truncate
:text=
"path"
position=
"middle"
class=
"bold"
/
>
</div>
</
template
>
app/assets/javascripts/whats_new/components/app.vue
View file @
3029720c
<
script
>
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
GlDrawer
,
GlBadge
,
GlIcon
,
GlLink
}
from
'
@gitlab/ui
'
;
import
{
GlDrawer
,
GlBadge
,
GlIcon
,
GlLink
,
GlInfiniteScroll
,
GlResizeObserverDirective
,
}
from
'
@gitlab/ui
'
;
import
SkeletonLoader
from
'
./skeleton_loader.vue
'
;
import
Tracking
from
'
~/tracking
'
;
import
{
getDrawerBodyHeight
}
from
'
../utils/get_drawer_body_height
'
;
const
trackingMixin
=
Tracking
.
mixin
();
...
...
@@ -12,8 +20,12 @@ export default {
GlBadge
,
GlIcon
,
GlLink
,
GlInfiniteScroll
,
SkeletonLoader
,
},
directives
:
{
GlResizeObserver
:
GlResizeObserverDirective
,
},
mixins
:
[
trackingMixin
],
props
:
{
storageKey
:
{
...
...
@@ -23,7 +35,7 @@ export default {
},
},
computed
:
{
...
mapState
([
'
open
'
,
'
features
'
]),
...
mapState
([
'
open
'
,
'
features
'
,
'
pageInfo
'
,
'
drawerBodyHeight
'
]),
},
mounted
()
{
this
.
openDrawer
(
this
.
storageKey
);
...
...
@@ -35,20 +47,41 @@ export default {
this
.
track
(
'
click_whats_new_drawer
'
,
{
label
:
'
namespace_id
'
,
value
:
namespaceId
});
},
methods
:
{
...
mapActions
([
'
openDrawer
'
,
'
closeDrawer
'
,
'
fetchItems
'
]),
...
mapActions
([
'
openDrawer
'
,
'
closeDrawer
'
,
'
fetchItems
'
,
'
setDrawerBodyHeight
'
]),
bottomReached
()
{
if
(
this
.
pageInfo
.
nextPage
)
{
this
.
fetchItems
(
this
.
pageInfo
.
nextPage
);
}
},
handleResize
()
{
const
height
=
getDrawerBodyHeight
(
this
.
$refs
.
drawer
.
$el
);
this
.
setDrawerBodyHeight
(
height
);
},
},
};
</
script
>
<
template
>
<div>
<gl-drawer
class=
"whats-new-drawer"
:open=
"open"
@
close=
"closeDrawer"
>
<gl-drawer
ref=
"drawer"
v-gl-resize-observer=
"handleResize"
class=
"whats-new-drawer"
:open=
"open"
@
close=
"closeDrawer"
>
<template
#header
>
<h4
class=
"page-title
my-2
"
>
{{
__
(
"
What's new at GitLab
"
)
}}
</h4>
<h4
class=
"page-title
gl-my-3
"
>
{{
__
(
"
What's new at GitLab
"
)
}}
</h4>
</
template
>
<div
class=
"pb-6"
>
<
template
v-if=
"features"
>
<div
v-for=
"feature in features"
:key=
"feature.title"
class=
"mb-6"
>
<gl-infinite-scroll
v-if=
"features.length"
:fetched-items=
"features.length"
:max-list-height=
"drawerBodyHeight"
class=
"gl-p-0"
@
bottomReached=
"bottomReached"
>
<
template
#items
>
<div
v-for=
"feature in features"
:key=
"feature.title"
class=
"gl-mb-7 gl-px-5 gl-pt-5"
>
<gl-link
:href=
"feature.url"
target=
"_blank"
...
...
@@ -60,11 +93,14 @@ export default {
<h5
class=
"gl-font-base"
>
{{
feature
.
title
}}
</h5>
</gl-link>
<div
v-if=
"feature.packages"
class=
"gl-mb-3"
>
<template
v-for=
"package_name in feature.packages"
>
<gl-badge
:key=
"package_name"
size=
"sm"
class=
"whats-new-item-badge gl-mr-2"
>
<gl-icon
name=
"license"
/>
{{
package_name
}}
</gl-badge>
</
template
>
<gl-badge
v-for=
"package_name in feature.packages"
:key=
"package_name"
size=
"sm"
class=
"whats-new-item-badge gl-mr-2"
>
<gl-icon
name=
"license"
/>
{{
package_name
}}
</gl-badge>
</div>
<gl-link
:href=
"feature.url"
...
...
@@ -76,7 +112,7 @@ export default {
<img
:alt=
"feature.title"
:src=
"feature.image_url"
class=
"img-thumbnail
px-6
gl-py-3 whats-new-item-image"
class=
"img-thumbnail
gl-px-8
gl-py-3 whats-new-item-image"
/>
</gl-link>
<p
class=
"gl-pt-3"
>
{{
feature
.
body
}}
</p>
...
...
@@ -90,10 +126,10 @@ export default {
>
</div>
</
template
>
<div
v-else
class=
"gl-mt-5"
>
<skeleton-loader
/
>
<skeleton-loader
/>
<
/div
>
</gl-infinite-scroll
>
<div
v-else
class=
"gl-mt-5"
>
<skeleton-loader
/>
<
skeleton-loader
/
>
</div>
</gl-drawer>
<div
v-if=
"open"
class=
"whats-new-modal-backdrop modal-backdrop"
></div>
...
...
app/assets/javascripts/whats_new/store/actions.js
View file @
3029720c
import
*
as
types
from
'
./mutation_types
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
parseIntPagination
,
normalizeHeaders
}
from
'
~/lib/utils/common_utils
'
;
export
default
{
closeDrawer
({
commit
})
{
...
...
@@ -12,9 +13,33 @@ export default {
localStorage
.
setItem
(
storageKey
,
JSON
.
stringify
(
false
));
}
},
fetchItems
({
commit
})
{
return
axios
.
get
(
'
/-/whats_new
'
).
then
(({
data
})
=>
{
commit
(
types
.
SET_FEATURES
,
data
);
});
fetchItems
({
commit
,
state
},
page
)
{
if
(
state
.
fetching
)
{
return
false
;
}
commit
(
types
.
SET_FETCHING
,
true
);
return
axios
.
get
(
'
/-/whats_new
'
,
{
params
:
{
page
,
},
})
.
then
(({
data
,
headers
})
=>
{
commit
(
types
.
ADD_FEATURES
,
data
);
const
normalizedHeaders
=
normalizeHeaders
(
headers
);
const
{
nextPage
}
=
parseIntPagination
(
normalizedHeaders
);
commit
(
types
.
SET_PAGE_INFO
,
{
nextPage
,
});
})
.
finally
(()
=>
{
commit
(
types
.
SET_FETCHING
,
false
);
});
},
setDrawerBodyHeight
({
commit
},
height
)
{
commit
(
types
.
SET_DRAWER_BODY_HEIGHT
,
height
);
},
};
app/assets/javascripts/whats_new/store/mutation_types.js
View file @
3029720c
export
const
CLOSE_DRAWER
=
'
CLOSE_DRAWER
'
;
export
const
OPEN_DRAWER
=
'
OPEN_DRAWER
'
;
export
const
SET_FEATURES
=
'
SET_FEATURES
'
;
export
const
ADD_FEATURES
=
'
ADD_FEATURES
'
;
export
const
SET_PAGE_INFO
=
'
SET_PAGE_INFO
'
;
export
const
SET_FETCHING
=
'
SET_FETCHING
'
;
export
const
SET_DRAWER_BODY_HEIGHT
=
'
SET_DRAWER_BODY_HEIGHT
'
;
app/assets/javascripts/whats_new/store/mutations.js
View file @
3029720c
...
...
@@ -7,7 +7,16 @@ export default {
[
types
.
OPEN_DRAWER
](
state
)
{
state
.
open
=
true
;
},
[
types
.
SET_FEATURES
](
state
,
data
)
{
state
.
features
=
data
;
[
types
.
ADD_FEATURES
](
state
,
data
)
{
state
.
features
=
state
.
features
.
concat
(
data
);
},
[
types
.
SET_PAGE_INFO
](
state
,
pageInfo
)
{
state
.
pageInfo
=
pageInfo
;
},
[
types
.
SET_FETCHING
](
state
,
fetching
)
{
state
.
fetching
=
fetching
;
},
[
types
.
SET_DRAWER_BODY_HEIGHT
](
state
,
height
)
{
state
.
drawerBodyHeight
=
height
;
},
};
app/assets/javascripts/whats_new/store/state.js
View file @
3029720c
export
default
{
open
:
false
,
features
:
null
,
features
:
[],
fetching
:
false
,
drawerBodyHeight
:
null
,
pageInfo
:
{
nextPage
:
null
,
},
};
app/assets/javascripts/whats_new/utils/get_drawer_body_height.js
0 → 100644
View file @
3029720c
export
const
getDrawerBodyHeight
=
drawer
=>
{
const
drawerViewableHeight
=
drawer
.
clientHeight
-
drawer
.
getBoundingClientRect
().
top
;
const
drawerHeaderHeight
=
drawer
.
querySelector
(
'
.gl-drawer-header
'
).
clientHeight
;
return
drawerViewableHeight
-
drawerHeaderHeight
;
};
app/assets/stylesheets/components/whats_new.scss
View file @
3029720c
.whats-new-drawer
{
margin-top
:
$header-height
;
@include
gl-shadow-none
;
overflow-y
:
hidden
;
.gl-infinite-scroll-legend
{
@include
gl-display-none
;
}
}
.with-performance-bar
.whats-new-drawer
{
...
...
app/controllers/whats_new_controller.rb
View file @
3029720c
...
...
@@ -5,14 +5,14 @@ class WhatsNewController < ApplicationController
skip_before_action
:authenticate_user!
before_action
:check_feature_flag
before_action
:check_feature_flag
,
:check_valid_page_param
,
:set_pagination_headers
feature_category
:navigation
def
index
respond_to
do
|
format
|
format
.
js
do
render
json:
whats_new_
most_recent_release_items
render
json:
whats_new_
release_items
(
page:
current_page
)
end
end
end
...
...
@@ -22,4 +22,23 @@ class WhatsNewController < ApplicationController
def
check_feature_flag
render_404
unless
Feature
.
enabled?
(
:whats_new_drawer
,
current_user
)
end
def
check_valid_page_param
render_404
if
current_page
<
1
end
def
set_pagination_headers
response
.
set_header
(
'X-Next-Page'
,
next_page
)
end
def
current_page
params
[
:page
]
&
.
to_i
||
1
end
def
next_page
next_page
=
current_page
+
1
next_index
=
next_page
-
1
next_page
if
whats_new_file_paths
[
next_index
]
end
end
app/helpers/whats_new_helper.rb
View file @
3029720c
...
...
@@ -5,7 +5,7 @@ module WhatsNewHelper
def
whats_new_most_recent_release_items_count
Gitlab
::
ProcessMemoryCache
.
cache_backend
.
fetch
(
'whats_new:release_items_count'
,
expires_in:
CACHE_DURATION
)
do
whats_new_
most_recent_
release_items
&
.
count
whats_new_release_items
&
.
count
end
end
...
...
@@ -19,9 +19,7 @@ module WhatsNewHelper
def
whats_new_most_recent_version
Gitlab
::
ProcessMemoryCache
.
cache_backend
.
fetch
(
'whats_new:release_version'
,
expires_in:
CACHE_DURATION
)
do
if
whats_new_most_recent_release_items
whats_new_most_recent_release_items
.
first
.
try
(
:[]
,
'release'
)
end
whats_new_release_items
&
.
first
&
.
[
](
'release'
)
end
end
end
app/views/projects/pipeline_schedules/index.html.haml
View file @
3029720c
...
...
@@ -2,7 +2,7 @@
-
page_title
_
(
"Pipeline Schedules"
)
-
add_page_specific_style
'page_bundles/pipeline_schedules'
#pipeline-schedules-callout
{
data:
{
docs_url:
help_page_path
(
'ci/pipelines/schedules'
),
i
mage
_url:
image_path
(
'illustrations/pipeline_schedule_callout.svg'
)
}
}
#pipeline-schedules-callout
{
data:
{
docs_url:
help_page_path
(
'ci/pipelines/schedules'
),
i
llustration
_url:
image_path
(
'illustrations/pipeline_schedule_callout.svg'
)
}
}
.top-area
-
schedule_path_proc
=
->
(
scope
)
{
pipeline_schedules_path
(
@project
,
scope:
scope
)
}
=
render
"tabs"
,
schedule_path_proc:
schedule_path_proc
,
all_schedules:
@all_schedules
,
scope:
@scope
...
...
changelogs/unreleased/ph-25973-mergeRequestSidebarLongPathTruncating.yml
0 → 100644
View file @
3029720c
---
title
:
Fixed long paths truncating in merge request sidebar incorrectly
merge_request
:
45994
author
:
type
:
fixed
doc/.vale/gitlab/AlertBoxCaution.yml
View file @
3029720c
...
...
@@ -6,6 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
substitution
message
:
"
CAUTION:
alert
boxes
must
be
of
the
format
'CAUTION:
**Caution:**'.
'Caution'
can
be
replaced
with
'Warning'
or
'Important'."
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
level
:
warning
nonword
:
true
scope
:
raw
...
...
doc/.vale/gitlab/AlertBoxDanger.yml
View file @
3029720c
...
...
@@ -6,6 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
substitution
message
:
"
DANGER:
alert
boxes
must
be
of
the
format
'DANGER:
**Warning:**'.
'Warning'
can
be
replaced
with
'Important',
'Deprecated',
or
'Required'."
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
level
:
error
nonword
:
true
scope
:
raw
...
...
doc/.vale/gitlab/AlertBoxNoteTip.yml
View file @
3029720c
...
...
@@ -6,6 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
substitution
message
:
"
NOTE:
and
TIP:
alert
boxes
must
be
of
the
format
'NOTE:
**Note:**'
or
'TIP:
**Tip:**"
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
level
:
warning
nonword
:
true
scope
:
raw
...
...
doc/.vale/gitlab/CurrentStatus.yml
View file @
3029720c
...
...
@@ -5,7 +5,7 @@
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
existence
message
:
'
Avoid
words
like
"%s"
that
promise
future
changes.'
message
:
'
Avoid
words
like
"%s"
that
promise
future
changes
,
because
documentation
is
about
the
current
state
of
the
product
.'
level
:
suggestion
ignorecase
:
true
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#language-to-avoid
...
...
doc/.vale/gitlab/FutureTense.yml
View file @
3029720c
...
...
@@ -6,7 +6,7 @@
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
existence
message
:
'
Avoid
using
future
tense:
"%s"'
message
:
'
Avoid
using
future
tense:
"%s"
.
Use
present
tense
instead.
'
ignorecase
:
true
level
:
warning
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#language-to-avoid
...
...
doc/.vale/gitlab/RelativeLinks.yml
View file @
3029720c
...
...
@@ -5,7 +5,7 @@
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
existence
message
:
'
Link
"%s"
must
be
relative
.'
message
:
'
Link
"%s"
must
be
a
relative
link
with
a
.md
extension
.'
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#links-to-internal-documentation
level
:
error
scope
:
raw
...
...
doc/.vale/gitlab/VersionText.yml
View file @
3029720c
---
# Error: gitlab.VersionText
#
# Checks that version text is formatted correctly.
#
# Specifically looks for either of the following that is immediately followed on the next line
# by content, which will break rendering:
#
# - `> Introduced` (version text without a link)
# - `> [Introduced` (version text with a link)
#
# Because it excludes `-`, it doesn't look for multi-line version text, for which content
# immediately on the next line is ok. However, this will often highlight where multi-line version
# text is attempted without `-` characters.
# Checks for use of some of the top misused terms at GitLab.
# For substitutions only flagged as warnings, see SubstitutionWarning.yml
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends
:
existence
message
:
'
"%s"
is
not
formatted
correctly.'
message
:
'
This
introduced-in
line
is
not
formatted
correctly.'
link
:
https://docs.gitlab.com/ee/development/documentation/styleguide.html#text-for-documentation-requiring-version-text
level
:
error
scope
:
raw
...
...
lib/gitlab/whats_new.rb
View file @
3029720c
...
...
@@ -2,27 +2,39 @@
module
Gitlab
module
WhatsNew
CACHE_DURATION
=
1
.
day
CACHE_DURATION
=
1
.
hour
WHATS_NEW_FILES_PATH
=
Rails
.
root
.
join
(
'data'
,
'whats_new'
,
'*.yml'
)
private
def
whats_new_most_recent_release_items
Rails
.
cache
.
fetch
(
'whats_new:release_items'
,
expires_in:
CACHE_DURATION
)
do
file
=
File
.
read
(
most_recent_release_file_path
)
def
whats_new_release_items
(
page:
1
)
Rails
.
cache
.
fetch
(
whats_new_items_cache_key
(
page
),
expires_in:
CACHE_DURATION
)
do
index
=
page
-
1
file_path
=
whats_new_file_paths
[
index
]
next
if
file_path
.
nil?
file
=
File
.
read
(
file_path
)
items
=
YAML
.
safe_load
(
file
,
permitted_classes:
[
Date
])
items
if
items
.
is_a?
(
Array
)
end
rescue
=>
e
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
yaml_file_path:
most_recent_release_file_path
)
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
page:
page
)
nil
end
def
most_recent_release_file_path
@most_recent_release_file_path
||=
Dir
.
glob
(
WHATS_NEW_FILES_PATH
).
max
def
whats_new_file_paths
@whats_new_file_paths
||=
Rails
.
cache
.
fetch
(
'whats_new:file_paths'
,
expires_in:
CACHE_DURATION
)
do
Dir
.
glob
(
WHATS_NEW_FILES_PATH
).
sort
.
reverse
end
end
def
whats_new_items_cache_key
(
page
)
filename
=
/\d*\_\d*\_\d*/
.
match
(
whats_new_file_paths
&
.
first
)
"whats_new:release_items:file-
#{
filename
}
:page-
#{
page
}
"
end
end
end
spec/fixtures/whats_new/01.yml
→
spec/fixtures/whats_new/
20201225_01_
01.yml
View file @
3029720c
File moved
spec/fixtures/whats_new/02.yml
→
spec/fixtures/whats_new/
20201225_01_
02.yml
View file @
3029720c
File moved
spec/fixtures/whats_new/05.yml
→
spec/fixtures/whats_new/
20201225_01_
05.yml
View file @
3029720c
---
-
title
:
bright and sunshinin' day
release
:
'
01.05'
spec/frontend/diffs/components/tree_list_spec.js
View file @
3029720c
...
...
@@ -91,12 +91,12 @@ describe('Diffs tree list component', () => {
expect
(
getFileRows
()
.
at
(
0
)
.
text
(),
.
html
(),
).
toContain
(
'
index.js
'
);
expect
(
getFileRows
()
.
at
(
1
)
.
text
(),
.
html
(),
).
toContain
(
'
app
'
);
});
...
...
@@ -138,7 +138,7 @@ describe('Diffs tree list component', () => {
wrapper
.
vm
.
$store
.
state
.
diffs
.
renderTreeList
=
false
;
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
find
(
'
.file-row
'
).
text
()).
toContain
(
'
index.js
'
);
expect
(
wrapper
.
find
(
'
.file-row
'
).
html
()).
toContain
(
'
index.js
'
);
});
});
});
...
...
spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
View file @
3029720c
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
Cookies
from
'
js-cookie
'
;
import
{
getByRole
}
from
'
@testing-library/dom
'
;
import
PipelineSchedulesCallout
from
'
~/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
'
;
const
PipelineSchedulesCalloutComponent
=
Vue
.
extend
(
PipelineSchedulesCallout
);
const
cookieKey
=
'
pipeline_schedules_callout_dismissed
'
;
const
docsUrl
=
'
help/ci/scheduled_pipelines
'
;
const
i
mage
Url
=
'
pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg
'
;
const
i
llustration
Url
=
'
pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg
'
;
describe
(
'
Pipeline Schedule Callout
'
,
()
=>
{
let
calloutComponent
;
let
wrapper
;
beforeEach
(()
=>
{
setFixtures
(
`
<div id='pipeline-schedules-callout' data-docs-url=
${
docsUrl
}
data-image-url=
${
imageUrl
}
></div>
`
);
});
describe
(
'
independent of cookies
'
,
()
=>
{
beforeEach
(()
=>
{
calloutComponent
=
new
PipelineSchedulesCalloutComponent
().
$mount
();
});
it
(
'
the component can be initialized
'
,
()
=>
{
expect
(
calloutComponent
).
toBeDefined
();
const
createComponent
=
()
=>
{
wrapper
=
shallowMount
(
PipelineSchedulesCallout
,
{
provide
:
{
docsUrl
,
illustrationUrl
,
},
});
};
it
(
'
correctly sets docsUrl
'
,
()
=>
{
expect
(
calloutComponent
.
docsUrl
).
toContain
(
docsUrl
);
});
it
(
'
correctly sets imageUrl
'
,
()
=>
{
expect
(
calloutComponent
.
imageUrl
).
toContain
(
imageUrl
);
});
});
const
findInnerContentOfCallout
=
()
=>
wrapper
.
find
(
'
[data-testid="innerContent"]
'
);
const
findDismissCalloutBtn
=
()
=>
wrapper
.
find
(
GlButton
);
describe
(
`when
${
cookieKey
}
cookie is set`
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(
async
()
=>
{
Cookies
.
set
(
cookieKey
,
true
);
calloutComponent
=
new
PipelineSchedulesCalloutComponent
().
$mount
();
createComponent
();
await
wrapper
.
vm
.
$nextTick
();
});
it
(
'
correctly sets calloutDismissed to true
'
,
()
=>
{
expect
(
calloutComponent
.
calloutDismissed
).
toBe
(
true
);
afterEach
(
()
=>
{
wrapper
.
destroy
(
);
});
it
(
'
does not render the callout
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
childNodes
.
length
).
toBe
(
0
);
expect
(
findInnerContentOfCallout
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when cookie is not set
'
,
()
=>
{
beforeEach
(()
=>
{
Cookies
.
remove
(
cookieKey
);
c
alloutComponent
=
new
PipelineSchedulesCalloutComponent
().
$mou
nt
();
c
reateCompone
nt
();
});
it
(
'
correctly sets calloutDismissed to false
'
,
()
=>
{
expect
(
calloutComponent
.
calloutDismissed
).
toBe
(
false
);
afterEach
(
()
=>
{
wrapper
.
destroy
(
);
});
it
(
'
renders the callout container
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
querySelector
(
'
.bordered-box
'
)).
not
.
toBeNull
();
});
it
(
'
renders the callout img
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
outerHTML
).
toContain
(
'
<img
'
);
expect
(
findInnerContentOfCallout
().
exists
()).
toBe
(
true
);
});
it
(
'
renders the callout title
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
outerHTML
).
toContain
(
'
Scheduling Pipelines
'
);
expect
(
wrapper
.
find
(
'
h4
'
).
text
()).
toBe
(
'
Scheduling Pipelines
'
);
});
it
(
'
renders the callout text
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
outerHTML
).
toContain
(
'
runs pipelines in the future
'
);
expect
(
wrapper
.
find
(
'
p
'
).
text
()
).
toContain
(
'
runs pipelines in the future
'
);
});
it
(
'
renders the documentation url
'
,
()
=>
{
expect
(
calloutComponent
.
$el
.
outerHTML
).
toContain
(
docsUrl
);
expect
(
wrapper
.
find
(
'
a
'
).
attributes
(
'
href
'
)).
toBe
(
docsUrl
);
});
it
(
'
updates calloutDismissed when close button is clicked
'
,
done
=>
{
getByRole
(
calloutComponent
.
$el
,
'
button
'
,
/dismiss/i
).
click
();
describe
(
'
methods
'
,
()
=>
{
it
(
'
#dismissCallout sets calloutDismissed to true
'
,
async
()
=>
{
expect
(
wrapper
.
vm
.
calloutDismissed
).
toBe
(
false
);
findDismissCalloutBtn
().
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
Vue
.
nextTick
(()
=>
{
expect
(
calloutComponent
.
calloutDismissed
).
toBe
(
true
);
done
();
expect
(
findInnerContentOfCallout
().
exists
()).
toBe
(
false
);
});
});
it
(
'
#dismissCallout updates calloutDismissed
'
,
done
=>
{
calloutComponent
.
dismissCallout
();
it
(
'
sets cookie on dismiss
'
,
()
=>
{
const
setCookiesSpy
=
jest
.
spyOn
(
Cookies
,
'
set
'
);
findDismissCalloutBtn
().
vm
.
$emit
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
expect
(
calloutComponent
.
calloutDismissed
).
toBe
(
true
);
done
(
);
expect
(
setCookiesSpy
).
toHaveBeenCalledWith
(
'
pipeline_schedules_callout_dismissed
'
,
true
,
{
expires
:
365
,
}
);
});
});
it
(
'
is hidden when close button is clicked
'
,
done
=>
{
getByRole
(
calloutComponent
.
$el
,
'
button
'
,
/dismiss/i
).
click
(
);
it
(
'
is hidden when close button is clicked
'
,
async
()
=>
{
findDismissCalloutBtn
().
vm
.
$emit
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
expect
(
calloutComponent
.
$el
.
childNodes
.
length
).
toBe
(
0
);
done
();
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findInnerContentOfCallout
().
exists
()).
toBe
(
false
);
});
});
});
spec/frontend/vue_shared/components/__snapshots__/file_row_header_spec.js.snap
View file @
3029720c
...
...
@@ -5,11 +5,11 @@ exports[`File row header component adds multiple ellipsises after 40 characters
class="file-row-header bg-white sticky-top p-2 js-file-row-header"
title="app/assets/javascripts/merge_requests/widget/diffs/notes"
>
<
span
<
gl-truncate-stub
class="bold"
>
app/assets/javascripts/…/…/diffs/notes
</span
>
position="middle"
text="app/assets/javascripts/merge_requests/widget/diffs/notes"
/
>
</div>
`;
...
...
@@ -18,11 +18,11 @@ exports[`File row header component renders file path 1`] = `
class="file-row-header bg-white sticky-top p-2 js-file-row-header"
title="app/assets"
>
<
span
<
gl-truncate-stub
class="bold"
>
app/assets
</span
>
position="middle"
text="app/assets"
/
>
</div>
`;
...
...
@@ -31,10 +31,10 @@ exports[`File row header component trucates path after 40 characters 1`] = `
class="file-row-header bg-white sticky-top p-2 js-file-row-header"
title="app/assets/javascripts/merge_requests"
>
<
span
<
gl-truncate-stub
class="bold"
>
app/assets/javascripts/merge_requests
</span
>
position="middle"
text="app/assets/javascripts/merge_requests"
/
>
</div>
`;
spec/frontend/whats_new/components/app_spec.js
View file @
3029720c
import
{
createLocalVue
,
mount
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
GlDrawer
,
GlInfiniteScroll
}
from
'
@gitlab/ui
'
;
import
{
mockTracking
,
unmockTracking
,
triggerEvent
}
from
'
helpers/tracking_helper
'
;
import
{
createMockDirective
,
getBinding
}
from
'
helpers/vue_mock_directive
'
;
import
App
from
'
~/whats_new/components/app.vue
'
;
import
{
getDrawerBodyHeight
}
from
'
~/whats_new/utils/get_drawer_body_height
'
;
const
MOCK_DRAWER_BODY_HEIGHT
=
42
;
jest
.
mock
(
'
~/whats_new/utils/get_drawer_body_height
'
,
()
=>
({
getDrawerBodyHeight
:
jest
.
fn
().
mockImplementation
(()
=>
MOCK_DRAWER_BODY_HEIGHT
),
}));
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
...
...
@@ -20,11 +28,13 @@ describe('App', () => {
openDrawer
:
jest
.
fn
(),
closeDrawer
:
jest
.
fn
(),
fetchItems
:
jest
.
fn
(),
setDrawerBodyHeight
:
jest
.
fn
(),
};
state
=
{
open
:
true
,
features
:
null
,
features
:
[],
drawerBodyHeight
:
null
,
};
store
=
new
Vuex
.
Store
({
...
...
@@ -36,9 +46,15 @@ describe('App', () => {
localVue
,
store
,
propsData
,
directives
:
{
GlResizeObserver
:
createMockDirective
(),
},
});
};
const
findInfiniteScroll
=
()
=>
wrapper
.
find
(
GlInfiniteScroll
);
const
emitBottomReached
=
()
=>
findInfiniteScroll
().
vm
.
$emit
(
'
bottomReached
'
);
beforeEach
(
async
()
=>
{
document
.
body
.
dataset
.
page
=
'
test-page
'
;
document
.
body
.
dataset
.
namespaceId
=
'
namespace-840
'
;
...
...
@@ -47,6 +63,7 @@ describe('App', () => {
buildWrapper
();
wrapper
.
vm
.
$store
.
state
.
features
=
[{
title
:
'
Whats New Drawer
'
,
url
:
'
www.url.com
'
}];
wrapper
.
vm
.
$store
.
state
.
drawerBodyHeight
=
MOCK_DRAWER_BODY_HEIGHT
;
await
wrapper
.
vm
.
$nextTick
();
});
...
...
@@ -61,7 +78,7 @@ describe('App', () => {
expect
(
getDrawer
().
exists
()).
toBe
(
true
);
});
it
(
'
dispatches openDrawer when mounted
'
,
()
=>
{
it
(
'
dispatches openDrawer
and tracking calls
when mounted
'
,
()
=>
{
expect
(
actions
.
openDrawer
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
'
storage-key
'
);
expect
(
trackingSpy
).
toHaveBeenCalledWith
(
undefined
,
'
click_whats_new_drawer
'
,
{
label
:
'
namespace_id
'
,
...
...
@@ -102,4 +119,46 @@ describe('App', () => {
},
]);
});
it
(
'
renders infinite scroll
'
,
()
=>
{
const
scroll
=
findInfiniteScroll
();
expect
(
scroll
.
props
()).
toMatchObject
({
fetchedItems
:
wrapper
.
vm
.
$store
.
state
.
features
.
length
,
maxListHeight
:
MOCK_DRAWER_BODY_HEIGHT
,
});
});
describe
(
'
bottomReached
'
,
()
=>
{
beforeEach
(()
=>
{
actions
.
fetchItems
.
mockClear
();
});
it
(
'
when nextPage exists it calls fetchItems
'
,
()
=>
{
wrapper
.
vm
.
$store
.
state
.
pageInfo
=
{
nextPage
:
840
};
emitBottomReached
();
expect
(
actions
.
fetchItems
).
toHaveBeenCalledWith
(
expect
.
anything
(),
840
);
});
it
(
'
when nextPage does not exist it does not call fetchItems
'
,
()
=>
{
wrapper
.
vm
.
$store
.
state
.
pageInfo
=
{
nextPage
:
null
};
emitBottomReached
();
expect
(
actions
.
fetchItems
).
not
.
toHaveBeenCalled
();
});
});
it
(
'
calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered
'
,
()
=>
{
const
{
value
}
=
getBinding
(
getDrawer
().
element
,
'
gl-resize-observer
'
);
value
();
expect
(
getDrawerBodyHeight
).
toHaveBeenCalledWith
(
wrapper
.
find
(
GlDrawer
).
element
);
expect
(
actions
.
setDrawerBodyHeight
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
MOCK_DRAWER_BODY_HEIGHT
,
);
});
});
spec/frontend/whats_new/store/actions_spec.js
View file @
3029720c
...
...
@@ -30,7 +30,9 @@ describe('whats new actions', () => {
axiosMock
=
new
MockAdapter
(
axios
);
axiosMock
.
onGet
(
'
/-/whats_new
'
)
.
replyOnce
(
200
,
[{
title
:
'
Whats New Drawer
'
,
url
:
'
www.url.com
'
}]);
.
replyOnce
(
200
,
[{
title
:
'
Whats New Drawer
'
,
url
:
'
www.url.com
'
}],
{
'
x-next-page
'
:
'
2
'
,
});
await
waitForPromises
();
});
...
...
@@ -39,10 +41,23 @@ describe('whats new actions', () => {
axiosMock
.
restore
();
});
it
(
'
should commit setFeatures
'
,
()
=>
{
it
(
'
if already fetching, does not fetch
'
,
()
=>
{
testAction
(
actions
.
fetchItems
,
{},
{
fetching
:
true
},
[]);
});
it
(
'
should commit fetching, setFeatures and setPagination
'
,
()
=>
{
testAction
(
actions
.
fetchItems
,
{},
{},
[
{
type
:
types
.
SET_FEATURES
,
payload
:
[{
title
:
'
Whats New Drawer
'
,
url
:
'
www.url.com
'
}]
},
{
type
:
types
.
SET_FETCHING
,
payload
:
true
},
{
type
:
types
.
ADD_FEATURES
,
payload
:
[{
title
:
'
Whats New Drawer
'
,
url
:
'
www.url.com
'
}]
},
{
type
:
types
.
SET_PAGE_INFO
,
payload
:
{
nextPage
:
2
}
},
{
type
:
types
.
SET_FETCHING
,
payload
:
false
},
]);
});
});
describe
(
'
setDrawerBodyHeight
'
,
()
=>
{
testAction
(
actions
.
setDrawerBodyHeight
,
42
,
{},
[
{
type
:
types
.
SET_DRAWER_BODY_HEIGHT
,
payload
:
42
},
]);
});
});
spec/frontend/whats_new/store/mutations_spec.js
View file @
3029720c
...
...
@@ -23,10 +23,37 @@ describe('whats new mutations', () => {
});
});
describe
(
'
setFeatures
'
,
()
=>
{
it
(
'
sets features to data
'
,
()
=>
{
mutations
[
types
.
SET_FEATURES
](
state
,
'
bells and whistles
'
);
expect
(
state
.
features
).
toBe
(
'
bells and whistles
'
);
describe
(
'
addFeatures
'
,
()
=>
{
it
(
'
adds features from data
'
,
()
=>
{
mutations
[
types
.
ADD_FEATURES
](
state
,
[
'
bells and whistles
'
]);
expect
(
state
.
features
).
toEqual
([
'
bells and whistles
'
]);
});
it
(
'
when there are already items, it adds items
'
,
()
=>
{
state
.
features
=
[
'
shiny things
'
];
mutations
[
types
.
ADD_FEATURES
](
state
,
[
'
bells and whistles
'
]);
expect
(
state
.
features
).
toEqual
([
'
shiny things
'
,
'
bells and whistles
'
]);
});
});
describe
(
'
setPageInfo
'
,
()
=>
{
it
(
'
sets page info
'
,
()
=>
{
mutations
[
types
.
SET_PAGE_INFO
](
state
,
{
nextPage
:
8
});
expect
(
state
.
pageInfo
).
toEqual
({
nextPage
:
8
});
});
});
describe
(
'
setFetching
'
,
()
=>
{
it
(
'
sets fetching
'
,
()
=>
{
mutations
[
types
.
SET_FETCHING
](
state
,
true
);
expect
(
state
.
fetching
).
toBe
(
true
);
});
});
describe
(
'
setDrawerBodyHeight
'
,
()
=>
{
it
(
'
sets drawerBodyHeight
'
,
()
=>
{
mutations
[
types
.
SET_DRAWER_BODY_HEIGHT
](
state
,
840
);
expect
(
state
.
drawerBodyHeight
).
toBe
(
840
);
});
});
});
spec/frontend/whats_new/utils/get_drawer_body_height_spec.js
0 → 100644
View file @
3029720c
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
getDrawerBodyHeight
}
from
'
~/whats_new/utils/get_drawer_body_height
'
;
describe
(
'
~/whats_new/utils/get_drawer_body_height
'
,
()
=>
{
let
drawerWrapper
;
beforeEach
(()
=>
{
drawerWrapper
=
mount
(
GlDrawer
,
{
propsData
:
{
open
:
true
},
});
});
afterEach
(()
=>
{
drawerWrapper
.
destroy
();
});
const
setClientHeight
=
(
el
,
height
)
=>
{
Object
.
defineProperty
(
el
,
'
clientHeight
'
,
{
get
()
{
return
height
;
},
});
};
const
setDrawerDimensions
=
({
height
,
top
,
headerHeight
})
=>
{
const
drawer
=
drawerWrapper
.
element
;
setClientHeight
(
drawer
,
height
);
jest
.
spyOn
(
drawer
,
'
getBoundingClientRect
'
).
mockReturnValue
({
top
});
setClientHeight
(
drawer
.
querySelector
(
'
.gl-drawer-header
'
),
headerHeight
);
};
it
(
'
calculates height of drawer body
'
,
()
=>
{
setDrawerDimensions
({
height
:
100
,
top
:
5
,
headerHeight
:
40
});
expect
(
getDrawerBodyHeight
(
drawerWrapper
.
element
)).
toBe
(
55
);
});
});
spec/helpers/whats_new_helper_spec.rb
View file @
3029720c
...
...
@@ -3,21 +3,23 @@
require
'spec_helper'
RSpec
.
describe
WhatsNewHelper
do
let
(
:fixture_dir_glob
)
{
Dir
.
glob
(
File
.
join
(
'spec'
,
'fixtures'
,
'whats_new'
,
'*.yml'
))
}
describe
'#whats_new_storage_key'
do
subject
{
helper
.
whats_new_storage_key
}
before
do
allow
(
helper
).
to
receive
(
:whats_new_most_recent_version
).
and_return
(
version
)
end
context
'when version exist'
do
let
(
:version
)
{
'84.0'
}
before
do
allow
(
Dir
).
to
receive
(
:glob
).
with
(
Rails
.
root
.
join
(
'data'
,
'whats_new'
,
'*.yml'
)).
and_return
(
fixture_dir_glob
)
end
it
{
is_expected
.
to
eq
(
'display-whats-new-notification-
84.0
'
)
}
it
{
is_expected
.
to
eq
(
'display-whats-new-notification-
01.05
'
)
}
end
context
'when recent release items do NOT exist'
do
let
(
:version
)
{
nil
}
before
do
allow
(
helper
).
to
receive
(
:whats_new_release_items
).
and_return
(
nil
)
end
it
{
is_expected
.
to
be_nil
}
end
...
...
@@ -27,8 +29,6 @@ RSpec.describe WhatsNewHelper do
subject
{
helper
.
whats_new_most_recent_release_items_count
}
context
'when recent release items exist'
do
let
(
:fixture_dir_glob
)
{
Dir
.
glob
(
File
.
join
(
'spec'
,
'fixtures'
,
'whats_new'
,
'*.yml'
))
}
it
'returns the count from the most recent file'
do
expect
(
Dir
).
to
receive
(
:glob
).
with
(
Rails
.
root
.
join
(
'data'
,
'whats_new'
,
'*.yml'
)).
and_return
(
fixture_dir_glob
)
...
...
@@ -48,4 +48,13 @@ RSpec.describe WhatsNewHelper do
end
end
end
# Testing this important private method here because the request spec required multiple confusing mocks and felt wrong and overcomplicated
describe
'#whats_new_items_cache_key'
do
it
'returns a key containing the most recent file name and page parameter'
do
allow
(
Dir
).
to
receive
(
:glob
).
with
(
Rails
.
root
.
join
(
'data'
,
'whats_new'
,
'*.yml'
)).
and_return
(
fixture_dir_glob
)
expect
(
helper
.
send
(
:whats_new_items_cache_key
,
2
)).
to
eq
(
'whats_new:release_items:file-20201225_01_05:page-2'
)
end
end
end
spec/requests/whats_new_controller_spec.rb
View file @
3029720c
...
...
@@ -4,19 +4,44 @@ require 'spec_helper'
RSpec
.
describe
WhatsNewController
do
describe
'whats_new_path'
do
before
do
allow_any_instance_of
(
WhatsNewController
).
to
receive
(
:whats_new_most_recent_release_items
).
and_return
(
'items'
)
end
context
'with whats_new_drawer feature enabled'
do
let
(
:fixture_dir_glob
)
{
Dir
.
glob
(
File
.
join
(
'spec'
,
'fixtures'
,
'whats_new'
,
'*.yml'
))
}
before
do
stub_feature_flags
(
whats_new_drawer:
true
)
allow
(
Dir
).
to
receive
(
:glob
).
with
(
Rails
.
root
.
join
(
'data'
,
'whats_new'
,
'*.yml'
)).
and_return
(
fixture_dir_glob
)
end
it
'is successful'
do
get
whats_new_path
,
xhr:
true
context
'with no page param'
do
it
'responds with paginated data and headers'
do
get
whats_new_path
,
xhr:
true
expect
(
response
.
body
).
to
eq
([{
title:
"bright and sunshinin' day"
,
release:
"01.05"
}].
to_json
)
expect
(
response
.
headers
[
'X-Next-Page'
]).
to
eq
(
2
)
end
end
context
'with page param'
do
it
'responds with paginated data and headers'
do
get
whats_new_path
(
page:
2
),
xhr:
true
expect
(
response
.
body
).
to
eq
([{
title:
'bright'
}].
to_json
)
expect
(
response
.
headers
[
'X-Next-Page'
]).
to
eq
(
3
)
end
it
'returns a 404 if page param is negative'
do
get
whats_new_path
(
page:
-
1
),
xhr:
true
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
context
'when there are no more paginated results'
do
it
'responds with nil X-Next-Page header'
do
get
whats_new_path
(
page:
3
),
xhr:
true
expect
(
response
.
body
).
to
eq
([{
title:
"It's gonna be a bright"
}].
to_json
)
expect
(
response
.
headers
[
'X-Next-Page'
]).
to
be
nil
end
end
end
end
...
...
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