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
7f3519b3
Commit
7f3519b3
authored
Apr 06, 2022
by
Ammar Alakkad
Committed by
Miguel Rincon
Apr 06, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use statistics card in storage app
Changelog: other EE: true
parent
c53a8610
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
409 additions
and
265 deletions
+409
-265
app/views/projects/usage_quotas/index.html.haml
app/views/projects/usage_quotas/index.html.haml
+1
-1
ee/app/assets/javascripts/usage_quotas/components/statistics_card.vue
...s/javascripts/usage_quotas/components/statistics_card.vue
+0
-6
ee/app/assets/javascripts/usage_quotas/components/storage_statistics_card.vue
...ripts/usage_quotas/components/storage_statistics_card.vue
+91
-0
ee/app/assets/javascripts/usage_quotas/storage/components/usage_statistics.vue
...ipts/usage_quotas/storage/components/usage_statistics.vue
+79
-100
ee/app/assets/javascripts/usage_quotas/storage/components/usage_statistics_card.vue
...usage_quotas/storage/components/usage_statistics_card.vue
+0
-79
ee/app/assets/javascripts/usage_quotas/storage/utils.js
ee/app/assets/javascripts/usage_quotas/storage/utils.js
+29
-4
ee/spec/frontend/usage_quotas/components/statistics_card_spec.js
.../frontend/usage_quotas/components/statistics_card_spec.js
+0
-6
ee/spec/frontend/usage_quotas/components/storage_statistics_card_spec.js
...d/usage_quotas/components/storage_statistics_card_spec.js
+85
-0
ee/spec/frontend/usage_quotas/storage/components/usage_statistics_spec.js
.../usage_quotas/storage/components/usage_statistics_spec.js
+99
-51
ee/spec/frontend/usage_quotas/storage/utils_spec.js
ee/spec/frontend/usage_quotas/storage/utils_spec.js
+16
-0
locale/gitlab.pot
locale/gitlab.pot
+9
-18
No files found.
app/views/projects/usage_quotas/index.html.haml
View file @
7f3519b3
...
...
@@ -7,7 +7,7 @@
.col-sm-12
=
s_
(
'UsageQuota|Usage of project resources across the %{strong_start}%{project_name}%{strong_end} project'
).
html_safe
%
{
strong_start:
'<strong>'
.
html_safe
,
strong_end:
'</strong>'
.
html_safe
,
project_name:
@project
.
name
}
+
'.'
%a
{
href:
help_page_path
(
'user/usage_quotas.md'
),
target:
'_blank'
,
rel:
'noopener noreferrer'
}
=
s_
(
'UsageQuota|Learn more about usage quotas
'
)
+
'.'
=
s_
(
'UsageQuota|Learn more about usage quotas
.'
)
=
gl_tabs_nav
do
=
gl_tab_link_to
'#storage-quota-tab'
,
item_active:
true
do
...
...
ee/app/assets/javascripts/usage_quotas/components/statistics_card.vue
View file @
7f3519b3
...
...
@@ -55,11 +55,6 @@ export default {
required
:
false
,
default
:
null
,
},
cssClass
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
},
};
</
script
>
...
...
@@ -68,7 +63,6 @@ export default {
<div
class=
"gl-bg-white gl-border-1 gl-border-gray-100 gl-border-solid gl-p-5 gl-rounded-base"
data-testid=
"container"
:class=
"cssClass"
>
<div
class=
"gl-display-flex gl-justify-content-space-between"
>
<p
...
...
ee/app/assets/javascripts/usage_quotas/components/storage_statistics_card.vue
0 → 100644
View file @
7f3519b3
<
script
>
import
{
GlProgressBar
}
from
'
@gitlab/ui
'
;
import
{
formatSizeAndSplit
}
from
'
ee/usage_quotas/storage/utils
'
;
export
default
{
name
:
'
StorageStatisticsCard
'
,
components
:
{
GlProgressBar
},
props
:
{
totalStorage
:
{
type
:
Number
,
required
:
false
,
default
:
null
,
},
usedStorage
:
{
type
:
Number
,
required
:
false
,
default
:
null
,
},
},
computed
:
{
formattedUsage
()
{
// we want to show the usage only if there's purchased storage
if
(
this
.
totalStorage
===
null
)
{
return
null
;
}
return
this
.
formatSizeAndSplit
(
this
.
usedStorage
);
},
formattedTotal
()
{
return
this
.
formatSizeAndSplit
(
this
.
totalStorage
);
},
percentage
()
{
// don't show the progress bar if there's no total storage
if
(
!
this
.
totalStorage
||
this
.
usedStorage
===
null
)
{
return
null
;
}
return
Math
.
min
(
Math
.
round
((
this
.
usedStorage
/
this
.
totalStorage
)
*
100
),
100
);
},
usageValue
()
{
if
(
!
this
.
totalStorage
&&
!
this
.
usedStorage
)
{
// if there is no total storage and no used storage, we want
// to show `0` instead of the formatted `0.0`
return
'
0
'
;
}
return
this
.
formattedUsage
?.
value
;
},
usageUnit
()
{
return
this
.
formattedUsage
?.
unit
;
},
totalValue
()
{
return
this
.
formattedTotal
?.
value
;
},
totalUnit
()
{
return
this
.
formattedTotal
?.
unit
;
},
shouldRenderTotalBlock
()
{
// only show the total block if the used and total storage are not 0
return
this
.
usedStorage
&&
this
.
totalStorage
;
},
},
methods
:
{
formatSizeAndSplit
,
},
};
</
script
>
<
template
>
<div
class=
"gl-bg-white gl-border-1 gl-border-gray-100 gl-border-solid gl-p-5 gl-rounded-base"
data-testid=
"container"
>
<div
class=
"gl-display-flex gl-justify-content-space-between"
>
<p
class=
"gl-font-size-h-display gl-font-weight-bold gl-mb-3"
data-testid=
"denominator"
>
{{
usageValue
}}
<span
class=
"gl-font-lg"
>
{{
usageUnit
}}
</span>
<span
v-if=
"shouldRenderTotalBlock"
data-testid=
"denominator-total"
>
/
{{
totalValue
}}
<span
class=
"gl-font-lg"
>
{{
totalUnit
}}
</span>
</span>
</p>
<div
data-testid=
"actions"
>
<slot
name=
"actions"
></slot>
</div>
</div>
<p
class=
"gl-font-weight-bold"
data-testid=
"description"
>
<slot
name=
"description"
></slot>
</p>
<gl-progress-bar
v-if=
"percentage !== null"
:value=
"percentage"
/>
</div>
</
template
>
ee/app/assets/javascripts/usage_quotas/storage/components/usage_statistics.vue
View file @
7f3519b3
<
script
>
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
Gl
Icon
,
GlLink
,
Gl
Button
}
from
'
@gitlab/ui
'
;
import
{
helpPagePath
}
from
'
~/helpers/help_page_helper
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
formatUsageSize
}
from
'
../utils
'
;
import
UsageStatisticsCard
from
'
./usage_statistics_card.vue
'
;
import
StorageStatisticsCard
from
'
ee/usage_quotas/components/storage_statistics_card.vue
'
;
export
default
{
components
:
{
GlIcon
,
GlLink
,
GlButton
,
Us
ageStatisticsCard
,
Stor
ageStatisticsCard
,
},
inject
:
[
'
purchaseStorageUrl
'
,
'
buyAddonTargetAttr
'
],
props
:
{
...
...
@@ -17,115 +18,93 @@ export default {
type
:
Object
,
},
},
computed
:
{
formattedActualRepoSizeLimit
()
{
return
formatUsageSize
(
this
.
rootStorageStatistics
.
actualRepositorySizeLimit
);
},
totalUsage
()
{
return
{
usage
:
this
.
formatSizeAndSplit
(
this
.
rootStorageStatistics
.
totalRepositorySize
),
description
:
s__
(
'
UsageQuota|Total namespace storage used
'
),
footerNote
:
s__
(
'
UsageQuota|This is the total amount of storage used across your projects within this namespace.
'
,
),
link
:
{
text
:
`
${
s__
(
'
UsageQuota|Learn more about usage quotas
'
)}
.`
,
url
:
helpPagePath
(
'
user/usage_quotas
'
),
},
};
},
excessUsage
()
{
return
{
usage
:
this
.
formatSizeAndSplit
(
this
.
rootStorageStatistics
.
totalRepositorySizeExcess
),
description
:
s__
(
'
UsageQuota|Total excess storage used
'
),
footerNote
:
s__
(
'
UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit.
'
,
),
link
:
{
text
:
s__
(
'
UsageQuota|Learn more about excess storage usage
'
),
url
:
helpPagePath
(
'
user/usage_quotas
'
,
{
anchor
:
'
excess-storage-usage
'
}),
i18n
:
{
purchasedUsageHelpLink
:
helpPagePath
(
'
user/usage_quotas
'
),
purchasedUsageHelpText
:
s__
(
'
UsageQuota|Learn more about usage quotas.
'
),
usedUsageHelpLink
:
helpPagePath
(
'
user/usage_quotas
'
),
usedUsageHelpText
:
s__
(
'
UsageQuota|Learn more about usage quotas.
'
),
purchaseButtonText
:
s__
(
'
UsageQuota|Buy storage
'
),
totalUsageDescription
:
s__
(
'
UsageQuota|Namespace storage used
'
),
},
};
},
purchasedUsage
()
{
computed
:
{
usedStorageAmount
()
{
const
{
totalRepositorySizeExcess
,
additionalPurchasedStorageSize
,
actualRepositorySizeLimit
,
totalRepositorySize
,
}
=
this
.
rootStorageStatistics
;
return
this
.
purchaseStorageUrl
?
{
usage
:
this
.
formatSizeAndSplit
(
Math
.
max
(
0
,
additionalPurchasedStorageSize
-
totalRepositorySizeExcess
),
),
usageTotal
:
this
.
formatSizeAndSplit
(
additionalPurchasedStorageSize
),
description
:
s__
(
'
UsageQuota|Purchased storage available
'
),
link
:
{
text
:
s__
(
'
UsageQuota|Purchase more storage
'
),
url
:
this
.
purchaseStorageUrl
,
target
:
this
.
buyAddonTargetAttr
,
if
(
additionalPurchasedStorageSize
&&
totalRepositorySize
>
actualRepositorySizeLimit
)
{
return
actualRepositorySizeLimit
;
}
return
totalRepositorySize
;
},
purchasedUsageDescription
()
{
if
(
this
.
rootStorageStatistics
.
additionalPurchasedStorageSize
)
{
return
s__
(
'
UsageQuota|Purchased storage used
'
);
}
:
null
;
return
s__
(
'
UsageQuota|Purchased storage
'
)
;
},
repositorySizeLimit
()
{
return
Number
(
this
.
rootStorageStatistics
.
actualRepositorySizeLimit
);
},
methods
:
{
/**
* The formatUsageSize method returns
* value along with the unit. However, the unit
* and the value needs to be separated so that
* they can have different styles. The method
* splits the value into value and unit.
*
* @params {Number} size size in bytes
* @returns {Object} value and unit of formatted size
*/
formatSizeAndSplit
(
size
)
{
const
formattedSize
=
formatUsageSize
(
size
);
return
{
value
:
formattedSize
.
slice
(
0
,
-
3
),
unit
:
formattedSize
.
slice
(
-
3
),
};
purchasedTotalStorage
()
{
return
Number
(
this
.
rootStorageStatistics
.
additionalPurchasedStorageSize
);
},
purchasedUsedStorage
()
{
// we don't want to show the used value if there's no purchased storage
return
this
.
rootStorageStatistics
.
additionalPurchasedStorageSize
?
Number
(
this
.
rootStorageStatistics
.
totalRepositorySizeExcess
)
:
0
;
},
},
};
</
script
>
<
template
>
<div
class=
"gl-display-flex gl-sm-flex-direction-column"
>
<usage-statistics-card
data-testid=
"total-usage"
:usage=
"totalUsage.usage"
:link=
"totalUsage.link"
:description=
"totalUsage.description"
css-class=
"gl-mr-4"
/>
<usage-statistics-card
data-testid=
"excess-usage"
:usage=
"excessUsage.usage"
:link=
"excessUsage.link"
:description=
"excessUsage.description"
css-class=
"gl-mx-4"
/>
<usage-statistics-card
v-if=
"purchasedUsage"
data-testid=
"purchased-usage"
:usage=
"purchasedUsage.usage"
:usage-total=
"purchasedUsage.usageTotal"
:link=
"purchasedUsage.link"
:description=
"purchasedUsage.description"
css-class=
"gl-ml-4"
<div
class=
"gl-display-flex gl-sm-flex-direction-column gl-py-5"
>
<storage-statistics-card
:used-storage=
"usedStorageAmount"
:total-storage=
"repositorySizeLimit"
data-testid=
"namespace-usage-card"
class=
"gl-w-half gl-md-mr-5"
>
<template
#
footer
="
{ link }"
>
<gl-button
:target=
"link.target"
:href=
"link.url"
class=
"mb-0
"
variant=
"confirm
"
c
ategory=
"primary
"
block
<template
#
description
>
{{
$options
.
i18n
.
totalUsageDescription
}}
<gl-link
:href=
"$options.i18n.usedUsageHelpLink
"
target=
"_blank
"
c
lass=
"gl-ml-2
"
:aria-label=
"$options.i18n.usedUsageHelpText"
>
{{
link
.
text
}}
<gl-icon
name=
"question-o"
/>
</gl-link>
</
template
>
</storage-statistics-card>
<storage-statistics-card
v-if=
"purchaseStorageUrl"
:used-storage=
"purchasedUsedStorage"
:total-storage=
"purchasedTotalStorage"
data-testid=
"purchased-usage-card"
class=
"gl-w-half"
>
<
template
#actions
>
<gl-button
:href=
"purchaseStorageUrl"
target=
"_blank"
category=
"primary"
variant=
"confirm"
>
{{
$options
.
i18n
.
purchaseButtonText
}}
</gl-button>
</
template
>
</usage-statistics-card>
<
template
#description
>
{{
purchasedUsageDescription
}}
<gl-link
:href=
"$options.purchasedUsageHelpLink"
target=
"_blank"
class=
"gl-ml-2"
:aria-label=
"$options.i18n.purchasedUsageHelpText"
>
<gl-icon
name=
"question-o"
/>
</gl-link>
</
template
>
</storage-statistics-card>
</div>
</template>
ee/app/assets/javascripts/usage_quotas/storage/components/usage_statistics_card.vue
deleted
100644 → 0
View file @
c53a8610
<
script
>
import
{
GlLink
,
GlIcon
,
GlSprintf
}
from
'
@gitlab/ui
'
;
export
default
{
components
:
{
GlIcon
,
GlLink
,
GlSprintf
,
},
props
:
{
link
:
{
type
:
Object
,
required
:
false
,
default
:
()
=>
({
text
:
''
,
url
:
''
}),
},
description
:
{
type
:
String
,
required
:
true
,
},
usage
:
{
type
:
Object
,
required
:
true
,
},
usageTotal
:
{
type
:
Object
,
required
:
false
,
default
:
null
,
},
cssClass
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
};
</
script
>
<
template
>
<div
class=
"gl-p-5 gl-my-5 gl-bg-gray-10 gl-flex-grow-1 gl-white-space-nowrap"
:class=
"cssClass"
>
<p
class=
"mb-2"
>
<gl-sprintf
:message=
"__('%
{size} %{unit}')">
<template
#size
>
<span
class=
"gl-font-size-h-display gl-font-weight-bold"
>
{{
usage
.
value
}}
</span>
</
template
>
<
template
#unit
>
<span
class=
"gl-font-lg gl-font-weight-bold"
>
{{
usage
.
unit
}}
</span>
</
template
>
</gl-sprintf>
<
template
v-if=
"usageTotal"
>
<span
class=
"gl-font-size-h-display gl-font-weight-bold"
>
/
</span>
<gl-sprintf
:message=
"__('%
{size} %{unit}')">
<template
#size
>
<span
data-qa-selector=
"purchased_usage_total"
class=
"gl-font-size-h-display gl-font-weight-bold"
>
{{
usageTotal
.
value
}}
</span
>
</
template
>
<
template
#unit
>
<span
class=
"gl-font-lg gl-font-weight-bold"
>
{{
usageTotal
.
unit
}}
</span>
</
template
>
</gl-sprintf>
</template>
</p>
<p
class=
"gl-border-b-2 gl-border-b-solid gl-border-b-gray-100 gl-font-weight-bold gl-pb-3"
>
{{ description }}
</p>
<p
class=
"gl-mb-0 gl-text-gray-900 gl-font-sm gl-white-space-normal"
data-testid=
"statistics-card-footer"
>
<slot
v-bind=
"{ link }"
name=
"footer"
>
<gl-link
target=
"_blank"
:href=
"link.url"
>
<span
class=
"text-truncate"
>
{{ link.text }}
</span>
<gl-icon
name=
"external-link"
class=
"gl-ml-2 gl-flex-shrink-0 gl-text-black-normal"
/>
</gl-link>
</slot>
</p>
</div>
</template>
ee/app/assets/javascripts/usage_quotas/storage/utils.js
View file @
7f3519b3
import
{
numberToHumanSize
,
bytesToKiB
}
from
'
~/lib/utils/number_utils
'
;
import
{
kibibytes
}
from
'
~/lib/utils/unit_format
'
;
import
{
gibibytes
,
kibibytes
}
from
'
~/lib/utils/unit_format
'
;
import
{
PROJECT_STORAGE_TYPES
,
STORAGE_USAGE_THRESHOLDS
}
from
'
./constants
'
;
export
function
usageRatioToThresholdLevel
(
currentUsageRatio
)
{
...
...
@@ -19,11 +19,12 @@ export function usageRatioToThresholdLevel(currentUsageRatio) {
* converting bytesToKiB before passing it to
* `getFormatter`
* @param {Number} size size in bytes
* @returns {String}
* @param sizeInBytes
* @param {String} unitSeparator
*/
export
const
formatUsageSize
=
(
sizeInBytes
)
=>
{
return
kibibytes
(
bytesToKiB
(
sizeInBytes
),
1
);
export
const
formatUsageSize
=
(
sizeInBytes
,
unitSeparator
=
''
)
=>
{
return
kibibytes
(
bytesToKiB
(
sizeInBytes
),
1
,
{
unitSeparator
}
);
};
/**
...
...
@@ -187,3 +188,27 @@ export const parseGetProjectStorageResults = (data, helpLinks) => {
export
function
descendingStorageUsageSort
(
storageUsageKey
)
{
return
(
a
,
b
)
=>
b
[
storageUsageKey
]
-
a
[
storageUsageKey
];
}
/**
* The formatUsageSize method returns
* value along with the unit. However, the unit
* and the value needs to be separated so that
* they can have different styles. The method
* splits the value into value and unit.
*
* @params {Number} size size in bytes
* @returns {Object} value and unit of formatted size
*/
export
function
formatSizeAndSplit
(
sizeInBytes
)
{
if
(
sizeInBytes
===
null
)
{
return
null
;
}
/**
* we're using a special separator to help us split the formatted value properly,
* the separator won't be shown in the output
*/
const
unitSeparator
=
'
@
'
;
const
format
=
sizeInBytes
===
0
?
gibibytes
:
kibibytes
;
const
[
value
,
unit
]
=
format
(
bytesToKiB
(
sizeInBytes
),
1
,
{
unitSeparator
}).
split
(
unitSeparator
);
return
{
value
,
unit
};
}
ee/spec/frontend/usage_quotas/components/statistics_card_spec.js
View file @
7f3519b3
...
...
@@ -30,12 +30,6 @@ describe('StatisticsCard', () => {
const
findHelpLink
=
()
=>
wrapper
.
findComponent
(
GlLink
);
const
findProgressBar
=
()
=>
wrapper
.
findComponent
(
GlProgressBar
);
it
(
'
passes cssClass to container div
'
,
()
=>
{
const
cssClass
=
'
awesome-css-class
'
;
createComponent
({
cssClass
});
expect
(
wrapper
.
find
(
'
[data-testid="container"]
'
).
classes
()).
toContain
(
cssClass
);
});
describe
(
'
denominator block
'
,
()
=>
{
it
(
'
renders denominator block with all elements when all props are passed
'
,
()
=>
{
createComponent
(
defaultProps
);
...
...
ee/spec/frontend/usage_quotas/components/storage_statistics_card_spec.js
0 → 100644
View file @
7f3519b3
import
{
GlProgressBar
}
from
'
@gitlab/ui
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
StorageStatisticsCard
from
'
ee/usage_quotas/components/storage_statistics_card.vue
'
;
describe
(
'
StorageStatisticsCard
'
,
()
=>
{
let
wrapper
;
const
defaultProps
=
{
totalStorage
:
100
*
1024
,
usedStorage
:
50
*
1024
,
};
const
createComponent
=
(
props
=
{})
=>
{
wrapper
=
shallowMountExtended
(
StorageStatisticsCard
,
{
propsData
:
{
...
defaultProps
,
...
props
},
slots
:
{
description
:
'
storage-statistics-card description slot
'
,
actions
:
'
storage-statistics-card actions slot
'
,
},
});
};
const
findDenominatorBlock
=
()
=>
wrapper
.
findByTestId
(
'
denominator
'
);
const
findTotalBlock
=
()
=>
wrapper
.
findByTestId
(
'
denominator-total
'
);
const
findDescriptionBlock
=
()
=>
wrapper
.
findByTestId
(
'
description
'
);
const
findActionsBlock
=
()
=>
wrapper
.
findByTestId
(
'
actions
'
);
const
findProgressBar
=
()
=>
wrapper
.
findComponent
(
GlProgressBar
);
describe
(
'
denominator block
'
,
()
=>
{
it
(
'
renders denominator block with all elements when all props are passed
'
,
()
=>
{
createComponent
();
expect
(
findDenominatorBlock
().
text
()).
toMatchInterpolatedText
(
'
50.0 KiB / 100.0 KiB
'
);
});
it
(
'
does not render total block if totalStorage and usedStorage are not passed
'
,
()
=>
{
createComponent
({
usedStorage
:
null
,
totalStorage
:
null
,
});
expect
(
findTotalBlock
().
exists
()).
toBe
(
false
);
});
it
(
'
renders the denominator block as 0 GiB if totalStorage and usedStorage are passed as 0
'
,
()
=>
{
createComponent
({
usedStorage
:
0
,
totalStorage
:
0
,
});
expect
(
findDenominatorBlock
().
text
()).
toMatchInterpolatedText
(
'
0 GiB
'
);
});
});
describe
(
'
slots
'
,
()
=>
{
it
(
'
renders description slot
'
,
()
=>
{
createComponent
();
expect
(
findDescriptionBlock
().
text
()).
toBe
(
'
storage-statistics-card description slot
'
);
});
it
(
'
renders actions slot
'
,
()
=>
{
createComponent
();
expect
(
findActionsBlock
().
text
()).
toBe
(
'
storage-statistics-card actions slot
'
);
});
});
describe
(
'
progress bar
'
,
()
=>
{
it
(
'
does not render progress bar if there is no totalStorage
'
,
()
=>
{
createComponent
({
totalStorage
:
null
});
expect
(
wrapper
.
findComponent
(
GlProgressBar
).
exists
()).
toBe
(
false
);
});
it
(
'
renders progress bar if percentage is greater than 0
'
,
()
=>
{
createComponent
({
totalStorage
:
10
,
usedStorage
:
5
});
expect
(
findProgressBar
().
exists
()).
toBe
(
true
);
expect
(
findProgressBar
().
attributes
(
'
value
'
)).
toBe
(
String
(
50
));
});
it
(
'
renders the progress bar if percentage is 0
'
,
()
=>
{
createComponent
({
totalStorage
:
10
,
usedStorage
:
0
});
expect
(
findProgressBar
().
exists
()).
toBe
(
true
);
expect
(
findProgressBar
().
attributes
(
'
value
'
)).
toBe
(
String
(
0
));
});
});
});
ee/spec/frontend/usage_quotas/storage/components/usage_statistics_spec.js
View file @
7f3519b3
import
{
GlButton
,
GlLink
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlLink
,
GlSprintf
,
GlProgressBar
,
GlButton
}
from
'
@gitlab/ui
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
StorageStatisticsCard
from
'
ee/usage_quotas/components/storage_statistics_card.vue
'
;
import
UsageStatistics
from
'
ee/usage_quotas/storage/components/usage_statistics.vue
'
;
import
UsageStatisticsCard
from
'
ee/usage_quotas/storage/components/usage_statistics_card.vue
'
;
import
{
withRootStorageStatistics
}
from
'
../mock_data
'
;
describe
(
'
UsageStatistics
'
,
()
=>
{
let
wrapper
;
const
createComponent
=
({
props
=
{},
provide
=
{}
}
=
{})
=>
{
wrapper
=
shallowMount
(
UsageStatistics
,
{
wrapper
=
shallowMount
Extended
(
UsageStatistics
,
{
propsData
:
{
rootStorageStatistics
:
{
totalRepositorySize
:
withRootStorageStatistics
.
totalRepositorySize
,
...
...
@@ -19,12 +19,15 @@ describe('UsageStatistics', () => {
...
props
,
},
provide
:
{
purchaseStorageUrl
:
'
some-fancy-url
'
,
buyAddonTargetAttr
:
'
_self
'
,
...
provide
,
},
stubs
:
{
Us
ageStatisticsCard
,
Stor
ageStatisticsCard
,
GlSprintf
,
GlLink
,
GlProgressBar
,
},
});
};
...
...
@@ -33,79 +36,124 @@ describe('UsageStatistics', () => {
wrapper
.
destroy
();
});
const
getStatisticsCards
=
()
=>
wrapper
.
findAllComponents
(
UsageStatisticsCard
);
const
getStatisticsCard
=
(
testId
)
=>
wrapper
.
find
(
`[data-testid="
${
testId
}
"]`
);
const
findGlLinkInCard
=
(
cardName
)
=>
getStatisticsCard
(
cardName
)
.
find
(
'
[data-testid="statistics-card-footer"]
'
)
.
findComponent
(
GlLink
);
const
findPurchasedUsageButton
=
()
=>
getStatisticsCard
(
'
purchased-usage
'
).
findComponent
(
GlButton
);
const
findAllStorageStatisticsCards
=
()
=>
wrapper
.
findAllComponents
(
StorageStatisticsCard
);
const
findNamespaceStorageCard
=
()
=>
wrapper
.
findByTestId
(
'
namespace-usage-card
'
);
const
findPurchasedStorageCard
=
()
=>
wrapper
.
findByTestId
(
'
purchased-usage-card
'
);
describe
(
'
with purchaseStorageUrl passed
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders two statistics cards
'
,
()
=>
{
expect
(
findAllStorageStatisticsCards
()).
toHaveLength
(
2
);
});
});
describe
(
'
with no purchaseStorageUrl
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
provide
:
{
purchaseStorageUrl
:
'
some-fancy-url
'
,
buyAddonTargetAttr
:
'
_self
'
,
purchaseStorageUrl
:
null
,
},
});
});
it
(
'
renders three statistics cards
'
,
()
=>
{
expect
(
getStatisticsCards
()).
toHaveLength
(
3
);
it
(
'
renders one statistics cards
'
,
()
=>
{
expect
(
findAllStorageStatisticsCards
()).
toHaveLength
(
1
);
});
});
it
(
'
renders URL in total usage card footer
'
,
()
=>
{
const
url
=
findGlLinkInCard
(
'
total-usage
'
);
describe
(
'
namespace storage used
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders progress bar with correct percentage
'
,
()
=>
{
expect
(
findNamespaceStorageCard
().
findComponent
(
GlProgressBar
).
attributes
(
'
value
'
)).
toBe
(
'
100
'
,
);
});
});
expect
(
url
.
attributes
(
'
href
'
)).
toBe
(
'
/help/user/usage_quotas
'
);
describe
(
'
purchase storage used
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders URL in excess usage card footer
'
,
()
=>
{
const
url
=
findGlLinkInCard
(
'
excess-usage
'
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
expect
(
url
.
attributes
(
'
href
'
)).
toBe
(
'
/help/user/usage_quotas#excess-storage-usage
'
);
it
(
'
renders the denominator and units correctly
'
,
()
=>
{
expect
(
findPurchasedStorageCard
().
text
().
replace
(
/
\s
+/g
,
'
'
)).
toContain
(
'
2.3 KiB / 0.3 KiB
'
);
});
it
(
'
renders
button in purchased usage card footer with correct link
'
,
()
=>
{
expect
(
findPurchasedUsageButton
().
attributes
()).
toMatchObject
({
href
:
'
some-fancy-url
'
,
target
:
'
_self
'
,
it
(
'
renders
purchase more storage button
'
,
()
=>
{
const
purchaseButton
=
findPurchasedStorageCard
().
findComponent
(
GlButton
);
expect
(
purchaseButton
.
exists
()).
toBe
(
true
);
expect
(
purchaseButton
.
attributes
(
'
href
'
)).
toBe
(
'
some-fancy-url
'
);
});
it
(
'
renders the percentage bar
'
,
()
=>
{
expect
(
findPurchasedStorageCard
().
findComponent
(
GlProgressBar
).
attributes
(
'
value
'
)).
toBe
(
'
100
'
,
);
});
});
describe
(
'
with buyAddonTargetAttr passed as _blank
'
,
()
=>
{
describe
(
'
when limit is exceeded
'
,
()
=>
{
describe
(
'
with purchased storage
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
provide
:
{
purchaseStorageUrl
:
'
some-fancy-url
'
,
buyAddonTargetAttr
:
'
_blank
'
,
props
:
{
rootStorageStatistics
:
{
totalRepositorySize
:
60
*
1024
,
actualRepositorySizeLimit
:
50
*
1024
,
totalRepositorySizeExcess
:
1024
,
additionalPurchasedStorageSize
:
10
*
1024
,
},
},
});
});
it
(
'
renders button in purchased usage card footer with correct target
'
,
()
=>
{
expect
(
findPurchasedUsageButton
().
attributes
()).
toMatchObject
({
href
:
'
some-fancy-url
'
,
target
:
'
_blank
'
,
it
(
'
shows only the limit in the namespace storage card
'
,
()
=>
{
expect
(
findNamespaceStorageCard
().
text
().
replace
(
/
\s
+/g
,
'
'
)).
toContain
(
'
50.0 KiB / 50.0 KiB
'
,
);
});
it
(
'
shows the excess amount in the purchased storage card
'
,
()
=>
{
expect
(
findPurchasedStorageCard
().
text
().
replace
(
/
\s
+/g
,
'
'
)).
toContain
(
'
1.0 KiB / 10.0 KiB
'
,
);
});
});
describe
(
'
with no purchaseStorageUrl
'
,
()
=>
{
describe
(
'
without purchased storage
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
provide
:
{
purchaseStorageUrl
:
null
,
buyAddonTargetAttr
:
'
_self
'
,
props
:
{
rootStorageStatistics
:
{
totalRepositorySize
:
502642
,
actualRepositorySizeLimit
:
500321
,
totalRepositorySizeExcess
:
2321
,
additionalPurchasedStorageSize
:
0
,
},
},
});
});
it
(
'
shows the total of limit and excess in the namespace storage card
'
,
()
=>
{
expect
(
findNamespaceStorageCard
().
text
().
replace
(
/
\s
+/g
,
'
'
)).
toContain
(
'
490.9 KiB / 488.6 KiB
'
,
);
});
it
(
'
does not render purchased usage card if purchaseStorageUrl is not provided
'
,
()
=>
{
expect
(
getStatisticsCard
(
'
purchased-usage
'
).
exists
()).
toBe
(
false
);
it
(
'
shows 0 GiB in the purchased storage card
'
,
()
=>
{
expect
(
findPurchasedStorageCard
().
text
().
replace
(
/
\s
+/g
,
'
'
)).
toContain
(
'
0 GiB
'
);
});
});
});
});
ee/spec/frontend/usage_quotas/storage/utils_spec.js
View file @
7f3519b3
...
...
@@ -5,6 +5,7 @@ import {
calculateUsedAndRemStorage
,
parseGetProjectStorageResults
,
descendingStorageUsageSort
,
formatSizeAndSplit
,
}
from
'
ee/usage_quotas/storage/utils
'
;
import
{
projectData
,
...
...
@@ -78,6 +79,11 @@ describe('formatUsageSize', () => {
`
(
'
returns $expected from $input
'
,
({
input
,
expected
})
=>
{
expect
(
formatUsageSize
(
input
)).
toBe
(
expected
);
});
it
(
'
render the output with unit separator when unitSeparator param is passed
'
,
()
=>
{
expect
(
formatUsageSize
(
1000
,
'
-
'
)).
toBe
(
'
1.0-KiB
'
);
expect
(
formatUsageSize
(
1000
,
'
'
)).
toBe
(
'
1.0 KiB
'
);
});
});
describe
(
'
calculateUsedAndRemStorage
'
,
()
=>
{
...
...
@@ -132,3 +138,13 @@ describe('descendingStorageUsageSort', () => {
expect
(
sorted
).
toEqual
(
expectedSorted
);
});
});
describe
(
'
formatSizeAndSplit
'
,
()
=>
{
it
(
'
returns null if passed parameter is null
'
,
()
=>
{
expect
(
formatSizeAndSplit
(
null
)).
toBe
(
null
);
});
it
(
'
returns formatted size as object { value, unit }
'
,
()
=>
{
expect
(
formatSizeAndSplit
(
1000
)).
toEqual
({
value
:
'
1.0
'
,
unit
:
'
KiB
'
});
});
});
locale/gitlab.pot
View file @
7f3519b3
...
...
@@ -969,9 +969,6 @@ msgstr[1] ""
msgid "%{service_ping_link_start}What information is shared with GitLab Inc.?%{service_ping_link_end}"
msgstr ""
msgid "%{size} %{unit}"
msgstr ""
msgid "%{size} GiB"
msgstr ""
...
...
@@ -40445,6 +40442,9 @@ msgstr ""
msgid "UsageQuota|Buy additional minutes"
msgstr ""
msgid "UsageQuota|Buy storage"
msgstr ""
msgid "UsageQuota|CI minutes usage by month"
msgstr ""
...
...
@@ -40475,10 +40475,10 @@ msgstr ""
msgid "UsageQuota|LFS storage"
msgstr ""
msgid "UsageQuota|Learn more about
excess storage usage
"
msgid "UsageQuota|Learn more about
usage quotas.
"
msgstr ""
msgid "UsageQuota|
Learn more about usage quotas
"
msgid "UsageQuota|
Namespace storage used
"
msgstr ""
msgid "UsageQuota|No CI minutes usage data available."
...
...
@@ -40499,7 +40499,10 @@ msgstr ""
msgid "UsageQuota|Purchase more storage"
msgstr ""
msgid "UsageQuota|Purchased storage available"
msgid "UsageQuota|Purchased storage"
msgstr ""
msgid "UsageQuota|Purchased storage used"
msgstr ""
msgid "UsageQuota|Repository"
...
...
@@ -40526,24 +40529,12 @@ msgstr ""
msgid "UsageQuota|Storage used"
msgstr ""
msgid "UsageQuota|This is the total amount of storage used across your projects within this namespace."
msgstr ""
msgid "UsageQuota|This is the total amount of storage used by projects above the free %{actualRepositorySizeLimit} storage limit."
msgstr ""
msgid "UsageQuota|This namespace contains locked projects"
msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr ""
msgid "UsageQuota|Total excess storage used"
msgstr ""
msgid "UsageQuota|Total namespace storage used"
msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
...
...
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