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
e06d0e77
Commit
e06d0e77
authored
Feb 25, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add latest changes from gitlab-org/gitlab@master
parent
f7dae0cd
Changes
46
Hide whitespace changes
Inline
Side-by-side
Showing
46 changed files
with
1199 additions
and
199 deletions
+1199
-199
app/assets/javascripts/boards/components/board.js
app/assets/javascripts/boards/components/board.js
+10
-1
app/assets/javascripts/boards/components/board_sidebar.js
app/assets/javascripts/boards/components/board_sidebar.js
+5
-3
app/assets/javascripts/lib/utils/datetime_utility.js
app/assets/javascripts/lib/utils/datetime_utility.js
+5
-5
app/assets/javascripts/lib/utils/highlight.js
app/assets/javascripts/lib/utils/highlight.js
+3
-4
app/assets/javascripts/lib/utils/text_utility.js
app/assets/javascripts/lib/utils/text_utility.js
+2
-2
app/assets/javascripts/lib/utils/unit_format/formatter_factory.js
...ts/javascripts/lib/utils/unit_format/formatter_factory.js
+119
-0
app/assets/javascripts/lib/utils/unit_format/index.js
app/assets/javascripts/lib/utils/unit_format/index.js
+167
-0
app/assets/javascripts/monitoring/components/charts/time_series.vue
.../javascripts/monitoring/components/charts/time_series.vue
+6
-3
app/assets/javascripts/notes/components/discussion_resolve_button.vue
...avascripts/notes/components/discussion_resolve_button.vue
+6
-6
app/assets/stylesheets/framework/modal.scss
app/assets/stylesheets/framework/modal.scss
+4
-6
app/assets/stylesheets/framework/spinner.scss
app/assets/stylesheets/framework/spinner.scss
+2
-1
app/assets/stylesheets/pages/issuable.scss
app/assets/stylesheets/pages/issuable.scss
+36
-3
app/assets/stylesheets/pages/labels.scss
app/assets/stylesheets/pages/labels.scss
+22
-5
app/assets/stylesheets/pages/milestone.scss
app/assets/stylesheets/pages/milestone.scss
+13
-3
app/assets/stylesheets/pages/notes.scss
app/assets/stylesheets/pages/notes.scss
+11
-1
app/helpers/labels_helper.rb
app/helpers/labels_helper.rb
+63
-19
app/models/clusters/cluster.rb
app/models/clusters/cluster.rb
+9
-9
app/services/issuable/common_system_notes_service.rb
app/services/issuable/common_system_notes_service.rb
+1
-1
app/services/resource_events/change_milestone_service.rb
app/services/resource_events/change_milestone_service.rb
+1
-1
app/views/projects/issues/_issue.html.haml
app/views/projects/issues/_issue.html.haml
+1
-1
app/views/projects/merge_requests/_merge_request.html.haml
app/views/projects/merge_requests/_merge_request.html.haml
+1
-1
app/views/shared/_delete_label_modal.html.haml
app/views/shared/_delete_label_modal.html.haml
+1
-1
app/views/shared/boards/components/_board.html.haml
app/views/shared/boards/components/_board.html.haml
+8
-5
app/views/shared/boards/components/sidebar/_labels.html.haml
app/views/shared/boards/components/sidebar/_labels.html.haml
+6
-9
app/views/shared/milestones/_issuable.html.haml
app/views/shared/milestones/_issuable.html.haml
+1
-1
app/views/shared/milestones/_labels_tab.html.haml
app/views/shared/milestones/_labels_tab.html.haml
+3
-5
changelogs/unreleased/196648-replace-_-with-lodash.yml
changelogs/unreleased/196648-replace-_-with-lodash.yml
+5
-0
changelogs/unreleased/38144-replace-labels-in-haml-with-gitlab-ui-css.yml
...eased/38144-replace-labels-in-haml-with-gitlab-ui-css.yml
+5
-0
changelogs/unreleased/Resolve-Migrate--fa-spinner-app-assets-javascripts-notes-components-discu.yml
...spinner-app-assets-javascripts-notes-components-discu.yml
+5
-0
lib/banzai/filter/label_reference_filter.rb
lib/banzai/filter/label_reference_filter.rb
+10
-7
lib/banzai/filter/reference_filter.rb
lib/banzai/filter/reference_filter.rb
+2
-1
lib/gitlab/markdown_cache.rb
lib/gitlab/markdown_cache.rb
+1
-1
locale/gitlab.pot
locale/gitlab.pot
+9
-3
spec/features/boards/sidebar_spec.rb
spec/features/boards/sidebar_spec.rb
+5
-5
spec/features/groups/container_registry_spec.rb
spec/features/groups/container_registry_spec.rb
+93
-0
spec/features/issuables/issuable_list_spec.rb
spec/features/issuables/issuable_list_spec.rb
+4
-4
spec/features/issues/filtered_search/filter_issues_spec.rb
spec/features/issues/filtered_search/filter_issues_spec.rb
+1
-1
spec/features/labels_hierarchy_spec.rb
spec/features/labels_hierarchy_spec.rb
+3
-3
spec/features/projects/container_registry_spec.rb
spec/features/projects/container_registry_spec.rb
+161
-0
spec/frontend/lib/utils/unit_format/formatter_factory_spec.js
.../frontend/lib/utils/unit_format/formatter_factory_spec.js
+200
-0
spec/frontend/lib/utils/unit_format/index_spec.js
spec/frontend/lib/utils/unit_format/index_spec.js
+117
-0
spec/helpers/labels_helper_spec.rb
spec/helpers/labels_helper_spec.rb
+6
-6
spec/helpers/markup_helper_spec.rb
spec/helpers/markup_helper_spec.rb
+3
-1
spec/lib/banzai/filter/label_reference_filter_spec.rb
spec/lib/banzai/filter/label_reference_filter_spec.rb
+13
-13
spec/services/resource_events/change_milestone_service_spec.rb
...services/resource_events/change_milestone_service_spec.rb
+4
-58
spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb
...source_events/change_milestone_service_shared_examples.rb
+46
-0
No files found.
app/assets/javascripts/boards/components/board.js
View file @
e06d0e77
import
$
from
'
jquery
'
;
import
Sortable
from
'
sortablejs
'
;
import
Vue
from
'
vue
'
;
import
{
GlButtonGroup
,
GlButton
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
GlButtonGroup
,
GlButton
,
Gl
Label
,
Gl
Tooltip
}
from
'
@gitlab/ui
'
;
import
isWipLimitsOn
from
'
ee_else_ce/boards/mixins/is_wip_limits
'
;
import
{
s__
,
__
,
sprintf
}
from
'
~/locale
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
...
...
@@ -14,6 +14,7 @@ import IssueCount from './issue_count.vue';
import
boardsStore
from
'
../stores/boards_store
'
;
import
{
getBoardSortableDefaultOptions
,
sortableEnd
}
from
'
../mixins/sortable_default_options
'
;
import
{
ListType
}
from
'
../constants
'
;
import
{
isScopedLabel
}
from
'
~/lib/utils/common_utils
'
;
export
default
Vue
.
extend
({
components
:
{
...
...
@@ -24,6 +25,7 @@ export default Vue.extend({
GlButtonGroup
,
IssueCount
,
GlButton
,
GlLabel
,
GlTooltip
,
},
directives
:
{
...
...
@@ -95,6 +97,9 @@ export default Vue.extend({
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return
`boards.
${
this
.
boardId
}
.
${
this
.
list
.
type
}
.
${
this
.
list
.
id
}
`
;
},
helpLink
()
{
return
boardsStore
.
scopedLabels
.
helpLink
;
},
},
watch
:
{
filter
:
{
...
...
@@ -145,6 +150,10 @@ export default Vue.extend({
}
},
methods
:
{
showScopedLabels
(
label
)
{
return
boardsStore
.
scopedLabels
.
enabled
&&
isScopedLabel
(
label
);
},
showNewIssueForm
()
{
this
.
$refs
[
'
board-list
'
].
showIssueForm
=
!
this
.
$refs
[
'
board-list
'
].
showIssueForm
;
},
...
...
app/assets/javascripts/boards/components/board_sidebar.js
View file @
e06d0e77
...
...
@@ -2,6 +2,7 @@
import
$
from
'
jquery
'
;
import
Vue
from
'
vue
'
;
import
{
GlLabel
}
from
'
@gitlab/ui
'
;
import
Flash
from
'
~/flash
'
;
import
{
sprintf
,
__
}
from
'
~/locale
'
;
import
Sidebar
from
'
~/right_sidebar
'
;
...
...
@@ -22,6 +23,7 @@ export default Vue.extend({
components
:
{
AssigneeTitle
,
Assignees
,
GlLabel
,
SidebarEpicsSelect
:
()
=>
import
(
'
ee_component/sidebar/components/sidebar_item_epics_select.vue
'
),
RemoveBtn
,
...
...
@@ -67,6 +69,9 @@ export default Vue.extend({
selectedLabels
()
{
return
this
.
hasLabels
?
this
.
issue
.
labels
.
map
(
l
=>
l
.
title
).
join
(
'
,
'
)
:
''
;
},
helpLink
()
{
return
boardsStore
.
scopedLabels
.
helpLink
;
},
},
watch
:
{
detail
:
{
...
...
@@ -147,8 +152,5 @@ export default Vue.extend({
showScopedLabels
(
label
)
{
return
boardsStore
.
scopedLabels
.
enabled
&&
isScopedLabel
(
label
);
},
helpLink
()
{
return
boardsStore
.
scopedLabels
.
helpLink
;
},
},
});
app/assets/javascripts/lib/utils/datetime_utility.js
View file @
e06d0e77
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
{
isString
,
mapValues
,
isNumber
,
reduce
}
from
'
lodash
'
;
import
*
as
timeago
from
'
timeago.js
'
;
import
dateFormat
from
'
dateformat
'
;
import
{
languageCode
,
s__
,
__
,
n__
}
from
'
../../locale
'
;
...
...
@@ -79,7 +79,7 @@ export const getDayName = date =>
* @returns {String}
*/
export
const
formatDate
=
(
datetime
,
format
=
'
mmm d, yyyy h:MMtt Z
'
)
=>
{
if
(
_
.
isString
(
datetime
)
&&
datetime
.
match
(
/
\d
+-
\d
+
\d
+ /
))
{
if
(
isString
(
datetime
)
&&
datetime
.
match
(
/
\d
+-
\d
+
\d
+ /
))
{
throw
new
Error
(
__
(
'
Invalid date
'
));
}
return
dateFormat
(
datetime
,
format
);
...
...
@@ -497,7 +497,7 @@ export const parseSeconds = (
let
unorderedMinutes
=
Math
.
abs
(
seconds
/
SECONDS_PER_MINUTE
);
return
_
.
mapObject
(
timePeriodConstraints
,
minutesPerPeriod
=>
{
return
mapValues
(
timePeriodConstraints
,
minutesPerPeriod
=>
{
if
(
minutesPerPeriod
===
0
)
{
return
0
;
}
...
...
@@ -516,7 +516,7 @@ export const parseSeconds = (
* If the 'fullNameFormat' param is passed it returns a non condensed string eg '1 week 3 days'
*/
export
const
stringifyTime
=
(
timeObject
,
fullNameFormat
=
false
)
=>
{
const
reducedTime
=
_
.
reduce
(
const
reducedTime
=
reduce
(
timeObject
,
(
memo
,
unitValue
,
unitName
)
=>
{
const
isNonZero
=
Boolean
(
unitValue
);
...
...
@@ -642,7 +642,7 @@ export const dayAfter = date => new Date(newDate(date).setDate(date.getDate() +
* @return {String} approximated time
*/
export
const
approximateDuration
=
(
seconds
=
0
)
=>
{
if
(
!
_
.
isNumber
(
seconds
)
||
seconds
<
0
)
{
if
(
!
isNumber
(
seconds
)
||
seconds
<
0
)
{
return
''
;
}
...
...
app/assets/javascripts/lib/utils/highlight.js
View file @
e06d0e77
import
fuzzaldrinPlus
from
'
fuzzaldrin-plus
'
;
import
_
from
'
underscore
'
;
import
sanitize
from
'
sanitize-html
'
;
/**
...
...
@@ -17,11 +16,11 @@ import sanitize from 'sanitize-html';
* @param {String} matchSuffix The string to insert at the end of a match
*/
export
default
function
highlight
(
string
,
match
=
''
,
matchPrefix
=
'
<b>
'
,
matchSuffix
=
'
</b>
'
)
{
if
(
_
.
isUndefined
(
string
)
||
_
.
isNull
(
string
)
)
{
if
(
!
string
)
{
return
''
;
}
if
(
_
.
isUndefined
(
match
)
||
_
.
isNull
(
match
)
||
match
===
''
)
{
if
(
!
match
)
{
return
string
;
}
...
...
@@ -34,7 +33,7 @@ export default function highlight(string, match = '', matchPrefix = '<b>', match
return
sanitizedValue
.
split
(
''
)
.
map
((
character
,
i
)
=>
{
if
(
_
.
contains
(
occurrences
,
i
))
{
if
(
occurrences
.
includes
(
i
))
{
return
`
${
matchPrefix
}${
character
}${
matchSuffix
}
`
;
}
...
...
app/assets/javascripts/lib/utils/text_utility.js
View file @
e06d0e77
import
_
from
'
underscore
'
;
import
{
isString
}
from
'
lodash
'
;
/**
* Adds a , to a string composed by numbers, at every 3 chars.
...
...
@@ -199,7 +199,7 @@ export const splitCamelCase = string =>
* i.e. "My Group / My Subgroup / My Project"
*/
export
const
truncateNamespace
=
(
string
=
''
)
=>
{
if
(
_
.
isNull
(
string
)
||
!
_
.
isString
(
string
))
{
if
(
string
===
null
||
!
isString
(
string
))
{
return
''
;
}
...
...
app/assets/javascripts/lib/utils/unit_format/formatter_factory.js
0 → 100644
View file @
e06d0e77
/**
* Formats a number as string using `toLocaleString`.
*
* @param {Number} number to be converted
* @param {params} Parameters
* @param {params.fractionDigits} Number of decimal digits
* to display, defaults to using `toLocaleString` defaults.
* @param {params.maxLength} Max output char lenght at the
* expense of precision, if the output is longer than this,
* the formatter switches to using exponential notation.
* @param {params.factor} Value is multiplied by this factor,
* useful for value normalization.
* @returns Formatted value
*/
function
formatNumber
(
value
,
{
fractionDigits
=
undefined
,
valueFactor
=
1
,
style
=
undefined
,
maxLength
=
undefined
},
)
{
if
(
value
===
null
)
{
return
''
;
}
const
num
=
value
*
valueFactor
;
const
formatted
=
num
.
toLocaleString
(
undefined
,
{
minimumFractionDigits
:
fractionDigits
,
maximumFractionDigits
:
fractionDigits
,
style
,
});
if
(
maxLength
!==
undefined
&&
formatted
.
length
>
maxLength
)
{
// 123456 becomes 1.23e+8
return
num
.
toExponential
(
2
);
}
return
formatted
;
}
/**
* Formats a number as a string scaling it up according to units.
*
* While the number is scaled down, the units are scaled up.
*
* @param {Array} List of units of the scale
* @param {Number} unitFactor - Factor of the scale for each
* unit after which the next unit is used scaled.
*/
const
scaledFormatter
=
(
units
,
unitFactor
=
1000
)
=>
{
if
(
unitFactor
===
0
)
{
return
new
RangeError
(
`unitFactor cannot have the value 0.`
);
}
return
(
value
,
fractionDigits
)
=>
{
if
(
value
===
null
)
{
return
''
;
}
if
(
value
===
Number
.
NEGATIVE_INFINITY
||
value
===
Number
.
POSITIVE_INFINITY
||
Number
.
isNaN
(
value
)
)
{
return
value
.
toLocaleString
(
undefined
);
}
let
num
=
value
;
let
scale
=
0
;
const
limit
=
units
.
length
;
while
(
Math
.
abs
(
num
)
>=
unitFactor
)
{
scale
+=
1
;
num
/=
unitFactor
;
if
(
scale
>=
limit
)
{
return
'
NA
'
;
}
}
const
unit
=
units
[
scale
];
return
`
${
formatNumber
(
num
,
{
fractionDigits
})}${
unit
}
`
;
};
};
/**
* Returns a function that formats a number as a string.
*/
export
const
numberFormatter
=
(
style
=
'
decimal
'
,
valueFactor
=
1
)
=>
{
return
(
value
,
fractionDigits
,
maxLength
)
=>
{
return
`
${
formatNumber
(
value
,
{
fractionDigits
,
maxLength
,
valueFactor
,
style
})}
`
;
};
};
/**
* Returns a function that formats a number as a string with a suffix.
*/
export
const
suffixFormatter
=
(
unit
=
''
,
valueFactor
=
1
)
=>
{
return
(
value
,
fractionDigits
,
maxLength
)
=>
{
const
length
=
maxLength
!==
undefined
?
maxLength
-
unit
.
length
:
undefined
;
return
`
${
formatNumber
(
value
,
{
fractionDigits
,
maxLength
:
length
,
valueFactor
})}${
unit
}
`
;
};
};
/**
* Returns a function that formats a number scaled using SI units notation.
*/
export
const
scaledSIFormatter
=
(
unit
=
''
,
prefixOffset
=
0
)
=>
{
const
fractional
=
[
'
y
'
,
'
z
'
,
'
a
'
,
'
f
'
,
'
p
'
,
'
n
'
,
'
µ
'
,
'
m
'
];
const
multiplicative
=
[
'
k
'
,
'
M
'
,
'
G
'
,
'
T
'
,
'
P
'
,
'
E
'
,
'
Z
'
,
'
Y
'
];
const
symbols
=
[...
fractional
,
''
,
...
multiplicative
];
const
units
=
symbols
.
slice
(
fractional
.
length
+
prefixOffset
).
map
(
prefix
=>
{
return
`
${
prefix
}${
unit
}
`
;
});
if
(
!
units
.
length
)
{
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
throw
new
RangeError
(
'
The unit cannot be converted, please try a different scale
'
);
}
return
scaledFormatter
(
units
);
};
app/assets/javascripts/lib/utils/unit_format/index.js
0 → 100644
View file @
e06d0e77
import
{
s__
}
from
'
~/locale
'
;
import
{
suffixFormatter
,
scaledSIFormatter
,
numberFormatter
}
from
'
./formatter_factory
'
;
/**
* Supported formats
*/
export
const
SUPPORTED_FORMATS
=
{
// Number
number
:
'
number
'
,
percent
:
'
percent
'
,
percentHundred
:
'
percentHundred
'
,
// Duration
seconds
:
'
seconds
'
,
miliseconds
:
'
miliseconds
'
,
// Digital
bytes
:
'
bytes
'
,
kilobytes
:
'
kilobytes
'
,
megabytes
:
'
megabytes
'
,
gigabytes
:
'
gigabytes
'
,
terabytes
:
'
terabytes
'
,
petabytes
:
'
petabytes
'
,
};
/**
* Returns a function that formats number to different units
* @param {String} format - Format to use, must be one of the SUPPORTED_FORMATS. Defaults to number.
*
*
*/
export
const
getFormatter
=
(
format
=
SUPPORTED_FORMATS
.
number
)
=>
{
// Number
if
(
format
===
SUPPORTED_FORMATS
.
number
)
{
/**
* Formats a number
*
* @function
* @param {Number} value - Number to format
* @param {Number} fractionDigits - precision decimals
* @param {Number} maxLength - Max lenght of formatted number
* if lenght is exceeded, exponential format is used.
*/
return
numberFormatter
();
}
if
(
format
===
SUPPORTED_FORMATS
.
percent
)
{
/**
* Formats a percentge (0 - 1)
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max lenght of formatted number
* if lenght is exceeded, exponential format is used.
*/
return
numberFormatter
(
'
percent
'
);
}
if
(
format
===
SUPPORTED_FORMATS
.
percentHundred
)
{
/**
* Formats a percentge (0 to 100)
*
* @function
* @param {Number} value - Number to format, `100` is rendered as `100%`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max lenght of formatted number
* if lenght is exceeded, exponential format is used.
*/
return
numberFormatter
(
'
percent
'
,
1
/
100
);
}
// Durations
if
(
format
===
SUPPORTED_FORMATS
.
seconds
)
{
/**
* Formats a number of seconds
*
* @function
* @param {Number} value - Number to format, `1` is rendered as `1s`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max lenght of formatted number
* if lenght is exceeded, exponential format is used.
*/
return
suffixFormatter
(
s__
(
'
Units|s
'
));
}
if
(
format
===
SUPPORTED_FORMATS
.
miliseconds
)
{
/**
* Formats a number of miliseconds with ms as units
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1ms`
* @param {Number} fractionDigits - number of precision decimals
* @param {Number} maxLength - Max lenght of formatted number
* if lenght is exceeded, exponential format is used.
*/
return
suffixFormatter
(
s__
(
'
Units|ms
'
));
}
// Digital
if
(
format
===
SUPPORTED_FORMATS
.
bytes
)
{
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
);
}
if
(
format
===
SUPPORTED_FORMATS
.
kilobytes
)
{
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
,
1
);
}
if
(
format
===
SUPPORTED_FORMATS
.
megabytes
)
{
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
,
2
);
}
if
(
format
===
SUPPORTED_FORMATS
.
gigabytes
)
{
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
,
3
);
}
if
(
format
===
SUPPORTED_FORMATS
.
terabytes
)
{
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
,
4
);
}
if
(
format
===
SUPPORTED_FORMATS
.
petabytes
)
{
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
return
scaledSIFormatter
(
'
B
'
,
5
);
}
// Fail so client library addresses issue
throw
TypeError
(
`
${
format
}
is not a valid number format`
);
};
app/assets/javascripts/monitoring/components/charts/time_series.vue
View file @
e06d0e77
...
...
@@ -4,7 +4,7 @@ import { GlLink, GlButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/
import
{
GlAreaChart
,
GlLineChart
,
GlChartSeriesLabel
}
from
'
@gitlab/ui/dist/charts
'
;
import
dateFormat
from
'
dateformat
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
roundOffFloat
}
from
'
~/lib/utils/common_utils
'
;
import
{
getFormatter
}
from
'
~/lib/utils/unit_format
'
;
import
{
getSvgIconPathContent
}
from
'
~/lib/utils/icon_utils
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
{
...
...
@@ -37,6 +37,8 @@ const events = {
datazoom
:
'
datazoom
'
,
};
const
yValFormatter
=
getFormatter
(
'
number
'
);
export
default
{
components
:
{
GlAreaChart
,
...
...
@@ -171,7 +173,7 @@ export default {
boundaryGap
:
[
0.1
,
0.1
],
scale
:
true
,
axisLabel
:
{
formatter
:
num
=>
roundOffFloat
(
num
,
3
).
toString
(
),
formatter
:
num
=>
yValFormatter
(
num
,
3
),
},
...
yAxis
,
};
...
...
@@ -313,7 +315,8 @@ export default {
this
.
tooltip
.
commitUrl
=
deploy
.
commitUrl
;
}
else
{
const
{
seriesName
,
color
,
dataIndex
}
=
dataPoint
;
const
value
=
yVal
.
toFixed
(
3
);
const
value
=
yValFormatter
(
yVal
,
3
);
this
.
tooltip
.
content
.
push
({
name
:
seriesName
,
dataIndex
,
...
...
app/assets/javascripts/notes/components/discussion_resolve_button.vue
View file @
e06d0e77
<
script
>
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
export
default
{
name
:
'
ResolveDiscussionButton
'
,
components
:
{
GlLoadingIcon
,
},
props
:
{
isResolving
:
{
type
:
Boolean
,
...
...
@@ -17,12 +22,7 @@ export default {
<
template
>
<button
ref=
"button"
type=
"button"
class=
"btn btn-default ml-sm-2"
@
click=
"$emit('onClick')"
>
<i
v-if=
"isResolving"
ref=
"isResolvingIcon"
aria-hidden=
"true"
class=
"fa fa-spinner fa-spin"
></i>
<gl-loading-icon
v-if=
"isResolving"
ref=
"isResolvingIcon"
inline
/>
{{
buttonTitle
}}
</button>
</
template
>
app/assets/stylesheets/framework/modal.scss
View file @
e06d0e77
...
...
@@ -13,16 +13,14 @@
.page-title
,
.modal-title
{
max-width
:
100%
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
.modal-title-with-label
span
{
vertical-align
:
middle
;
display
:
inline-block
;
}
.color-label
{
font-size
:
$gl-font-size
;
padding
:
$gl-vert-padding
$label-padding-modal
;
vertical-align
:
middle
;
}
}
.modal-title
{
...
...
app/assets/stylesheets/framework/spinner.scss
View file @
e06d0e77
...
...
@@ -51,7 +51,8 @@
}
.btn
{
.spinner
{
.spinner
,
.gl-spinner
{
vertical-align
:
text-bottom
;
}
}
app/assets/stylesheets/pages/issuable.scss
View file @
e06d0e77
...
...
@@ -86,14 +86,19 @@
}
.issuable-show-labels
{
a
{
.gl-label
{
margin-bottom
:
5px
;
margin-right
:
5px
;
}
a
{
display
:
inline-block
;
.color-label
{
padding
:
4px
$grid-size
;
border-radius
:
$label-border-radius
;
margin-right
:
4px
;
margin-bottom
:
4px
;
}
&
:hover
.color-label
{
...
...
@@ -159,9 +164,25 @@
.avatar
{
border-color
:
rgba
(
$gray-normal
,
0
.2
);
}
}
}
a
.gl-label-icon
{
color
:
$gray-500
;
}
.gl-label
.gl-label-link
:hover
{
text-decoration
:
none
;
color
:
inherit
;
.gl-label-text
:last-of-type
{
text-decoration
:
underline
;
}
}
.gl-label
.gl-label-icon
:hover
{
text-decoration
:
none
;
color
:
$gray-500
;
}
.btn-link
{
...
...
@@ -800,11 +821,23 @@
a
{
color
:
$gl-text-color
;
}
.fa
{
color
:
$gl-text-color-secondary
;
.gl-label-link
{
color
:
inherit
;
&
:hover
{
text-decoration
:
none
;
.gl-label-text
:last-of-type
{
text-decoration
:
underline
;
}
}
}
.gl-label-icon
{
color
:
$gray-500
;
}
}
@media
(
max-width
:
map-get
(
$grid-breakpoints
,
lg
)
-1
)
{
...
...
app/assets/stylesheets/pages/labels.scss
View file @
e06d0e77
...
...
@@ -127,6 +127,11 @@
.color-label
{
padding
:
$gl-padding-4
$grid-size
;
}
.prepend-description-left
{
vertical-align
:
top
;
line-height
:
24px
;
}
}
.prioritized-labels
{
...
...
@@ -305,10 +310,13 @@
width
:
150px
;
flex-shrink
:
0
;
.badge
{
overflow
:
hidden
;
text-overflow
:
ellipsis
;
max-width
:
100%
;
.scoped-label-wrapper
,
.gl-label
{
line-height
:
$gl-line-height
;
}
.gl-label-scoped
.gl-label-text
:last-of-type
{
padding-right
:
22px
;
}
}
...
...
@@ -445,10 +453,19 @@
}
}
.gl-label-scoped
{
box-shadow
:
0
0
0
2px
currentColor
inset
;
&
.gl-label-sm
{
box-shadow
:
0
0
0
1px
inset
;
}
}
// Label inside title of Delete Label Modal
.modal-header
.page-title
{
.scoped-label-wrapper
{
.scoped-label
{
.scoped-label
,
.gl-label-icon
{
line-height
:
20px
;
}
...
...
app/assets/stylesheets/pages/milestone.scss
View file @
e06d0e77
...
...
@@ -59,9 +59,19 @@ $status-box-line-height: 26px;
}
.issuable-row
{
span
a
{
color
:
$gl-text-color
;
word-wrap
:
break-word
;
span
{
a
{
color
:
$gl-text-color
;
word-wrap
:
break-word
;
}
.gl-label-link
{
color
:
inherit
;
}
.gl-label-icon
{
color
:
$gray-500
;
}
}
}
...
...
app/assets/stylesheets/pages/notes.scss
View file @
e06d0e77
...
...
@@ -283,7 +283,7 @@ $note-form-margin-left: 72px;
text-transform
:
lowercase
;
}
a
{
a
:not
(
.gl-link
)
{
color
:
$blue-600
;
}
...
...
@@ -671,6 +671,16 @@ $note-form-margin-left: 72px;
a
:hover
{
text-decoration
:
underline
;
}
.gl-label-link
:hover
,
.gl-label-icon
:hover
{
text-decoration
:
none
;
color
:
inherit
;
.gl-label-text
:last-of-type
{
text-decoration
:
underline
;
}
}
}
/**
...
...
app/helpers/labels_helper.rb
View file @
e06d0e77
...
...
@@ -36,37 +36,42 @@ module LabelsHelper
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
def
link_to_label
(
label
,
type: :issue
,
tooltip:
true
,
css_class:
nil
,
&
block
)
def
link_to_label
(
label
,
type: :issue
,
tooltip:
true
,
small:
false
,
&
block
)
link
=
label
.
filter_path
(
type:
type
)
if
block_given?
link_to
link
,
class:
css_class
,
&
block
link_to
link
,
&
block
else
render_label
(
label
,
tooltip:
tooltip
,
link:
link
,
css:
css_class
)
render_label
(
label
,
link:
link
,
tooltip:
tooltip
,
small:
small
)
end
end
def
render_label
(
label
,
tooltip:
true
,
link:
nil
,
css:
nil
,
dataset:
nil
)
# if scoped label is used then EE wraps label tag with scoped label
# doc link
html
=
render_colored_label
(
label
,
tooltip:
tooltip
)
html
=
link_to
(
html
,
link
,
class:
css
,
data:
dataset
)
if
link
def
render_label
(
label
,
link:
nil
,
tooltip:
true
,
dataset:
nil
,
small:
false
)
html
=
render_colored_label
(
label
)
html
if
link
title
=
label_tooltip_title
(
label
)
if
tooltip
html
=
render_label_link
(
html
,
link:
link
,
title:
title
,
dataset:
dataset
)
end
wrap_label_html
(
html
,
small:
small
,
label:
label
)
end
def
render_colored_label
(
label
,
label_suffix:
''
,
tooltip:
true
,
title:
nil
)
text_color
=
text_color_for_bg
(
label
.
color
)
title
||=
tooltip
?
label_tooltip_title
(
label
)
:
label
.
name
def
render_colored_label
(
label
,
suffix:
''
)
render_label_text
(
label
.
name
,
suffix:
suffix
,
css_class:
text_color_class_for_bg
(
label
.
color
),
bg_color:
label
.
color
)
end
# Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter
span
=
%(<span class="badge color-label #{"has-tooltip" if tooltip}" )
+
%(data-html="true" style="background-color: #{label.color}; color: #{text_color}" )
+
%(title="#{ERB::Util.html_escape_once(title)}" data-container="body">)
+
%(#{ERB::Util.html_escape_once(label.name)}#{label_suffix}</span>)
# We need the `label` argument here for EE
def
wrap_label_html
(
label_html
,
small
:,
label
:)
wrapper_classes
=
%w(gl-label)
wrapper_classes
<<
'gl-label-sm'
if
small
span
.
html_safe
%(<span class="#{wrapper_classes.join(' ')}">#{label_html}</span>)
.
html_safe
end
def
label_tooltip_title
(
label
)
...
...
@@ -109,6 +114,20 @@ module LabelsHelper
end
end
def
text_color_class_for_bg
(
bg_color
)
if
bg_color
.
length
==
4
r
,
g
,
b
=
bg_color
[
1
,
4
].
scan
(
/./
).
map
{
|
v
|
(
v
*
2
).
hex
}
else
r
,
g
,
b
=
bg_color
[
1
,
7
].
scan
(
/.{2}/
).
map
(
&
:hex
)
end
if
(
r
+
g
+
b
)
>
500
'gl-label-text-dark'
else
'gl-label-text-light'
end
end
def
text_color_for_bg
(
bg_color
)
if
bg_color
.
length
==
4
r
,
g
,
b
=
bg_color
[
1
,
4
].
scan
(
/./
).
map
{
|
v
|
(
v
*
2
).
hex
}
...
...
@@ -246,6 +265,31 @@ module LabelsHelper
def
issuable_types
[
'issues'
,
'merge requests'
]
end
private
def
render_label_link
(
label_html
,
link
:,
title
:,
dataset
:)
classes
=
%w(gl-link gl-label-link)
dataset
||=
{}
if
title
.
present?
classes
<<
'has-tooltip'
dataset
.
merge!
(
html:
true
,
title:
title
)
end
link_to
(
label_html
,
link
,
class:
classes
.
join
(
' '
),
data:
dataset
)
end
def
render_label_text
(
name
,
suffix:
''
,
css_class:
nil
,
bg_color:
nil
)
<<~
HTML
.
chomp
.
html_safe
<span
class="gl-label-text
#{
css_class
}
"
data-container="body"
data-html="true"
#{
"style=
\"
background-color:
#{
bg_color
}
\"
"
if
bg_color
}
>
#{
ERB
::
Util
.
html_escape_once
(
name
)
}#{
suffix
}
</span>
HTML
end
end
LabelsHelper
.
prepend_if_ee
(
'EE::LabelsHelper'
)
app/models/clusters/cluster.rb
View file @
e06d0e77
...
...
@@ -11,15 +11,15 @@ module Clusters
self
.
table_name
=
'clusters'
APPLICATIONS
=
{
Applications
::
Helm
.
application_name
=>
Applications
::
Helm
,
Applications
::
Ingress
.
application_name
=>
Applications
::
Ingress
,
Applications
::
CertManager
.
application_name
=>
Applications
::
CertManager
,
Applications
::
Crossplane
.
application_name
=>
Applications
::
Crossplane
,
Applications
::
Prometheus
.
application_name
=>
Applications
::
Prometheus
,
Applications
::
Runner
.
application_name
=>
Applications
::
Runner
,
Applications
::
Jupyter
.
application_name
=>
Applications
::
Jupyter
,
Applications
::
Knative
.
application_name
=>
Applications
::
Knative
,
Applications
::
ElasticStack
.
application_name
=>
Applications
::
ElasticStack
Clusters
::
Applications
::
Helm
.
application_name
=>
Clusters
::
Applications
::
Helm
,
Clusters
::
Applications
::
Ingress
.
application_name
=>
Clusters
::
Applications
::
Ingress
,
Clusters
::
Applications
::
CertManager
.
application_name
=>
Clusters
::
Applications
::
CertManager
,
Clusters
::
Applications
::
Crossplane
.
application_name
=>
Clusters
::
Applications
::
Crossplane
,
Clusters
::
Applications
::
Prometheus
.
application_name
=>
Clusters
::
Applications
::
Prometheus
,
Clusters
::
Applications
::
Runner
.
application_name
=>
Clusters
::
Applications
::
Runner
,
Clusters
::
Applications
::
Jupyter
.
application_name
=>
Clusters
::
Applications
::
Jupyter
,
Clusters
::
Applications
::
Knative
.
application_name
=>
Clusters
::
Applications
::
Knative
,
Clusters
::
Applications
::
ElasticStack
.
application_name
=>
Clusters
::
Applications
::
ElasticStack
}.
freeze
DEFAULT_ENVIRONMENT
=
'*'
KUBE_INGRESS_BASE_DOMAIN
=
'KUBE_INGRESS_BASE_DOMAIN'
...
...
app/services/issuable/common_system_notes_service.rb
View file @
e06d0e77
...
...
@@ -101,7 +101,7 @@ module Issuable
def
create_milestone_note
if
milestone_changes_tracking_enabled?
# Creates a synthetic note
ResourceEvents
::
ChangeMilestoneService
.
new
(
resource:
issuable
,
user:
current_user
).
execute
ResourceEvents
::
ChangeMilestoneService
.
new
(
issuable
,
current_user
).
execute
else
SystemNoteService
.
change_milestone
(
issuable
,
issuable
.
project
,
current_user
,
issuable
.
milestone
)
end
...
...
app/services/resource_events/change_milestone_service.rb
View file @
e06d0e77
...
...
@@ -4,7 +4,7 @@ module ResourceEvents
class
ChangeMilestoneService
attr_reader
:resource
,
:user
,
:event_created_at
,
:milestone
def
initialize
(
resource
:,
user
:
,
created_at:
Time
.
now
)
def
initialize
(
resource
,
user
,
created_at:
Time
.
now
)
@resource
=
resource
@user
=
user
@event_created_at
=
created_at
...
...
app/views/projects/issues/_issue.html.haml
View file @
e06d0e77
...
...
@@ -38,7 +38,7 @@
-
if
issue
.
labels
.
any?
-
presented_labels_sorted_by_title
(
issue
.
labels
,
issue
.
project
).
each
do
|
label
|
=
link_to_label
(
label
,
css_class:
'label-link'
)
=
link_to_label
(
label
,
small:
true
)
=
render_if_exists
"projects/issues/issue_weight"
,
issue:
issue
...
...
app/views/projects/merge_requests/_merge_request.html.haml
View file @
e06d0e77
...
...
@@ -35,7 +35,7 @@
-
if
merge_request
.
labels
.
any?
-
presented_labels_sorted_by_title
(
merge_request
.
labels
,
merge_request
.
project
).
each
do
|
label
|
=
link_to_label
(
label
,
type: :merge_request
,
css_class:
'label-link'
)
=
link_to_label
(
label
,
type: :merge_request
,
small:
true
)
.issuable-meta
%ul
.controls.d-flex.align-items-end
...
...
app/views/shared/_delete_label_modal.html.haml
View file @
e06d0e77
...
...
@@ -2,7 +2,7 @@
.modal-dialog
.modal-content
.modal-header
%h3
.page-title
Delete
#{
render_label
(
label
,
tooltip:
false
)
}
?
%h3
.page-title
Delete
label:
#{
label
.
name
}
?
%button
.close
{
type:
"button"
,
"data-dismiss"
:
"modal"
,
"aria-label"
=>
_
(
'Close'
)
}
%span
{
"aria-hidden"
:
true
}
×
...
...
app/views/shared/boards/components/_board.html.haml
View file @
e06d0e77
...
...
@@ -29,11 +29,14 @@
":title"
=>
'(list.assignee && list.assignee.username || "")'
}
@{{ list.assignee.username }}
%span
.has-tooltip.badge.color-label.title.d-inline-block.mw-100.text-truncate.align-middle
{
"v-if"
:
"list.type ===
\"
label
\"
"
,
":title"
=>
'(list.label ? list.label.description : "")'
,
data:
{
container:
"body"
,
placement:
"bottom"
},
":style"
=>
"{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.textColor ? list.label.textColor :
\"
#2e2e2e
\"
) }"
}
{{ list.title }}
%gl-label
{
"v-if"
=>
" list.type ===
\"
label
\"
"
,
":background-color"
=>
"list.label.color"
,
":title"
=>
"list.label.title"
,
":description"
=>
"list.label.description"
,
"tooltipPlacement"
=>
"bottom"
,
":size"
=>
'(!list.isExpanded ? "sm" : "")'
,
":scoped"
=>
"showScopedLabels(list.label)"
,
":scoped-labels-documentation-link"
=>
"helpLink"
}
-
if
can?
(
current_user
,
:admin_list
,
current_board_parent
)
%board-delete
{
"inline-template"
=>
true
,
...
...
app/views/shared/boards/components/sidebar/_labels.html.haml
View file @
e06d0e77
...
...
@@ -8,15 +8,12 @@
%span
.no-value
{
"v-if"
=>
"issue.labels && issue.labels.length === 0"
}
=
_
(
"None"
)
%span
{
"v-for"
=>
"label in issue.labels"
}
%span
.d-inline-block.position-relative.scoped-label-wrapper
{
"v-if"
=>
"showScopedLabels(label)"
}
%a
{
href:
'#'
}
%span
.badge.color-label.label
{
":style"
=>
"{ backgroundColor: label.color, color: label.textColor }"
}
{{ label.title }}
%a
.label.scoped-label
{
":href"
=>
"helpLink()"
}
%i
.fa.fa-question-circle
{
":style"
=>
"{ backgroundColor: label.color, color: label.textColor }"
}
%a
{
href:
"#"
,
"v-else"
=>
true
}
.badge.color-label.has-tooltip
{
":style"
=>
"{ backgroundColor: label.color, color: label.textColor }"
}
{{ label.title }}
%gl-label
{
":key"
=>
"label.id"
,
":background-color"
=>
"label.color"
,
":title"
=>
"label.title"
,
":description"
=>
"label.description"
,
":scoped"
=>
"showScopedLabels(label)"
,
":scoped-labels-documentation-link"
=>
"helpLink"
}
-
if
can_admin_issue?
.selectbox
...
...
app/views/shared/milestones/_issuable.html.haml
View file @
e06d0e77
...
...
@@ -21,7 +21,7 @@
%span
.issuable-number
=
issuable
.
to_reference
-
labels
.
each
do
|
label
|
=
render_label
(
label
.
present
(
issuable_subject:
project
),
link:
polymorphic_path
(
issuable_type_args
,
{
milestone_title:
@milestone
.
title
,
label_name:
label
.
title
,
state:
'all'
}))
=
render_label
(
label
.
present
(
issuable_subject:
project
),
link:
polymorphic_path
(
issuable_type_args
,
{
milestone_title:
@milestone
.
title
,
label_name:
label
.
title
,
state:
'all'
})
,
small:
true
)
%span
.assignee-icon
-
assignees
.
each
do
|
assignee
|
...
...
app/views/shared/milestones/_labels_tab.html.haml
View file @
e06d0e77
...
...
@@ -3,11 +3,9 @@
-
options
=
{
milestone_title:
@milestone
.
title
,
label_name:
label
.
title
}
%li
.no-border
%span
.label-row
%span
.label-name
=
render_label
(
label
,
tooltip:
false
,
link:
milestones_label_path
(
options
))
%span
.prepend-description-left
=
markdown_field
(
label
,
:description
)
=
render_label
(
label
,
tooltip:
false
,
link:
milestones_label_path
(
options
))
%span
.prepend-description-left
=
markdown_field
(
label
,
:description
)
.float-right.d-none.d-lg-block.d-xl-block
=
link_to
milestones_label_path
(
options
.
merge
(
state:
'opened'
)),
class:
'btn btn-transparent btn-action'
do
...
...
changelogs/unreleased/196648-replace-_-with-lodash.yml
0 → 100644
View file @
e06d0e77
---
title
:
Replaced underscore with lodash for app/assets/javascripts/lib
merge_request
:
25042
author
:
Shubham Pandey
type
:
other
changelogs/unreleased/38144-replace-labels-in-haml-with-gitlab-ui-css.yml
0 → 100644
View file @
e06d0e77
---
title
:
New styles for scoped labels
merge_request
:
21377
author
:
type
:
changed
changelogs/unreleased/Resolve-Migrate--fa-spinner-app-assets-javascripts-notes-components-discu.yml
0 → 100644
View file @
e06d0e77
---
title
:
Migrate .fa-spinner to .spinner for app/assets/javascripts/notes/components/discussion_resolve_button.vue
merge_request
:
25055
author
:
nuwe1
type
:
other
lib/banzai/filter/label_reference_filter.rb
View file @
e06d0e77
...
...
@@ -93,23 +93,26 @@ module Banzai
end
presenter
=
object
.
present
(
issuable_subject:
parent
)
LabelsHelper
.
render_colored_label
(
presenter
,
label_suffix:
label_suffix
,
title:
tooltip_title
(
presenter
)
)
LabelsHelper
.
render_colored_label
(
presenter
,
suffix:
label_suffix
)
end
def
tooltip_title
(
label
)
nil
def
wrap_link
(
link
,
label
)
presenter
=
label
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
wrap_label_html
(
link
,
small:
true
,
label:
presenter
)
end
def
full_path_ref?
(
matches
)
matches
[
:namespace
]
&&
matches
[
:project
]
end
def
reference_class
(
type
,
tooltip:
true
)
super
+
' gl-link gl-label-link'
end
def
object_link_title
(
object
,
matches
)
# use title of wrapped element instead
nil
presenter
=
object
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
label_tooltip_title
(
presenter
)
end
end
end
end
Banzai
::
Filter
::
LabelReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::LabelReferenceFilter'
)
lib/banzai/filter/reference_filter.rb
View file @
e06d0e77
...
...
@@ -37,7 +37,8 @@ module Banzai
attributes
[
:reference_type
]
||=
self
.
class
.
reference_type
attributes
[
:container
]
||=
'body'
attributes
[
:placement
]
||=
'bottom'
attributes
[
:placement
]
||=
'top'
attributes
[
:html
]
||=
'true'
attributes
.
delete
(
:original
)
if
context
[
:no_original_data
]
attributes
.
map
do
|
key
,
value
|
%Q(data-
#{
key
.
to_s
.
dasherize
}
="
#{
escape_once
(
value
)
}
")
...
...
lib/gitlab/markdown_cache.rb
View file @
e06d0e77
...
...
@@ -3,7 +3,7 @@
module
Gitlab
module
MarkdownCache
# Increment this number every time the renderer changes its output
CACHE_COMMONMARK_VERSION
=
1
8
CACHE_COMMONMARK_VERSION
=
1
9
CACHE_COMMONMARK_VERSION_START
=
10
BaseError
=
Class
.
new
(
StandardError
)
...
...
locale/gitlab.pot
View file @
e06d0e77
...
...
@@ -5848,6 +5848,9 @@ msgstr ""
msgid "CustomCycleAnalytics|Select stop event"
msgstr ""
msgid "CustomCycleAnalytics|Stage name already exists"
msgstr ""
msgid "CustomCycleAnalytics|Start event"
msgstr ""
...
...
@@ -18564,9 +18567,6 @@ msgstr ""
msgid "Subscription deletion failed."
msgstr ""
msgid "Subscription successfully applied to \"%{group_name}\""
msgstr ""
msgid "Subscription successfully created."
msgstr ""
...
...
@@ -20756,6 +20756,12 @@ msgstr ""
msgid "Uninstalling"
msgstr ""
msgid "Units|ms"
msgstr ""
msgid "Units|s"
msgstr ""
msgid "Unknown"
msgstr ""
...
...
spec/features/boards/sidebar_spec.rb
View file @
e06d0e77
...
...
@@ -277,7 +277,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_selector
(
'.
badge
'
,
count:
2
)
expect
(
page
).
to
have_selector
(
'.
gl-label-text
'
,
count:
2
)
expect
(
page
).
to
have_content
(
development
.
title
)
expect
(
page
).
to
have_content
(
stretch
.
title
)
end
...
...
@@ -299,7 +299,7 @@ describe 'Issue Boards', :js do
find
(
'.dropdown-menu-close-icon'
).
click
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_selector
(
'.
badge
'
,
count:
3
)
expect
(
page
).
to
have_selector
(
'.
gl-label-text
'
,
count:
3
)
expect
(
page
).
to
have_content
(
bug
.
title
)
end
end
...
...
@@ -328,7 +328,7 @@ describe 'Issue Boards', :js do
find
(
'.dropdown-menu-close-icon'
).
click
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_selector
(
'.
badge
'
,
count:
4
)
expect
(
page
).
to
have_selector
(
'.
gl-label-text
'
,
count:
4
)
expect
(
page
).
to
have_content
(
bug
.
title
)
expect
(
page
).
to
have_content
(
regression
.
title
)
end
...
...
@@ -357,13 +357,13 @@ describe 'Issue Boards', :js do
find
(
'.dropdown-menu-close-icon'
).
click
page
.
within
(
'.value'
)
do
expect
(
page
).
to
have_selector
(
'.
badge
'
,
count:
1
)
expect
(
page
).
to
have_selector
(
'.
gl-label-text
'
,
count:
1
)
expect
(
page
).
not_to
have_content
(
stretch
.
title
)
end
end
# 'Development' label does not show since the card is in a 'Development' list label
expect
(
card
).
to
have_selector
(
'.
badge
'
,
count:
0
)
expect
(
card
).
to
have_selector
(
'.
gl-label-text
'
,
count:
0
)
expect
(
card
).
not_to
have_content
(
stretch
.
title
)
end
...
...
spec/features/container_registry_spec.rb
→
spec/features/
groups/
container_registry_spec.rb
View file @
e06d0e77
...
...
@@ -4,30 +4,31 @@ require 'spec_helper'
describe
'Container Registry'
,
:js
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
namespace:
group
)
}
let
(
:container_repository
)
do
create
(
:container_repository
,
name:
'my/image'
)
end
before
do
group
.
add_owner
(
user
)
sign_in
(
user
)
project
.
add_developer
(
user
)
stub_container_registry_config
(
enabled:
true
)
stub_container_registry_tags
(
repository: :any
,
tags:
[])
stub_feature_flags
(
vue_container_registry_explorer:
false
)
end
it
'has a page title set'
do
visit_container_registry
expect
(
page
).
to
have_title
(
_
(
'Container Registry'
))
expect
(
page
).
to
have_title
_
(
'Container Registry'
)
end
context
'when there are no image repositories'
do
it
'
user visits container registry main pag
e'
do
it
'
list page has no container titl
e'
do
visit_container_registry
expect
(
page
).
to
have_content
'no container images'
expect
(
page
).
to
have_content
_
(
'There are no container images available in this group'
)
end
end
...
...
@@ -37,39 +38,56 @@ describe 'Container Registry', :js do
project
.
container_repositories
<<
container_repository
end
it
'
user wants to see multi-level container repository
'
do
it
'
list page has a list of images
'
do
visit_container_registry
expect
(
page
).
to
have_content
(
'my/image'
)
expect
(
page
).
to
have_content
'my/image'
end
it
'
user removes entire container repository'
,
:sidekiq_might_not_need_inline
do
it
'
image repository delete is disabled'
do
visit_container_registry
expect_any_instance_of
(
ContainerRepository
).
to
receive
(
:delete_tags!
).
and_return
(
true
)
delete_btn
=
find
(
'[title="Remove repository"]'
)
expect
(
delete_btn
).
to
be_disabled
end
it
'navigates to repo details'
do
visit_container_registry_details
(
'my/image'
)
click_on
(
class:
'js-remove-repo'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
'Remove repository'
find
(
'.modal .modal-footer .btn-danger'
).
click
expect
(
page
).
to
have_content
'latest'
end
it
'user removes a specific tag from container repository'
do
visit_container_registry
describe
'image repo details'
do
before
do
visit_container_registry_details
'my/image'
end
find
(
'.js-toggle-repo'
).
click
wait_for_requests
it
'shows the details breadcrumb'
do
expect
(
find
(
'.breadcrumbs'
)).
to
have_link
'my/image'
end
service
=
double
(
'service'
)
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
e
xpect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
it
'shows the image title'
do
expect
(
page
).
to
have_content
'my/image tags'
e
nd
click_on
(
class:
'js-delete-registry-row'
,
visible:
false
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
'Remove tag'
find
(
'.modal .modal-footer .btn-danger'
).
click
it
'user removes a specific tag from container repository'
do
service
=
double
(
'service'
)
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
click_on
(
class:
'js-delete-registry'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
end
end
end
def
visit_container_registry
visit
project_container_registry_index_path
(
project
)
visit
group_container_registries_path
(
group
)
end
def
visit_container_registry_details
(
name
)
visit_container_registry
click_link
(
name
)
end
end
spec/features/issuables/issuable_list_spec.rb
View file @
e06d0e77
...
...
@@ -41,10 +41,10 @@ describe 'issuable list' do
visit_issuable_list
(
issuable_type
)
expect
(
all
(
'.
label-link
'
)[
0
].
text
).
to
have_content
(
'B'
)
expect
(
all
(
'.
label-link
'
)[
1
].
text
).
to
have_content
(
'X'
)
expect
(
all
(
'.
label-link
'
)[
2
].
text
).
to
have_content
(
'a'
)
expect
(
all
(
'.
label-link
'
)[
3
].
text
).
to
have_content
(
'z'
)
expect
(
all
(
'.
gl-label-text
'
)[
0
].
text
).
to
have_content
(
'B'
)
expect
(
all
(
'.
gl-label-text
'
)[
1
].
text
).
to
have_content
(
'X'
)
expect
(
all
(
'.
gl-label-text
'
)[
2
].
text
).
to
have_content
(
'a'
)
expect
(
all
(
'.
gl-label-text
'
)[
3
].
text
).
to
have_content
(
'z'
)
end
end
...
...
spec/features/issues/filtered_search/filter_issues_spec.rb
View file @
e06d0e77
...
...
@@ -332,7 +332,7 @@ describe 'Filter issues', :js do
context
'issue label clicked'
do
it
'filters and displays in search bar'
do
find
(
'.issues-list .issue .issuable-main-info .issuable-info a .
badge
'
,
text:
multiple_words_label
.
title
).
click
find
(
'.issues-list .issue .issuable-main-info .issuable-info a .
gl-label-text
'
,
text:
multiple_words_label
.
title
).
click
expect_issues_list_count
(
1
)
expect_tokens
([
label_token
(
"
\"
#{
multiple_words_label
.
title
}
\"
"
)])
...
...
spec/features/labels_hierarchy_spec.rb
View file @
e06d0e77
...
...
@@ -161,9 +161,9 @@ describe 'Labels Hierarchy', :js do
find
(
'.btn-success'
).
click
expect
(
page
.
find
(
'.issue-details h2.title'
)).
to
have_content
(
'new created issue'
)
expect
(
page
).
to
have_selector
(
'span.
badge
'
,
text:
grandparent_group_label
.
title
)
expect
(
page
).
to
have_selector
(
'span.
badge
'
,
text:
parent_group_label
.
title
)
expect
(
page
).
to
have_selector
(
'span.
badge
'
,
text:
project_label_1
.
title
)
expect
(
page
).
to
have_selector
(
'span.
gl-label-text
'
,
text:
grandparent_group_label
.
title
)
expect
(
page
).
to
have_selector
(
'span.
gl-label-text
'
,
text:
parent_group_label
.
title
)
expect
(
page
).
to
have_selector
(
'span.
gl-label-text
'
,
text:
project_label_1
.
title
)
end
end
...
...
spec/features/projects/container_registry_spec.rb
0 → 100644
View file @
e06d0e77
# frozen_string_literal: true
require
'spec_helper'
describe
'Container Registry'
,
:js
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:container_repository
)
do
create
(
:container_repository
,
name:
'my/image'
)
end
before
do
sign_in
(
user
)
project
.
add_developer
(
user
)
stub_container_registry_config
(
enabled:
true
)
stub_container_registry_tags
(
repository: :any
,
tags:
[])
end
describe
'Registry explorer is off'
do
before
do
stub_feature_flags
(
vue_container_registry_explorer:
false
)
end
it
'has a page title set'
do
visit_container_registry
expect
(
page
).
to
have_title
_
(
'Container Registry'
)
end
context
'when there are no image repositories'
do
it
'user visits container registry main page'
do
visit_container_registry
expect
(
page
).
to
have_content
_
(
'no container images'
)
end
end
context
'when there are image repositories'
do
before
do
stub_container_registry_tags
(
repository:
%r{my/image}
,
tags:
%w[latest]
,
with_manifest:
true
)
project
.
container_repositories
<<
container_repository
end
it
'user wants to see multi-level container repository'
do
visit_container_registry
expect
(
page
).
to
have_content
'my/image'
end
it
'user removes entire container repository'
,
:sidekiq_might_not_need_inline
do
visit_container_registry
expect_any_instance_of
(
ContainerRepository
).
to
receive
(
:delete_tags!
).
and_return
(
true
)
click_on
(
class:
'js-remove-repo'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove repository'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
end
it
'user removes a specific tag from container repository'
do
visit_container_registry
find
(
'.js-toggle-repo'
).
click
wait_for_requests
service
=
double
(
'service'
)
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
click_on
(
class:
'js-delete-registry-row'
,
visible:
false
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
end
end
end
describe
'Registry explorer is on'
do
it
'has a page title set'
do
visit_container_registry
expect
(
page
).
to
have_title
_
(
'Container Registry'
)
end
context
'when there are no image repositories'
do
it
'list page has no container title'
do
visit_container_registry
expect
(
page
).
to
have_content
_
(
'There are no container images stored for this project'
)
end
it
'list page has quickstart'
do
visit_container_registry
expect
(
page
).
to
have_content
_
(
'Quick Start'
)
end
end
context
'when there are image repositories'
do
before
do
stub_container_registry_tags
(
repository:
%r{my/image}
,
tags:
%w[latest]
,
with_manifest:
true
)
project
.
container_repositories
<<
container_repository
end
it
'list page has a list of images'
do
visit_container_registry
expect
(
page
).
to
have_content
'my/image'
end
it
'user removes entire container repository'
,
:sidekiq_might_not_need_inline
do
visit_container_registry
expect_any_instance_of
(
ContainerRepository
).
to
receive
(
:delete_tags!
).
and_return
(
true
)
find
(
'[title="Remove repository"]'
).
click
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove repository'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
end
it
'navigates to repo details'
do
visit_container_registry_details
(
'my/image'
)
expect
(
page
).
to
have_content
'latest'
end
describe
'image repo details'
do
before
do
visit_container_registry_details
'my/image'
end
it
'shows the details breadcrumb'
do
expect
(
find
(
'.breadcrumbs'
)).
to
have_link
'my/image'
end
it
'shows the image title'
do
expect
(
page
).
to
have_content
'my/image tags'
end
it
'user removes a specific tag from container repository'
do
service
=
double
(
'service'
)
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
click_on
(
class:
'js-delete-registry'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
end
end
end
end
def
visit_container_registry
visit
project_container_registry_index_path
(
project
)
end
def
visit_container_registry_details
(
name
)
visit_container_registry
click_link
(
name
)
end
end
spec/frontend/lib/utils/unit_format/formatter_factory_spec.js
0 → 100644
View file @
e06d0e77
import
{
numberFormatter
,
suffixFormatter
,
scaledSIFormatter
,
}
from
'
~/lib/utils/unit_format/formatter_factory
'
;
describe
(
'
unit_format/formatter_factory
'
,
()
=>
{
describe
(
'
numberFormatter
'
,
()
=>
{
let
formatNumber
;
beforeEach
(()
=>
{
formatNumber
=
numberFormatter
();
});
it
(
'
formats a integer
'
,
()
=>
{
expect
(
formatNumber
(
1
)).
toEqual
(
'
1
'
);
expect
(
formatNumber
(
100
)).
toEqual
(
'
100
'
);
expect
(
formatNumber
(
1000
)).
toEqual
(
'
1,000
'
);
expect
(
formatNumber
(
10000
)).
toEqual
(
'
10,000
'
);
expect
(
formatNumber
(
1000000
)).
toEqual
(
'
1,000,000
'
);
});
it
(
'
formats a floating point number
'
,
()
=>
{
expect
(
formatNumber
(
0.1
)).
toEqual
(
'
0.1
'
);
expect
(
formatNumber
(
0.1
,
0
)).
toEqual
(
'
0
'
);
expect
(
formatNumber
(
0.1
,
2
)).
toEqual
(
'
0.10
'
);
expect
(
formatNumber
(
0.1
,
3
)).
toEqual
(
'
0.100
'
);
expect
(
formatNumber
(
12.345
)).
toEqual
(
'
12.345
'
);
expect
(
formatNumber
(
12.345
,
2
)).
toEqual
(
'
12.35
'
);
expect
(
formatNumber
(
12.345
,
4
)).
toEqual
(
'
12.3450
'
);
});
it
(
'
formats a large integer with a length limit
'
,
()
=>
{
expect
(
formatNumber
(
10
**
7
,
undefined
)).
toEqual
(
'
10,000,000
'
);
expect
(
formatNumber
(
10
**
7
,
undefined
,
9
)).
toEqual
(
'
1.00e+7
'
);
expect
(
formatNumber
(
10
**
7
,
undefined
,
10
)).
toEqual
(
'
10,000,000
'
);
});
});
describe
(
'
suffixFormatter
'
,
()
=>
{
let
formatSuffix
;
beforeEach
(()
=>
{
formatSuffix
=
suffixFormatter
(
'
pop.
'
,
undefined
);
});
it
(
'
formats a integer
'
,
()
=>
{
expect
(
formatSuffix
(
1
)).
toEqual
(
'
1pop.
'
);
expect
(
formatSuffix
(
100
)).
toEqual
(
'
100pop.
'
);
expect
(
formatSuffix
(
1000
)).
toEqual
(
'
1,000pop.
'
);
expect
(
formatSuffix
(
10000
)).
toEqual
(
'
10,000pop.
'
);
expect
(
formatSuffix
(
1000000
)).
toEqual
(
'
1,000,000pop.
'
);
});
it
(
'
formats a floating point number
'
,
()
=>
{
expect
(
formatSuffix
(
0.1
)).
toEqual
(
'
0.1pop.
'
);
expect
(
formatSuffix
(
0.1
,
0
)).
toEqual
(
'
0pop.
'
);
expect
(
formatSuffix
(
0.1
,
2
)).
toEqual
(
'
0.10pop.
'
);
expect
(
formatSuffix
(
0.1
,
3
)).
toEqual
(
'
0.100pop.
'
);
expect
(
formatSuffix
(
12.345
)).
toEqual
(
'
12.345pop.
'
);
expect
(
formatSuffix
(
12.345
,
2
)).
toEqual
(
'
12.35pop.
'
);
expect
(
formatSuffix
(
12.345
,
4
)).
toEqual
(
'
12.3450pop.
'
);
});
it
(
'
formats a negative integer
'
,
()
=>
{
expect
(
formatSuffix
(
-
1
)).
toEqual
(
'
-1pop.
'
);
expect
(
formatSuffix
(
-
100
)).
toEqual
(
'
-100pop.
'
);
expect
(
formatSuffix
(
-
1000
)).
toEqual
(
'
-1,000pop.
'
);
expect
(
formatSuffix
(
-
10000
)).
toEqual
(
'
-10,000pop.
'
);
expect
(
formatSuffix
(
-
1000000
)).
toEqual
(
'
-1,000,000pop.
'
);
});
it
(
'
formats a floating point nugative number
'
,
()
=>
{
expect
(
formatSuffix
(
-
0.1
)).
toEqual
(
'
-0.1pop.
'
);
expect
(
formatSuffix
(
-
0.1
,
0
)).
toEqual
(
'
-0pop.
'
);
expect
(
formatSuffix
(
-
0.1
,
2
)).
toEqual
(
'
-0.10pop.
'
);
expect
(
formatSuffix
(
-
0.1
,
3
)).
toEqual
(
'
-0.100pop.
'
);
expect
(
formatSuffix
(
-
12.345
)).
toEqual
(
'
-12.345pop.
'
);
expect
(
formatSuffix
(
-
12.345
,
2
)).
toEqual
(
'
-12.35pop.
'
);
expect
(
formatSuffix
(
-
12.345
,
4
)).
toEqual
(
'
-12.3450pop.
'
);
});
it
(
'
formats a large integer
'
,
()
=>
{
expect
(
formatSuffix
(
10
**
7
)).
toEqual
(
'
10,000,000pop.
'
);
expect
(
formatSuffix
(
10
**
10
)).
toEqual
(
'
10,000,000,000pop.
'
);
});
it
(
'
formats a large integer with a length limit
'
,
()
=>
{
expect
(
formatSuffix
(
10
**
7
,
undefined
,
10
)).
toEqual
(
'
1.00e+7pop.
'
);
expect
(
formatSuffix
(
10
**
10
,
undefined
,
10
)).
toEqual
(
'
1.00e+10pop.
'
);
});
});
describe
(
'
scaledSIFormatter
'
,
()
=>
{
describe
(
'
scaled format
'
,
()
=>
{
let
formatScaled
;
beforeEach
(()
=>
{
formatScaled
=
scaledSIFormatter
(
'
B
'
);
});
it
(
'
formats bytes
'
,
()
=>
{
expect
(
formatScaled
(
12.345
)).
toEqual
(
'
12.345B
'
);
expect
(
formatScaled
(
12.345
,
0
)).
toEqual
(
'
12B
'
);
expect
(
formatScaled
(
12.345
,
1
)).
toEqual
(
'
12.3B
'
);
expect
(
formatScaled
(
12.345
,
2
)).
toEqual
(
'
12.35B
'
);
});
it
(
'
formats bytes in a scale
'
,
()
=>
{
expect
(
formatScaled
(
1
)).
toEqual
(
'
1B
'
);
expect
(
formatScaled
(
10
)).
toEqual
(
'
10B
'
);
expect
(
formatScaled
(
10
**
2
)).
toEqual
(
'
100B
'
);
expect
(
formatScaled
(
10
**
3
)).
toEqual
(
'
1kB
'
);
expect
(
formatScaled
(
10
**
4
)).
toEqual
(
'
10kB
'
);
expect
(
formatScaled
(
10
**
5
)).
toEqual
(
'
100kB
'
);
expect
(
formatScaled
(
10
**
6
)).
toEqual
(
'
1MB
'
);
expect
(
formatScaled
(
10
**
7
)).
toEqual
(
'
10MB
'
);
expect
(
formatScaled
(
10
**
8
)).
toEqual
(
'
100MB
'
);
expect
(
formatScaled
(
10
**
9
)).
toEqual
(
'
1GB
'
);
expect
(
formatScaled
(
10
**
10
)).
toEqual
(
'
10GB
'
);
expect
(
formatScaled
(
10
**
11
)).
toEqual
(
'
100GB
'
);
});
});
describe
(
'
scaled format with offset
'
,
()
=>
{
let
formatScaled
;
beforeEach
(()
=>
{
// formats gigabytes
formatScaled
=
scaledSIFormatter
(
'
B
'
,
3
);
});
it
(
'
formats floating point numbers
'
,
()
=>
{
expect
(
formatScaled
(
12.345
)).
toEqual
(
'
12.345GB
'
);
expect
(
formatScaled
(
12.345
,
0
)).
toEqual
(
'
12GB
'
);
expect
(
formatScaled
(
12.345
,
1
)).
toEqual
(
'
12.3GB
'
);
expect
(
formatScaled
(
12.345
,
2
)).
toEqual
(
'
12.35GB
'
);
});
it
(
'
formats large numbers scaled
'
,
()
=>
{
expect
(
formatScaled
(
1
)).
toEqual
(
'
1GB
'
);
expect
(
formatScaled
(
1
,
1
)).
toEqual
(
'
1.0GB
'
);
expect
(
formatScaled
(
10
)).
toEqual
(
'
10GB
'
);
expect
(
formatScaled
(
10
**
2
)).
toEqual
(
'
100GB
'
);
expect
(
formatScaled
(
10
**
3
)).
toEqual
(
'
1TB
'
);
expect
(
formatScaled
(
10
**
4
)).
toEqual
(
'
10TB
'
);
expect
(
formatScaled
(
10
**
5
)).
toEqual
(
'
100TB
'
);
expect
(
formatScaled
(
10
**
6
)).
toEqual
(
'
1PB
'
);
expect
(
formatScaled
(
10
**
7
)).
toEqual
(
'
10PB
'
);
expect
(
formatScaled
(
10
**
8
)).
toEqual
(
'
100PB
'
);
expect
(
formatScaled
(
10
**
9
)).
toEqual
(
'
1EB
'
);
});
it
(
'
formatting of too large numbers is not suported
'
,
()
=>
{
// formatting YB is out of range
expect
(()
=>
scaledSIFormatter
(
'
B
'
,
9
)).
toThrow
();
});
});
describe
(
'
scaled format with negative offset
'
,
()
=>
{
let
formatScaled
;
beforeEach
(()
=>
{
formatScaled
=
scaledSIFormatter
(
'
g
'
,
-
1
);
});
it
(
'
formats floating point numbers
'
,
()
=>
{
expect
(
formatScaled
(
12.345
)).
toEqual
(
'
12.345mg
'
);
expect
(
formatScaled
(
12.345
,
0
)).
toEqual
(
'
12mg
'
);
expect
(
formatScaled
(
12.345
,
1
)).
toEqual
(
'
12.3mg
'
);
expect
(
formatScaled
(
12.345
,
2
)).
toEqual
(
'
12.35mg
'
);
});
it
(
'
formats large numbers scaled
'
,
()
=>
{
expect
(
formatScaled
(
1
)).
toEqual
(
'
1mg
'
);
expect
(
formatScaled
(
1
,
1
)).
toEqual
(
'
1.0mg
'
);
expect
(
formatScaled
(
10
)).
toEqual
(
'
10mg
'
);
expect
(
formatScaled
(
10
**
2
)).
toEqual
(
'
100mg
'
);
expect
(
formatScaled
(
10
**
3
)).
toEqual
(
'
1g
'
);
expect
(
formatScaled
(
10
**
4
)).
toEqual
(
'
10g
'
);
expect
(
formatScaled
(
10
**
5
)).
toEqual
(
'
100g
'
);
expect
(
formatScaled
(
10
**
6
)).
toEqual
(
'
1kg
'
);
expect
(
formatScaled
(
10
**
7
)).
toEqual
(
'
10kg
'
);
expect
(
formatScaled
(
10
**
8
)).
toEqual
(
'
100kg
'
);
});
it
(
'
formats negative numbers scaled
'
,
()
=>
{
expect
(
formatScaled
(
-
12.345
)).
toEqual
(
'
-12.345mg
'
);
expect
(
formatScaled
(
-
12.345
,
0
)).
toEqual
(
'
-12mg
'
);
expect
(
formatScaled
(
-
12.345
,
1
)).
toEqual
(
'
-12.3mg
'
);
expect
(
formatScaled
(
-
12.345
,
2
)).
toEqual
(
'
-12.35mg
'
);
expect
(
formatScaled
(
-
10
)).
toEqual
(
'
-10mg
'
);
expect
(
formatScaled
(
-
100
)).
toEqual
(
'
-100mg
'
);
expect
(
formatScaled
(
-
(
10
**
4
))).
toEqual
(
'
-10g
'
);
});
});
});
});
spec/frontend/lib/utils/unit_format/index_spec.js
0 → 100644
View file @
e06d0e77
import
{
getFormatter
,
SUPPORTED_FORMATS
}
from
'
~/lib/utils/unit_format
'
;
describe
(
'
unit_format
'
,
()
=>
{
describe
(
'
when a supported format is provided, the returned function formats
'
,
()
=>
{
it
(
'
numbers, by default
'
,
()
=>
{
expect
(
getFormatter
()(
1
)).
toEqual
(
'
1
'
);
});
it
(
'
numbers
'
,
()
=>
{
const
formatNumber
=
getFormatter
(
SUPPORTED_FORMATS
.
number
);
expect
(
formatNumber
(
1
)).
toEqual
(
'
1
'
);
expect
(
formatNumber
(
100
)).
toEqual
(
'
100
'
);
expect
(
formatNumber
(
1000
)).
toEqual
(
'
1,000
'
);
expect
(
formatNumber
(
10000
)).
toEqual
(
'
10,000
'
);
expect
(
formatNumber
(
1000000
)).
toEqual
(
'
1,000,000
'
);
});
it
(
'
percent
'
,
()
=>
{
const
formatPercent
=
getFormatter
(
SUPPORTED_FORMATS
.
percent
);
expect
(
formatPercent
(
1
)).
toEqual
(
'
100%
'
);
expect
(
formatPercent
(
1
,
2
)).
toEqual
(
'
100.00%
'
);
expect
(
formatPercent
(
0.1
)).
toEqual
(
'
10%
'
);
expect
(
formatPercent
(
0.5
)).
toEqual
(
'
50%
'
);
expect
(
formatPercent
(
0.888888
)).
toEqual
(
'
89%
'
);
expect
(
formatPercent
(
0.888888
,
2
)).
toEqual
(
'
88.89%
'
);
expect
(
formatPercent
(
0.888888
,
5
)).
toEqual
(
'
88.88880%
'
);
expect
(
formatPercent
(
2
)).
toEqual
(
'
200%
'
);
expect
(
formatPercent
(
10
)).
toEqual
(
'
1,000%
'
);
});
it
(
'
percentunit
'
,
()
=>
{
const
formatPercentHundred
=
getFormatter
(
SUPPORTED_FORMATS
.
percentHundred
);
expect
(
formatPercentHundred
(
1
)).
toEqual
(
'
1%
'
);
expect
(
formatPercentHundred
(
1
,
2
)).
toEqual
(
'
1.00%
'
);
expect
(
formatPercentHundred
(
88.8888
)).
toEqual
(
'
89%
'
);
expect
(
formatPercentHundred
(
88.8888
,
2
)).
toEqual
(
'
88.89%
'
);
expect
(
formatPercentHundred
(
88.8888
,
5
)).
toEqual
(
'
88.88880%
'
);
expect
(
formatPercentHundred
(
100
)).
toEqual
(
'
100%
'
);
expect
(
formatPercentHundred
(
100
,
2
)).
toEqual
(
'
100.00%
'
);
expect
(
formatPercentHundred
(
200
)).
toEqual
(
'
200%
'
);
expect
(
formatPercentHundred
(
1000
)).
toEqual
(
'
1,000%
'
);
});
it
(
'
seconds
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
seconds
)(
1
)).
toEqual
(
'
1s
'
);
});
it
(
'
miliseconds
'
,
()
=>
{
const
formatMiliseconds
=
getFormatter
(
SUPPORTED_FORMATS
.
miliseconds
);
expect
(
formatMiliseconds
(
1
)).
toEqual
(
'
1ms
'
);
expect
(
formatMiliseconds
(
100
)).
toEqual
(
'
100ms
'
);
expect
(
formatMiliseconds
(
1000
)).
toEqual
(
'
1,000ms
'
);
expect
(
formatMiliseconds
(
10000
)).
toEqual
(
'
10,000ms
'
);
expect
(
formatMiliseconds
(
1000000
)).
toEqual
(
'
1,000,000ms
'
);
});
it
(
'
bytes
'
,
()
=>
{
const
formatBytes
=
getFormatter
(
SUPPORTED_FORMATS
.
bytes
);
expect
(
formatBytes
(
1
)).
toEqual
(
'
1B
'
);
expect
(
formatBytes
(
1
,
1
)).
toEqual
(
'
1.0B
'
);
expect
(
formatBytes
(
10
)).
toEqual
(
'
10B
'
);
expect
(
formatBytes
(
10
**
2
)).
toEqual
(
'
100B
'
);
expect
(
formatBytes
(
10
**
3
)).
toEqual
(
'
1kB
'
);
expect
(
formatBytes
(
10
**
4
)).
toEqual
(
'
10kB
'
);
expect
(
formatBytes
(
10
**
5
)).
toEqual
(
'
100kB
'
);
expect
(
formatBytes
(
10
**
6
)).
toEqual
(
'
1MB
'
);
expect
(
formatBytes
(
10
**
7
)).
toEqual
(
'
10MB
'
);
expect
(
formatBytes
(
10
**
8
)).
toEqual
(
'
100MB
'
);
expect
(
formatBytes
(
10
**
9
)).
toEqual
(
'
1GB
'
);
expect
(
formatBytes
(
10
**
10
)).
toEqual
(
'
10GB
'
);
expect
(
formatBytes
(
10
**
11
)).
toEqual
(
'
100GB
'
);
});
it
(
'
kilobytes
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
kilobytes
)(
1
)).
toEqual
(
'
1kB
'
);
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
kilobytes
)(
1
,
1
)).
toEqual
(
'
1.0kB
'
);
});
it
(
'
megabytes
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
megabytes
)(
1
)).
toEqual
(
'
1MB
'
);
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
megabytes
)(
1
,
1
)).
toEqual
(
'
1.0MB
'
);
});
it
(
'
gigabytes
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
gigabytes
)(
1
)).
toEqual
(
'
1GB
'
);
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
gigabytes
)(
1
,
1
)).
toEqual
(
'
1.0GB
'
);
});
it
(
'
terabytes
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
terabytes
)(
1
)).
toEqual
(
'
1TB
'
);
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
terabytes
)(
1
,
1
)).
toEqual
(
'
1.0TB
'
);
});
it
(
'
petabytes
'
,
()
=>
{
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
petabytes
)(
1
)).
toEqual
(
'
1PB
'
);
expect
(
getFormatter
(
SUPPORTED_FORMATS
.
petabytes
)(
1
,
1
)).
toEqual
(
'
1.0PB
'
);
});
});
describe
(
'
when get formatter format is incorrect
'
,
()
=>
{
it
(
'
formatter fails
'
,
()
=>
{
expect
(()
=>
getFormatter
(
'
not-supported
'
)(
1
)).
toThrow
();
});
});
});
spec/helpers/labels_helper_spec.rb
View file @
e06d0e77
...
...
@@ -56,7 +56,7 @@ describe LabelsHelper do
context
'without subject'
do
it
"uses the label's project"
do
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
href="/
#{
label
.
project
.
full_path
}
/issues
\?
label_name%5B%5D=
#{
label
.
name
}
">.*</a>}
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
.*href="/
#{
label
.
project
.
full_path
}
/issues
\?
label_name%5B%5D=
#{
label
.
name
}
".*>.*</a>}m
end
end
...
...
@@ -65,7 +65,7 @@ describe LabelsHelper do
let
(
:subject
)
{
build
(
:project
,
namespace:
namespace
,
name:
'bar3'
)
}
it
'links to project issues page'
do
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
href="/foo3/bar3/issues
\?
label_name%5B%5D=
#{
label
.
name
}
">.*</a>}
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
.*href="/foo3/bar3/issues
\?
label_name%5B%5D=
#{
label
.
name
}
".*>.*</a>}m
end
end
...
...
@@ -73,7 +73,7 @@ describe LabelsHelper do
let
(
:subject
)
{
build
(
:group
,
name:
'bar'
)
}
it
'links to group issues page'
do
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
href="/groups/bar/-/issues
\?
label_name%5B%5D=
#{
label
.
name
}
">.*</a>}
expect
(
link_to_label
(
label_presenter
)).
to
match
%r{<a
.*href="/groups/bar/-/issues
\?
label_name%5B%5D=
#{
label
.
name
}
".*>.*</a>}m
end
end
...
...
@@ -81,7 +81,7 @@ describe LabelsHelper do
[
'issue'
,
:issue
].
each
do
|
type
|
context
"set to
#{
type
}
"
do
it
'links to correct page'
do
expect
(
link_to_label
(
label_presenter
,
type:
type
)).
to
match
%r{<a
href="/
#{
label
.
project
.
full_path
}
/
#{
type
.
to_s
.
pluralize
}
\?
label_name%5B%5D=
#{
label
.
name
}
">.*</a>}
expect
(
link_to_label
(
label_presenter
,
type:
type
)).
to
match
%r{<a
.*href="/
#{
label
.
project
.
full_path
}
/
#{
type
.
to_s
.
pluralize
}
\?
label_name%5B%5D=
#{
label
.
name
}
".*>.*</a>}m
end
end
end
...
...
@@ -89,7 +89,7 @@ describe LabelsHelper do
[
'merge_request'
,
:merge_request
].
each
do
|
type
|
context
"set to
#{
type
}
"
do
it
'links to correct page'
do
expect
(
link_to_label
(
label_presenter
,
type:
type
)).
to
match
%r{<a
href="/
#{
label
.
project
.
full_path
}
/-/
#{
type
.
to_s
.
pluralize
}
\?
label_name%5B%5D=
#{
label
.
name
}
">.*</a>}
expect
(
link_to_label
(
label_presenter
,
type:
type
)).
to
match
%r{<a
.*href="/
#{
label
.
project
.
full_path
}
/-/
#{
type
.
to_s
.
pluralize
}
\?
label_name%5B%5D=
#{
label
.
name
}
".*>.*</a>}m
end
end
end
...
...
@@ -113,7 +113,7 @@ describe LabelsHelper do
context
'without block'
do
it
'uses render_colored_label as the link content'
do
expect
(
self
).
to
receive
(
:render_colored_label
)
.
with
(
label_presenter
,
tooltip:
true
).
and_return
(
'Foo'
)
.
with
(
label_presenter
).
and_return
(
'Foo'
)
expect
(
link_to_label
(
label_presenter
)).
to
match
(
'Foo'
)
end
end
...
...
spec/helpers/markup_helper_spec.rb
View file @
e06d0e77
...
...
@@ -537,8 +537,10 @@ describe MarkupHelper do
it
'does not style a label that can not be accessed by current_user'
do
project
=
create
(
:project
,
:private
)
label
=
create_and_format_label
(
project
)
expect
(
create_and_format_label
(
project
)).
to
eq
(
"<p>
#{
label_title
}
</p>"
)
expect
(
label
).
to
include
(
"~label_1"
)
expect
(
label
).
not_to
match
(
/span class=.*style=.*/
)
end
end
...
...
spec/lib/banzai/filter/label_reference_filter_spec.rb
View file @
e06d0e77
...
...
@@ -28,7 +28,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'includes default classes'
do
doc
=
reference_filter
(
"Label
#{
reference
}
"
)
expect
(
doc
.
css
(
'a'
).
first
.
attr
(
'class'
)).
to
eq
'gfm gfm-label has-tooltip'
expect
(
doc
.
css
(
'a'
).
first
.
attr
(
'class'
)).
to
eq
'gfm gfm-label has-tooltip
gl-link gl-label-link
'
end
it
'includes a data-project attribute'
do
...
...
@@ -66,12 +66,12 @@ describe Banzai::Filter::LabelReferenceFilter do
describe
'label span element'
do
it
'includes default classes'
do
doc
=
reference_filter
(
"Label
#{
reference
}
"
)
expect
(
doc
.
css
(
'a span'
).
first
.
attr
(
'class'
)).
to
eq
'badge color-label has-tooltip
'
expect
(
doc
.
css
(
'a span'
).
first
.
attr
(
'class'
)).
to
include
'gl-label-text
'
end
it
'includes a style attribute'
do
doc
=
reference_filter
(
"Label
#{
reference
}
"
)
expect
(
doc
.
css
(
'a span'
).
first
.
attr
(
'style'
)).
to
match
(
/\Abackground-color: #\h{6}
; color: #\h{6}
\z/
)
expect
(
doc
.
css
(
'a span'
).
first
.
attr
(
'style'
)).
to
match
(
/\Abackground-color: #\h{6}\z/
)
end
end
...
...
@@ -85,7 +85,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
label
.
name
}
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
label
.
name
}
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label IDs'
do
...
...
@@ -109,7 +109,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
)."
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
label
.
name
}
</span></a
>
\)\.
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
label
.
name
}
</span></a></span
>
\)\.
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -133,7 +133,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
)."
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
label
.
name
}
</span></a
>
\)\.
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
label
.
name
}
</span></a></span
>
\)\.
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -158,7 +158,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'does not include trailing punctuation'
,
:aggregate_failures
do
[
'.'
,
', ok?'
,
'...'
,
'?'
,
'!'
,
': is that ok?'
].
each
do
|
trailing_punctuation
|
doc
=
filter
(
"Label
#{
reference
}#{
trailing_punctuation
}
"
)
expect
(
doc
.
to_html
).
to
match
(
%r(<
a.+><span.+>
\?
g
\.
fm&</span></a
>
#{
Regexp
.
escape
(
trailing_punctuation
)
}
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(<
span.+><a.+><span.+>
\?
g
\.
fm&</span></a></span
>
#{
Regexp
.
escape
(
trailing_punctuation
)
}
)
)
end
end
...
...
@@ -184,7 +184,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
label
.
name
}
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
label
.
name
}
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -208,7 +208,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
label
.
name
}
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
label
.
name
}
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -232,7 +232,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>g
\.
fm & references
\?
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>g
\.
fm & references
\?
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -320,7 +320,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+>Label</a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+>Label</a></span
>
\.\)
)
)
end
it
'includes a data-project attribute'
do
...
...
@@ -358,7 +358,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
group_label
.
name
}
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
group_label
.
name
}
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label names'
do
...
...
@@ -381,7 +381,7 @@ describe Banzai::Filter::LabelReferenceFilter do
it
'links with adjacent text'
do
doc
=
reference_filter
(
"Label (
#{
reference
}
.)"
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
a.+><span.+>
#{
group_label
.
name
}
</span></a
>
\.\)
)
)
expect
(
doc
.
to_html
).
to
match
(
%r(
\(
<
span.+><a.+><span.+>
#{
group_label
.
name
}
</span></a></span
>
\.\)
)
)
end
it
'ignores invalid label names'
do
...
...
spec/services/resource_events/change_milestone_service_spec.rb
View file @
e06d0e77
...
...
@@ -3,65 +3,11 @@
require
'spec_helper'
describe
ResourceEvents
::
ChangeMilestoneService
do
shared_examples
'milestone events creator'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
)
}
context
'when milestone is present'
do
before
do
resource
.
milestone
=
milestone
end
let
(
:service
)
{
described_class
.
new
(
resource:
resource
,
user:
user
,
created_at:
created_at_time
)
}
it
'creates the expected event record'
do
expect
{
service
.
execute
}.
to
change
{
ResourceMilestoneEvent
.
count
}.
from
(
0
).
to
(
1
)
events
=
ResourceMilestoneEvent
.
all
expect
(
events
.
size
).
to
eq
(
1
)
expect_event_record
(
events
.
first
,
action:
'add'
,
milestone:
milestone
,
state:
'opened'
)
end
end
context
'when milestones is not present'
do
before
do
resource
.
milestone
=
nil
end
let
(
:service
)
{
described_class
.
new
(
resource:
resource
,
user:
user
,
created_at:
created_at_time
)
}
it
'creates the expected event records'
do
expect
{
service
.
execute
}.
to
change
{
ResourceMilestoneEvent
.
count
}.
from
(
0
).
to
(
1
)
expect_event_record
(
ResourceMilestoneEvent
.
first
,
action:
'remove'
,
milestone:
nil
,
state:
'opened'
)
end
end
def
expect_event_record
(
event
,
expected_attrs
)
expect
(
event
.
action
).
to
eq
(
expected_attrs
[
:action
])
expect
(
event
.
state
).
to
eq
(
expected_attrs
[
:state
])
expect
(
event
.
user
).
to
eq
(
user
)
expect
(
event
.
issue
).
to
eq
(
resource
)
if
resource
.
is_a?
(
Issue
)
expect
(
event
.
issue
).
to
be_nil
unless
resource
.
is_a?
(
Issue
)
expect
(
event
.
merge_request
).
to
eq
(
resource
)
if
resource
.
is_a?
(
MergeRequest
)
expect
(
event
.
merge_request
).
to
be_nil
unless
resource
.
is_a?
(
MergeRequest
)
expect
(
event
.
milestone
).
to
eq
(
expected_attrs
[
:milestone
])
expect
(
event
.
created_at
).
to
eq
(
created_at_time
)
end
end
let_it_be
(
:merge_request
)
{
create
(
:merge_request
)
}
let_it_be
(
:issue
)
{
create
(
:issue
)
}
let!
(
:created_at_time
)
{
Time
.
utc
(
2019
,
12
,
30
)
}
it_behaves_like
'milestone events creator'
do
let
(
:resource
)
{
issue
}
it_behaves_like
'a milestone events creator'
do
let
(
:resource
)
{
create
(
:issue
)
}
end
it_behaves_like
'milestone events creator'
do
let
(
:resource
)
{
merge_request
}
it_behaves_like
'
a
milestone events creator'
do
let
(
:resource
)
{
create
(
:merge_request
)
}
end
end
spec/support/shared_examples/services/resource_events/change_milestone_service_shared_examples.rb
0 → 100644
View file @
e06d0e77
# frozen_string_literal: true
shared_examples
'a milestone events creator'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:created_at_time
)
{
Time
.
utc
(
2019
,
12
,
30
)
}
let
(
:service
)
{
described_class
.
new
(
resource
,
user
,
created_at:
created_at_time
)
}
context
'when milestone is present'
do
let_it_be
(
:milestone
)
{
create
(
:milestone
)
}
before
do
resource
.
milestone
=
milestone
end
it
'creates the expected event record'
do
expect
{
service
.
execute
}.
to
change
{
ResourceMilestoneEvent
.
count
}.
by
(
1
)
expect_event_record
(
ResourceMilestoneEvent
.
last
,
action:
'add'
,
milestone:
milestone
,
state:
'opened'
)
end
end
context
'when milestones is not present'
do
before
do
resource
.
milestone
=
nil
end
it
'creates the expected event records'
do
expect
{
service
.
execute
}.
to
change
{
ResourceMilestoneEvent
.
count
}.
by
(
1
)
expect_event_record
(
ResourceMilestoneEvent
.
last
,
action:
'remove'
,
milestone:
nil
,
state:
'opened'
)
end
end
def
expect_event_record
(
event
,
expected_attrs
)
expect
(
event
.
action
).
to
eq
(
expected_attrs
[
:action
])
expect
(
event
.
state
).
to
eq
(
expected_attrs
[
:state
])
expect
(
event
.
user
).
to
eq
(
user
)
expect
(
event
.
issue
).
to
eq
(
resource
)
if
resource
.
is_a?
(
Issue
)
expect
(
event
.
issue
).
to
be_nil
unless
resource
.
is_a?
(
Issue
)
expect
(
event
.
merge_request
).
to
eq
(
resource
)
if
resource
.
is_a?
(
MergeRequest
)
expect
(
event
.
merge_request
).
to
be_nil
unless
resource
.
is_a?
(
MergeRequest
)
expect
(
event
.
milestone
).
to
eq
(
expected_attrs
[
:milestone
])
expect
(
event
.
created_at
).
to
eq
(
created_at_time
)
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