Commit 079d4b89 authored by Zack Cuddy's avatar Zack Cuddy Committed by Phil Hughes

Cleanup styles for Geo Node Items

Remove unneeded class

Fix geo node status colors

Node warning styles

Cleanup geo-node-item

Cleanup node section items

Mobile friendly node actions

Tweak margins

Remove unneeded class

Starting to fix tests

Another fix

Lots of tests

Final lint and tests

Fix backend tests
parent c7fc256d
...@@ -79,53 +79,49 @@ export default { ...@@ -79,53 +79,49 @@ export default {
</script> </script>
<template> <template>
<div class="geo-node-actions"> <div class="d-flex align-items-center justify-content-end geo-node-actions">
<div v-if="isSecondaryNode" class="node-action-container"> <a v-if="isSecondaryNode" :href="node.geoProjectsUrl" class="btn btn-sm mx-1 " target="_blank">
<a :href="node.geoProjectsUrl" class="btn btn-sm btn-node-action" target="_blank"> <icon v-if="!node.current" name="external-link" /> {{ __('Open projects') }}
<icon v-if="!node.current" name="external-link" /> {{ __('Open projects') }} </a>
</a>
</div>
<template v-if="nodeActionsAllowed"> <template v-if="nodeActionsAllowed">
<div v-if="nodeMissingOauth" class="node-action-container"> <button
<button type="button" class="btn btn-default btn-sm btn-node-action" @click="onRepairNode"> v-if="nodeMissingOauth"
{{ s__('Repair authentication') }} type="button"
</button> class="btn btn-sm btn-default mx-1"
</div> @click="onRepairNode"
<div v-if="isToggleAllowed" class="node-action-container"> >
<button {{ s__('Repair authentication') }}
:class="{ </button>
'btn-warning': node.enabled, <button
'btn-success': !node.enabled, v-if="isToggleAllowed"
}" :class="{
type="button" 'btn-warning': node.enabled,
class="btn btn-sm btn-node-action" 'btn-success': !node.enabled,
@click="onToggleNode" }"
> type="button"
<icon :name="nodeToggleIcon" /> class="btn btn-sm mx-1"
{{ nodeToggleLabel }} @click="onToggleNode"
</button> >
</div> <icon :name="nodeToggleIcon" />
<div v-if="nodeEditAllowed" class="node-action-container"> {{ nodeToggleLabel }}
<a :href="node.editPath" class="btn btn-sm btn-node-action"> {{ __('Edit') }} </a> </button>
</div> <a v-if="nodeEditAllowed" :href="node.editPath" class="btn btn-sm mx-1"> {{ __('Edit') }} </a>
<div class="node-action-container"> <button
<button v-if="isSecondaryNode"
v-if="isSecondaryNode" type="button"
type="button" class="btn btn-sm btn-danger mx-1"
class="btn btn-sm btn-node-action btn-danger" @click="onRemoveSecondaryNode"
@click="onRemoveSecondaryNode" >
> {{ __('Remove') }}
{{ __('Remove') }} </button>
</button> <button
<button v-if="!isSecondaryNode"
v-else type="button"
type="button" class="btn btn-sm btn-danger mx-1"
class="btn btn-sm btn-node-action btn-danger" @click="onRemovePrimaryNode"
@click="onRemovePrimaryNode" >
> {{ __('Remove') }}
{{ __('Remove') }} </button>
</button>
</div>
</template> </template>
</div> </div>
</template> </template>
...@@ -126,19 +126,21 @@ export default { ...@@ -126,19 +126,21 @@ export default {
</script> </script>
<template> <template>
<div v-if="!featureDisabled" class="prepend-top-15 prepend-left-10 node-detail-item"> <div v-if="!featureDisabled" class="mt-2 ml-2 node-detail-item">
<div class="node-detail-title"> <div class="d-flex align-items-center text-secondary-700">
<span>{{ itemTitle }}</span> <span class="node-detail-title">{{ itemTitle }}</span>
<icon <icon
v-if="hasHelpInfo" v-if="hasHelpInfo"
v-popover="popoverConfig" v-popover="popoverConfig"
:size="12" :size="12"
class="node-detail-help-text prepend-left-5" class="text-primary-600 ml-1 node-detail-help-text"
name="question" name="question"
/> />
</div> </div>
<div v-if="isValueTypePlain" :class="cssClass" class="node-detail-value">{{ itemValue }}</div> <div v-if="isValueTypePlain" :class="cssClass" class="mt-1 node-detail-value">
<div v-if="isValueTypeGraph" :class="{ 'd-flex': itemValueStale }" class="node-detail-value"> {{ itemValue }}
</div>
<div v-if="isValueTypeGraph" :class="{ 'd-flex': itemValueStale }" class="mt-1">
<stacked-progress-bar <stacked-progress-bar
:css-class="itemValueStale ? 'flex-fill' : ''" :css-class="itemValueStale ? 'flex-fill' : ''"
:success-label="successLabel" :success-label="successLabel"
...@@ -153,7 +155,7 @@ export default { ...@@ -153,7 +155,7 @@ export default {
v-tooltip v-tooltip
:title="itemValueStaleTooltip" :title="itemValueStaleTooltip"
name="time-out" name="time-out"
class="prepend-left-10 detail-value-stale-icon" class="ml-2 text-warning-500"
data-container="body" data-container="body"
/> />
</div> </div>
......
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
</script> </script>
<template> <template>
<div class="card-body"> <div class="card-body p-0">
<node-details-section-main <node-details-section-main
:node="node" :node="node"
:node-details="nodeDetails" :node-details="nodeDetails"
...@@ -85,8 +85,8 @@ export default { ...@@ -85,8 +85,8 @@ export default {
:node-details="nodeDetails" :node-details="nodeDetails"
:node-type-primary="node.primary" :node-type-primary="node.primary"
/> />
<div v-if="hasError || hasVersionMismatch" class="node-health-message-container"> <div v-if="hasError || hasVersionMismatch">
<p class="node-health-message"> <p class="p-3 mb-0 bg-danger-100 text-danger-500">
{{ errorMessage }} {{ errorMessage }}
<gl-link :href="geoTroubleshootingHelpPath">{{ <gl-link :href="geoTroubleshootingHelpPath">{{
s__('Geo|Please refer to Geo Troubleshooting.') s__('Geo|Please refer to Geo Troubleshooting.')
......
...@@ -39,7 +39,7 @@ export default { ...@@ -39,7 +39,7 @@ export default {
</script> </script>
<template> <template>
<div class="node-detail-value"> <div class="mt-1 node-detail-value">
<template v-if="eventTimeStamp"> <template v-if="eventTimeStamp">
<strong> {{ eventString }} </strong> <strong> {{ eventString }} </strong>
<span <span
......
...@@ -42,11 +42,10 @@ export default { ...@@ -42,11 +42,10 @@ export default {
return this.isNodeHTTP || this.nodeDetailsFailed; return this.isNodeHTTP || this.nodeDetailsFailed;
}, },
nodeStatusIconClass() { nodeStatusIconClass() {
const iconClasses = 'prepend-left-10 node-status-icon'; return [
if (this.nodeDetailsFailed) { 'ml-2',
return `${iconClasses} status-icon-failure`; { 'text-danger-500': this.nodeDetailsFailed, 'text-warning-500': !this.nodeDetailsFailed },
} ];
return `${iconClasses} status-icon-warning`;
}, },
nodeStatusIconName() { nodeStatusIconName() {
if (this.nodeDetailsFailed) { if (this.nodeDetailsFailed) {
...@@ -70,7 +69,7 @@ export default { ...@@ -70,7 +69,7 @@ export default {
<div class="card-header"> <div class="card-header">
<div class="row"> <div class="row">
<div class="col-md-8 clearfix"> <div class="col-md-8 clearfix">
<span class="d-flex float-left append-right-10"> <span class="d-flex align-items-center float-left append-right-10">
<strong class="node-url"> {{ node.url }} </strong> <strong class="node-url"> {{ node.url }} </strong>
<gl-loading-icon <gl-loading-icon
v-if="nodeDetailsLoading || node.nodeActionActive" v-if="nodeDetailsLoading || node.nodeActionActive"
...@@ -87,11 +86,17 @@ export default { ...@@ -87,11 +86,17 @@ export default {
data-placement="bottom" data-placement="bottom"
/> />
</span> </span>
<span class="inline node-type-badges"> <span class="inline">
<span v-if="node.current" class="node-badge current-node"> <span
v-if="node.current"
class="rounded-pill gl-font-size-12 p-1 text-white bg-success-400"
>
{{ s__('Current node') }} {{ s__('Current node') }}
</span> </span>
<span v-if="node.primary" class="prepend-left-5 node-badge primary-node"> <span
v-if="node.primary"
class="ml-1 rounded-pill gl-font-size-12 p-1 text-white bg-primary-600"
>
{{ s__('Primary') }} {{ s__('Primary') }}
</span> </span>
</span> </span>
......
<script> <script>
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import { HEALTH_STATUS_ICON } from '../constants'; import { HEALTH_STATUS_ICON, HEALTH_STATUS_CLASS } from '../constants';
export default { export default {
components: { components: {
...@@ -14,7 +14,7 @@ export default { ...@@ -14,7 +14,7 @@ export default {
}, },
computed: { computed: {
healthCssClass() { healthCssClass() {
return `geo-node-${this.status.toLowerCase()}`; return HEALTH_STATUS_CLASS[this.status.toLowerCase()];
}, },
statusIconName() { statusIconName() {
return HEALTH_STATUS_ICON[this.status.toLowerCase()]; return HEALTH_STATUS_ICON[this.status.toLowerCase()];
...@@ -24,11 +24,11 @@ export default { ...@@ -24,11 +24,11 @@ export default {
</script> </script>
<template> <template>
<div class="prepend-top-15 detail-section-item"> <div class="mt-2 detail-section-item">
<div class="node-detail-title">{{ s__('GeoNodes|Health status') }}</div> <div class="text-secondary-700 node-detail-title">{{ s__('GeoNodes|Health status') }}</div>
<div :class="healthCssClass" class="node-detail-value node-health-status"> <div :class="healthCssClass" class="mt-1 d-flex align-items-center node-health-status">
<icon :size="16" :name="statusIconName" /> <icon :size="16" :name="statusIconName" />
<span class="status-text prepend-left-5"> {{ status }} </span> <span class="status-text ml-2"> {{ status }} </span>
</div> </div>
</div> </div>
</template> </template>
...@@ -78,7 +78,7 @@ export default { ...@@ -78,7 +78,7 @@ export default {
</script> </script>
<template> <template>
<div :class="{ 'node-action-active': node.nodeActionActive }" class="card geo-node-item"> <div :class="{ 'node-action-active': node.nodeActionActive }" class="card">
<geo-node-header <geo-node-header
:node="node" :node="node"
:node-details="nodeDetails" :node-details="nodeDetails"
...@@ -93,8 +93,8 @@ export default { ...@@ -93,8 +93,8 @@ export default {
:node-actions-allowed="nodeActionsAllowed" :node-actions-allowed="nodeActionsAllowed"
:geo-troubleshooting-help-path="geoTroubleshootingHelpPath" :geo-troubleshooting-help-path="geoTroubleshootingHelpPath"
/> />
<div v-if="isNodeDetailsFailed" class="node-health-message-container"> <div v-if="isNodeDetailsFailed">
<p class="node-health-message"> <p class="p-3 mb-0 bg-danger-100 text-danger-500">
{{ errorMessage {{ errorMessage
}}<gl-link :href="geoTroubleshootingHelpPath">{{ }}<gl-link :href="geoTroubleshootingHelpPath">{{
s__('Geo|Please refer to Geo Troubleshooting.') s__('Geo|Please refer to Geo Troubleshooting.')
......
...@@ -108,18 +108,18 @@ export default { ...@@ -108,18 +108,18 @@ export default {
</script> </script>
<template> <template>
<div class="node-detail-value"> <div class="mt-1 node-sync-settings">
<span v-if="syncStatusUnavailable" class="node-detail-value-bold"> {{ __('Unknown') }} </span> <strong v-if="syncStatusUnavailable"> {{ __('Unknown') }} </strong>
<span <span
v-else v-else
v-tooltip v-tooltip
:title="syncStatusTooltip" :title="syncStatusTooltip"
class="node-sync-settings" class="d-flex align-items-center"
data-placement="bottom" data-placement="bottom"
> >
<strong>{{ syncType }}</strong> <strong>{{ syncType }}</strong>
<icon name="retry" class="sync-status-icon prepend-left-5" /> <icon name="retry" class="ml-2" />
<span v-if="!eventTimestampEmpty" class="sync-status-event-info prepend-left-5"> <span v-if="!eventTimestampEmpty" class="ml-2">
{{ syncStatusEventInfo }} {{ syncStatusEventInfo }}
</span> </span>
</span> </span>
......
...@@ -46,13 +46,13 @@ export default { ...@@ -46,13 +46,13 @@ export default {
</script> </script>
<template> <template>
<div class="row-fluid clearfix node-detail-section primary-section"> <div class="row-fluid clearfix py-3 primary-section">
<div class="col-md-8 section-items-container"> <div class="col-md-8">
<div class="detail-section-item node-version"> <div>
<div class="node-detail-title">{{ s__('GeoNodes|GitLab version') }}</div> <div class="text-secondary-700 node-detail-title">{{ s__('GeoNodes|GitLab version') }}</div>
<div <div
:class="{ 'node-detail-value-error': versionMismatch }" :class="{ 'text-danger-500': versionMismatch }"
class="node-detail-value node-detail-value-bold" class="mt-1 font-weight-bold node-detail-value"
> >
{{ nodeVersion }} {{ nodeVersion }}
</div> </div>
......
...@@ -54,7 +54,7 @@ export default { ...@@ -54,7 +54,7 @@ export default {
itemTitle: s__('GeoNodes|Replication slot WAL'), itemTitle: s__('GeoNodes|Replication slot WAL'),
itemValue: numberToHumanSize(this.nodeDetails.replicationSlotWAL), itemValue: numberToHumanSize(this.nodeDetails.replicationSlotWAL),
itemValueType: VALUE_TYPE.PLAIN, itemValueType: VALUE_TYPE.PLAIN,
cssClass: 'node-detail-value-bold', cssClass: 'font-weight-bold',
}); });
} }
...@@ -63,7 +63,7 @@ export default { ...@@ -63,7 +63,7 @@ export default {
itemTitle: s__('GeoNodes|Internal URL'), itemTitle: s__('GeoNodes|Internal URL'),
itemValue: this.node.internalUrl, itemValue: this.node.internalUrl,
itemValueType: VALUE_TYPE.PLAIN, itemValueType: VALUE_TYPE.PLAIN,
cssClass: 'node-detail-value-bold', cssClass: 'font-weight-bold',
}); });
} }
...@@ -76,7 +76,7 @@ export default { ...@@ -76,7 +76,7 @@ export default {
itemTitle: s__('GeoNodes|Storage config'), itemTitle: s__('GeoNodes|Storage config'),
itemValue: this.storageShardsStatus, itemValue: this.storageShardsStatus,
itemValueType: VALUE_TYPE.PLAIN, itemValueType: VALUE_TYPE.PLAIN,
cssClass: this.storageShardsCssClass, cssClass: this.storageShardsCssClass.join(' '),
}, },
]; ];
}, },
...@@ -89,10 +89,7 @@ export default { ...@@ -89,10 +89,7 @@ export default {
: s__('GeoNodes|Does not match the primary storage configuration'); : s__('GeoNodes|Does not match the primary storage configuration');
}, },
storageShardsCssClass() { storageShardsCssClass() {
const cssClass = 'node-detail-value-bold'; return ['font-weight-bold', { 'text-danger-500': !this.nodeDetails.storageShardsMatch }];
return !this.nodeDetails.storageShardsMatch
? `${cssClass} node-detail-value-error`
: cssClass;
}, },
}, },
methods: { methods: {
...@@ -104,17 +101,14 @@ export default { ...@@ -104,17 +101,14 @@ export default {
</script> </script>
<template> <template>
<div class="row-fluid clearfix node-detail-section other-section"> <div class="row-fluid clearfix py-3 border-top border-color-default other-section">
<div class="col-md-12"> <div class="col-md-12">
<section-reveal-button <section-reveal-button
:button-title="__('Other information')" :button-title="__('Other information')"
@toggleButton="handleSectionToggle" @toggleButton="handleSectionToggle"
/> />
</div> </div>
<div <div v-if="showSectionItems" class="col-md-6 ml-2 mt-2 section-items-container">
v-show="showSectionItems"
class="col-md-6 prepend-left-15 prepend-top-10 section-items-container"
>
<geo-node-detail-item <geo-node-detail-item
v-for="(nodeDetailItem, index) in nodeDetailItems" v-for="(nodeDetailItem, index) in nodeDetailItems"
:key="index" :key="index"
......
...@@ -130,17 +130,14 @@ export default { ...@@ -130,17 +130,14 @@ export default {
</script> </script>
<template> <template>
<div class="row-fluid clearfix node-detail-section sync-section"> <div class="row-fluid clearfix py-3 border-top border-color-default sync-section">
<div class="col-md-12"> <div class="col-md-12">
<section-reveal-button <section-reveal-button
:button-title="__('Sync information')" :button-title="__('Sync information')"
@toggleButton="handleSectionToggle" @toggleButton="handleSectionToggle"
/> />
</div> </div>
<div <div v-if="showSectionItems" class="col-md-6 ml-2 mt-2 section-items-container">
v-show="showSectionItems"
class="col-md-6 prepend-left-15 prepend-top-10 section-items-container"
>
<geo-node-detail-item <geo-node-detail-item
v-for="(nodeDetailItem, index) in nodeDetailItems" v-for="(nodeDetailItem, index) in nodeDetailItems"
:key="index" :key="index"
......
...@@ -113,7 +113,7 @@ export default { ...@@ -113,7 +113,7 @@ export default {
</script> </script>
<template> <template>
<div class="row-fluid clearfix node-detail-section verification-section"> <div class="row-fluid clearfix py-3 border-top border-color-default verification-section">
<div class="col-md-12"> <div class="col-md-12">
<section-reveal-button <section-reveal-button
:button-title="__('Verification information')" :button-title="__('Verification information')"
...@@ -121,7 +121,7 @@ export default { ...@@ -121,7 +121,7 @@ export default {
/> />
</div> </div>
<template v-if="showSectionItems"> <template v-if="showSectionItems">
<div class="col-md-6 prepend-left-15 prepend-top-10 section-items-container"> <div class="col-md-6 ml-2 mt-2 section-items-container">
<geo-node-detail-item <geo-node-detail-item
v-for="(nodeDetailItem, index) in nodeDetailItems" v-for="(nodeDetailItem, index) in nodeDetailItems"
:key="index" :key="index"
......
...@@ -31,7 +31,7 @@ export default { ...@@ -31,7 +31,7 @@ export default {
</script> </script>
<template> <template>
<button class="btn-link btn-show-section" type="button" @click="onClickButton"> <button class="btn-link d-flex align-items-center" type="button" @click="onClickButton">
<icon :size="16" :name="toggleButtonIcon" /> <icon :size="16" :name="toggleButtonIcon" />
<span class="prepend-left-8">{{ buttonTitle }}</span> <span class="prepend-left-8">{{ buttonTitle }}</span>
</button> </button>
......
...@@ -23,6 +23,14 @@ export const HEALTH_STATUS_ICON = { ...@@ -23,6 +23,14 @@ export const HEALTH_STATUS_ICON = {
offline: 'status_canceled', offline: 'status_canceled',
}; };
export const HEALTH_STATUS_CLASS = {
healthy: 'text-success-500',
unhealthy: 'text-danger-500',
disabled: 'text-secondary-950',
unknown: 'cdark',
offline: 'cdark',
};
export const TIME_DIFF = { export const TIME_DIFF = {
FIVE_MINS: 300, FIVE_MINS: 300,
HOUR: 3600, HOUR: 3600,
......
.node-badge { @media (max-width: map-get($grid-breakpoints, sm)) {
color: $white-light; .geo-node-actions {
padding: 1px $gl-padding-8; flex-direction: column;
font-size: $label-font-size; margin: 0 1rem;
border-radius: $label-border-radius;
&.primary-node { .btn-sm {
background-color: $blue-600;
}
&.current-node {
background-color: $green-400;
}
}
.geo-node-healthy {
color: $green-500;
}
.geo-node-unhealthy {
color: $red-500;
}
.geo-node-offline {
color: $gray-950;
}
.geo-node-disabled {
color: $gray-darkest;
}
.geo-node-unknown {
color: $gray-darkest;
}
.geo-node-item {
.node-status-icon {
height: 35px;
}
.status-icon-warning {
fill: $orange-500;
}
.status-icon-failure {
fill: $red-500;
}
.card-body {
padding: 0;
.node-detail-section {
padding: $gl-padding 0;
&.sync-section,
&.verification-section,
&.other-section {
border-top: 1px solid $border-color;
}
.btn-show-section {
padding: 0;
}
}
}
.node-health-message-container {
max-height: $dropdown-max-height;
overflow-y: auto;
.node-health-message {
margin-bottom: 0;
padding: $gl-padding;
background-color: $red-100;
color: $red-500;
}
}
}
.node-detail-section {
.detail-section-item,
.section-items-container {
.node-detail-title {
color: $gray-700;
.node-detail-help-text {
color: $blue-600;
}
.tooltip .tooltip-inner {
text-align: left;
}
}
.node-detail-value {
margin-top: 4px;
}
.detail-value-stale-icon {
color: $orange-500;
}
.node-detail-value-bold {
font-weight: $gl-font-weight-bold;
}
.node-detail-value-error {
color: $red-500;
}
}
.section-items-container {
display: inline-block;
.node-detail-item {
&:first-child {
margin-top: 0;
}
}
@include media-breakpoint-down(sm) {
width: 95%;
}
}
}
.node-detail-title,
.node-health-status,
.node-sync-settings,
.node-detail-section .btn-show-section {
display: flex;
align-items: center;
}
.geo-node-actions {
display: inline-flex;
justify-content: center;
float: right;
.node-action-container {
margin: 0 5px;
&:last-child {
margin-right: $gl-padding;
}
}
@include media-breakpoint-down(sm) {
display: block;
width: 100%;
.node-action-container {
width: 100%;
margin: 0;
margin-top: 10px;
padding: 0 10px;
}
.btn-node-action {
width: 100%; width: 100%;
margin-top: 1rem;
} }
} }
} }
......
...@@ -16,7 +16,7 @@ describe 'admin Geo Nodes', :js, :geo do ...@@ -16,7 +16,7 @@ describe 'admin Geo Nodes', :js, :geo do
wait_for_requests wait_for_requests
expect(page).to have_link('New node', href: new_admin_geo_node_path) expect(page).to have_link('New node', href: new_admin_geo_node_path)
page.within(find('.geo-node-item', match: :first)) do page.within(find('.card', match: :first)) do
expect(page).to have_content(geo_node.url) expect(page).to have_content(geo_node.url)
end end
end end
...@@ -86,7 +86,7 @@ describe 'admin Geo Nodes', :js, :geo do ...@@ -86,7 +86,7 @@ describe 'admin Geo Nodes', :js, :geo do
expect(current_path).to eq admin_geo_nodes_path expect(current_path).to eq admin_geo_nodes_path
wait_for_requests wait_for_requests
page.within(find('.geo-node-item', match: :first)) do page.within(find('.card', match: :first)) do
expect(page).to have_content(geo_node.url) expect(page).to have_content(geo_node.url)
end end
end end
...@@ -167,7 +167,7 @@ describe 'admin Geo Nodes', :js, :geo do ...@@ -167,7 +167,7 @@ describe 'admin Geo Nodes', :js, :geo do
expect(current_path).to eq admin_geo_nodes_path expect(current_path).to eq admin_geo_nodes_path
wait_for_requests wait_for_requests
page.within(find('.geo-node-item', match: :first)) do page.within(find('.card', match: :first)) do
expect(page).to have_content('http://newsite.com') expect(page).to have_content('http://newsite.com')
expect(page).to have_content('Primary') expect(page).to have_content('Primary')
...@@ -196,7 +196,7 @@ describe 'admin Geo Nodes', :js, :geo do ...@@ -196,7 +196,7 @@ describe 'admin Geo Nodes', :js, :geo do
expect(current_path).to eq admin_geo_nodes_path expect(current_path).to eq admin_geo_nodes_path
wait_for_requests wait_for_requests
expect(page).not_to have_css('.geo-node-item') expect(page).not_to have_css('.card')
end end
end end
end end
...@@ -125,8 +125,7 @@ describe('GeoNodeActionsComponent', () => { ...@@ -125,8 +125,7 @@ describe('GeoNodeActionsComponent', () => {
describe('template', () => { describe('template', () => {
it('renders container elements correctly', () => { it('renders container elements correctly', () => {
expect(vm.$el.classList.contains('geo-node-actions')).toBe(true); expect(vm.$el.classList.contains('geo-node-actions')).toBe(true);
expect(vm.$el.querySelectorAll('.node-action-container').length).not.toBe(0); expect(vm.$el.querySelectorAll('.btn-sm').length).not.toBe(0);
expect(vm.$el.querySelectorAll('.btn-node-action').length).not.toBe(0);
}); });
}); });
}); });
...@@ -75,7 +75,7 @@ describe('GeoNodeDetailItemComponent', () => { ...@@ -75,7 +75,7 @@ describe('GeoNodeDetailItemComponent', () => {
itemValueStaleTooltip, itemValueStaleTooltip,
}); });
const iconEl = vm.$el.querySelector('.detail-value-stale-icon'); const iconEl = vm.$el.querySelector('.text-warning-500');
expect(iconEl).not.toBeNull(); expect(iconEl).not.toBeNull();
expect(iconEl.dataset.originalTitle).toBe(itemValueStaleTooltip); expect(iconEl.dataset.originalTitle).toBe(itemValueStaleTooltip);
......
...@@ -87,9 +87,7 @@ describe('GeoNodeDetailsComponent', () => { ...@@ -87,9 +87,7 @@ describe('GeoNodeDetailsComponent', () => {
vm.errorMessage = 'Foobar'; vm.errorMessage = 'Foobar';
vm.$nextTick(() => { vm.$nextTick(() => {
expect(vm.$el.querySelector('.node-health-message-container a').getAttribute('href')).toBe( expect(vm.$el.querySelector('.bg-danger-100 a').getAttribute('href')).toBe('/foo/bar');
'/foo/bar',
);
done(); done();
}); });
}); });
......
import Vue from 'vue'; import Vue from 'vue';
import geoNodeHealthStatusComponent from 'ee/geo_nodes/components/geo_node_health_status.vue'; import geoNodeHealthStatusComponent from 'ee/geo_nodes/components/geo_node_health_status.vue';
import { HEALTH_STATUS_ICON, HEALTH_STATUS_CLASS } from 'ee/geo_nodes/constants';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockNodeDetails } from '../mock_data'; import { mockNodeDetails } from '../mock_data';
...@@ -16,38 +17,38 @@ describe('GeoNodeHealthStatusComponent', () => { ...@@ -16,38 +17,38 @@ describe('GeoNodeHealthStatusComponent', () => {
describe('computed', () => { describe('computed', () => {
describe('healthCssClass', () => { describe('healthCssClass', () => {
it('returns CSS class representing `status` prop value', () => { it('returns CSS class representing `status` prop value', () => {
const vm = createComponent('Healthy'); const vm = createComponent('healthy');
expect(vm.healthCssClass).toBe('geo-node-healthy'); expect(vm.healthCssClass).toBe(HEALTH_STATUS_CLASS.healthy);
vm.$destroy(); vm.$destroy();
}); });
}); });
describe('statusIconName', () => { describe('statusIconName', () => {
it('returns icon name representing `status` prop value', () => { it('returns icon name representing `status` prop value', () => {
let vm = createComponent('Healthy'); let vm = createComponent('healthy');
expect(vm.statusIconName).toBe('status_success'); expect(vm.statusIconName).toBe(HEALTH_STATUS_ICON.healthy);
vm.$destroy(); vm.$destroy();
vm = createComponent('Unhealthy'); vm = createComponent('unhealthy');
expect(vm.statusIconName).toBe('status_failed'); expect(vm.statusIconName).toBe(HEALTH_STATUS_ICON.unhealthy);
vm.$destroy(); vm.$destroy();
vm = createComponent('Disabled'); vm = createComponent('disabled');
expect(vm.statusIconName).toBe('status_canceled'); expect(vm.statusIconName).toBe(HEALTH_STATUS_ICON.disabled);
vm.$destroy(); vm.$destroy();
vm = createComponent('Unknown'); vm = createComponent('unknown');
expect(vm.statusIconName).toBe('status_warning'); expect(vm.statusIconName).toBe(HEALTH_STATUS_ICON.unknown);
vm.$destroy(); vm.$destroy();
vm = createComponent('Offline'); vm = createComponent('offline');
expect(vm.statusIconName).toBe('status_canceled'); expect(vm.statusIconName).toBe(HEALTH_STATUS_ICON.offline);
vm.$destroy(); vm.$destroy();
}); });
}); });
...@@ -60,7 +61,7 @@ describe('GeoNodeHealthStatusComponent', () => { ...@@ -60,7 +61,7 @@ describe('GeoNodeHealthStatusComponent', () => {
expect(vm.$el.classList.contains('detail-section-item')).toBe(true); expect(vm.$el.classList.contains('detail-section-item')).toBe(true);
expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('Health status'); expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('Health status');
const iconContainerEl = vm.$el.querySelector('.node-detail-value.node-health-status'); const iconContainerEl = vm.$el.querySelector('.node-health-status');
expect(iconContainerEl).not.toBeNull(); expect(iconContainerEl).not.toBeNull();
expect(iconContainerEl.querySelector('svg use').getAttribute('xlink:href')).toContain( expect(iconContainerEl.querySelector('svg use').getAttribute('xlink:href')).toContain(
......
...@@ -148,11 +148,9 @@ describe('GeoNodeItemComponent', () => { ...@@ -148,11 +148,9 @@ describe('GeoNodeItemComponent', () => {
vm.isNodeDetailsFailed = true; vm.isNodeDetailsFailed = true;
vm.errorMessage = err; vm.errorMessage = err;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(vm.$el.querySelectorAll('p.node-health-message').length).not.toBe(0); expect(vm.$el.querySelectorAll('p.bg-danger-100').length).not.toBe(0);
expect(vm.$el.querySelector('p.node-health-message').innerText.trim()).toContain(err); expect(vm.$el.querySelector('p.bg-danger-100').innerText.trim()).toContain(err);
expect(vm.$el.querySelector('p.node-health-message a').getAttribute('href')).toBe( expect(vm.$el.querySelector('p.bg-danger-100 a').getAttribute('href')).toBe('/foo/bar');
'/foo/bar',
);
done(); done();
}); });
}); });
......
...@@ -89,18 +89,20 @@ describe('NodeDetailsSectionOther', () => { ...@@ -89,18 +89,20 @@ describe('NodeDetailsSectionOther', () => {
}); });
describe('storageShardsCssClass', () => { describe('storageShardsCssClass', () => {
it('returns CSS class `node-detail-value-bold` when `nodeDetails.storageShardsMatch` is true', done => { it('returns CSS class `font-weight-bold` when `nodeDetails.storageShardsMatch` is true', done => {
vm.nodeDetails.storageShardsMatch = true; vm.nodeDetails.storageShardsMatch = true;
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.storageShardsCssClass).toBe('node-detail-value-bold'); expect(vm.storageShardsCssClass[0]).toBe('font-weight-bold');
expect(vm.storageShardsCssClass[1]['text-danger-500']).toBeFalsy();
}) })
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
}); });
it('returns CSS class `node-detail-value-bold node-detail-value-error` when `nodeDetails.storageShardsMatch` is false', () => { it('returns CSS class `font-weight-bold text-danger-500` when `nodeDetails.storageShardsMatch` is false', () => {
expect(vm.storageShardsCssClass).toBe('node-detail-value-bold node-detail-value-error'); expect(vm.storageShardsCssClass[0]).toBe('font-weight-bold');
expect(vm.storageShardsCssClass[1]['text-danger-500']).toBeTruthy();
}); });
}); });
}); });
...@@ -111,14 +113,16 @@ describe('NodeDetailsSectionOther', () => { ...@@ -111,14 +113,16 @@ describe('NodeDetailsSectionOther', () => {
}); });
it('renders show section button element', () => { it('renders show section button element', () => {
expect(vm.$el.querySelector('.btn-show-section')).not.toBeNull(); expect(vm.$el.querySelector('.btn-link')).not.toBeNull();
expect(vm.$el.querySelector('.btn-show-section > span').innerText.trim()).toBe( expect(vm.$el.querySelector('.btn-link > span').innerText.trim()).toBe('Other information');
'Other information',
);
}); });
it('renders section items container element', () => { it('renders section items container element', done => {
expect(vm.$el.querySelector('.section-items-container')).not.toBeNull(); vm.showSectionItems = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.section-items-container')).not.toBeNull();
done();
});
}); });
}); });
}); });
...@@ -89,14 +89,16 @@ describe('NodeDetailsSectionSync', () => { ...@@ -89,14 +89,16 @@ describe('NodeDetailsSectionSync', () => {
}); });
it('renders show section button element', () => { it('renders show section button element', () => {
expect(vm.$el.querySelector('.btn-show-section')).not.toBeNull(); expect(vm.$el.querySelector('.btn-link')).not.toBeNull();
expect(vm.$el.querySelector('.btn-show-section > span').innerText.trim()).toBe( expect(vm.$el.querySelector('.btn-link > span').innerText.trim()).toBe('Sync information');
'Sync information',
);
}); });
it('renders section items container element', () => { it('renders section items container element', done => {
expect(vm.$el.querySelector('.section-items-container')).not.toBeNull(); vm.showSectionItems = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.section-items-container')).not.toBeNull();
done();
});
}); });
}); });
}); });
...@@ -83,6 +83,13 @@ describe('NodeDetailsSectionVerification', () => { ...@@ -83,6 +83,13 @@ describe('NodeDetailsSectionVerification', () => {
expect(vm.$el.classList.contains('verification-section')).toBe(true); expect(vm.$el.classList.contains('verification-section')).toBe(true);
}); });
it('renders show section button element', () => {
expect(vm.$el.querySelector('.btn-link')).not.toBeNull();
expect(vm.$el.querySelector('.btn-link > span').innerText.trim()).toBe(
'Verification information',
);
});
it('renders section items container element', done => { it('renders section items container element', done => {
vm.showSectionItems = true; vm.showSectionItems = true;
Vue.nextTick(() => { Vue.nextTick(() => {
......
...@@ -62,7 +62,7 @@ describe('SectionRevealButton', () => { ...@@ -62,7 +62,7 @@ describe('SectionRevealButton', () => {
describe('template', () => { describe('template', () => {
it('renders button element', () => { it('renders button element', () => {
expect(vm.$el.classList.contains('btn-show-section')).toBe(true); expect(vm.$el.classList.contains('btn-link')).toBe(true);
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('#angle-down'); expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('#angle-down');
expect(vm.$el.querySelector('span').innerText.trim()).toBe('Foo button'); expect(vm.$el.querySelector('span').innerText.trim()).toBe('Foo button');
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment