Commit 7edc337f authored by Kushal Pandya's avatar Kushal Pandya

Update layout for Geo nodes admin page

parent 2c84eb5c
......@@ -10,13 +10,13 @@ import eventHub from '../event_hub';
import { NODE_ACTIONS } from '../constants';
import geoNodesList from './geo_nodes_list.vue';
import GeoNodeItem from './geo_node_item.vue';
export default {
components: {
loadingIcon,
DeprecatedModal,
geoNodesList,
GeoNodeItem,
},
props: {
store: {
......@@ -46,7 +46,6 @@ export default {
modalKind: 'warning',
modalMessage: '',
modalActionLabel: '',
errorMessage: '',
};
},
computed: {
......@@ -85,7 +84,6 @@ export default {
});
},
fetchGeoNodes() {
this.hasError = false;
this.service
.getGeoNodes()
.then(res => res.data)
......@@ -93,9 +91,11 @@ export default {
this.store.setNodes(nodes);
this.isLoading = false;
})
.catch(err => {
this.hasError = true;
this.errorMessage = err;
.catch(() => {
this.isLoading = false;
Flash(
s__('GeoNodes|Something went wrong while fetching nodes'),
);
});
},
fetchNodeDetails(node) {
......@@ -217,28 +217,21 @@ export default {
</script>
<template>
<div class="panel panel-default">
<div class="panel-heading">
Geo nodes ({{ nodes.length }})
</div>
<div class="geo-nodes-container">
<loading-icon
class="loading-animation prepend-top-20 append-bottom-20"
size="2"
v-if="isLoading"
:label="s__('GeoNodes|Loading nodes')"
/>
<geo-nodes-list
v-if="!isLoading"
:nodes="nodes"
<geo-node-item
v-for="(node, index) in nodes"
:key="index"
:node="node"
:primary-node="node.primary"
:node-actions-allowed="nodeActionsAllowed"
:node-edit-allowed="nodeEditAllowed"
/>
<p
class="health-message prepend-left-15 append-right-15"
v-if="hasError"
>
{{ errorMessage }}
</p>
<deprecated-modal
v-show="showModal"
:title="__('Are you sure?')"
......
......@@ -4,14 +4,12 @@
import { VALUE_TYPE, CUSTOM_TYPE } from '../constants';
import geoNodeHealthStatus from './geo_node_health_status.vue';
import geoNodeSyncSettings from './geo_node_sync_settings.vue';
import geoNodeEventStatus from './geo_node_event_status.vue';
export default {
components: {
stackedProgressBar,
geoNodeHealthStatus,
geoNodeSyncSettings,
geoNodeEventStatus,
},
......@@ -53,6 +51,11 @@
required: false,
default: '',
},
eventTypeLogStatus: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
isValueTypePlain() {
......@@ -64,9 +67,6 @@
isValueTypeCustom() {
return this.itemValueType === VALUE_TYPE.CUSTOM;
},
isCustomTypeStatus() {
return this.customType === CUSTOM_TYPE.STATUS;
},
isCustomTypeSync() {
return this.customType === CUSTOM_TYPE.SYNC;
},
......@@ -75,7 +75,7 @@
</script>
<template>
<li class="row node-detail-item">
<div class="node-detail-item prepend-top-15 prepend-left-10">
<div class="node-detail-title">
{{ itemTitle }}
</div>
......@@ -100,12 +100,8 @@
/>
</div>
<template v-if="isValueTypeCustom">
<geo-node-health-status
v-if="isCustomTypeStatus"
:status="itemValue"
/>
<geo-node-sync-settings
v-else-if="isCustomTypeSync"
v-if="isCustomTypeSync"
:sync-status-unavailable="itemValue.syncStatusUnavailable"
:selective-sync-type="itemValue.selectiveSyncType"
:last-event="itemValue.lastEvent"
......@@ -115,7 +111,8 @@
v-else
:event-id="itemValue.eventId"
:event-time-stamp="itemValue.eventTimeStamp"
:event-type-log-status="eventTypeLogStatus"
/>
</template>
</li>
</div>
</template>
<script>
import { s__, sprintf } from '~/locale';
import { formatDate } from '~/lib/utils/datetime_utility';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -18,6 +19,12 @@
eventTimeStamp: {
type: Number,
required: true,
default: 0,
},
eventTypeLogStatus: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
......@@ -27,6 +34,14 @@
timeStampString() {
return formatDate(this.timeStamp);
},
eventString() {
if (this.eventTypeLogStatus) {
return sprintf(s__('GeoNodes|%{eventId} events behind'), {
eventId: this.eventId,
});
}
return this.eventId;
},
},
};
</script>
......@@ -37,7 +52,7 @@
>
<template v-if="eventTimeStamp">
<strong>
{{ eventId }}
{{ eventString }}
</strong>
<span
v-tooltip
......
......@@ -24,6 +24,10 @@
</script>
<template>
<div class="prepend-top-15 detail-section-item">
<div class="node-detail-title">
{{ s__('GeoNodes|Health status:') }}
</div>
<div
class="node-detail-value node-health-status"
:class="healthCssClass"
......@@ -38,4 +42,5 @@
{{ status }}
</span>
</div>
</div>
</template>
<script>
import { s__ } from '~/locale';
import icon from '~/vue_shared/components/icon.vue';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../event_hub';
import geoNodeActions from './geo_node_actions.vue';
import geoNodeDetails from './geo_node_details.vue';
import GeoNodeHeader from './geo_node_header.vue';
import GeoNodeDetails from './geo_node_details.vue';
export default {
components: {
icon,
loadingIcon,
geoNodeActions,
geoNodeDetails,
},
directives: {
tooltip,
GeoNodeHeader,
GeoNodeDetails,
},
props: {
node: {
......@@ -47,41 +37,12 @@ export default {
};
},
computed: {
isNodeNonHTTPS() {
return this.node.url.startsWith('http://');
},
showNodeStatusIcon() {
if (this.isNodeDetailsLoading) {
return false;
}
return this.isNodeNonHTTPS || this.isNodeDetailsFailed;
},
showNodeDetails() {
if (!this.isNodeDetailsLoading) {
return !this.isNodeDetailsFailed;
}
return false;
},
nodeStatusIconClass() {
const iconClasses = 'prepend-left-10 pull-left';
if (this.isNodeDetailsFailed) {
return `${iconClasses} node-status-icon-failure`;
}
return `${iconClasses} node-status-icon-warning`;
},
nodeStatusIconName() {
if (this.isNodeDetailsFailed) {
return 'status_failed_borderless';
}
return 'warning';
},
nodeStatusIconTooltip() {
if (this.isNodeDetailsFailed) {
return '';
}
return s__('GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS.');
},
},
created() {
eventHub.$on('nodeDetailsLoaded', this.handleNodeDetails);
......@@ -119,59 +80,22 @@ export default {
</script>
<template>
<li
<div
class="panel panel-default geo-node-item"
:class="{ 'node-action-active': node.nodeActionActive }"
>
<div class="row">
<div class="col-md-8">
<div class="row">
<div class="col-md-8 clearfix">
<strong class="node-url inline pull-left">
{{ node.url }}
</strong>
<loading-icon
v-if="isNodeDetailsLoading || node.nodeActionActive"
class="node-details-loading prepend-left-10 pull-left inline"
size="1"
/>
<icon
v-tooltip
v-if="showNodeStatusIcon"
data-container="body"
data-placement="bottom"
:name="nodeStatusIconName"
:size="18"
:css-classes="nodeStatusIconClass"
:title="nodeStatusIconTooltip"
/>
<span class="inline pull-left prepend-left-10">
<span
class="node-badge current-node"
v-if="node.current"
>
{{ s__('Current node') }}
</span>
<span
class="node-badge primary-node"
v-if="node.primary"
>
{{ s__('Primary') }}
</span>
</span>
</div>
</div>
</div>
<geo-node-actions
v-if="showNodeDetails && nodeActionsAllowed"
<geo-node-header
:node="node"
:node-edit-allowed="nodeEditAllowed"
:node-missing-oauth="nodeDetails.missingOAuthApplication"
:node-details="nodeDetails"
:node-details-loading="isNodeDetailsLoading"
:node-details-failed="isNodeDetailsFailed"
/>
</div>
<geo-node-details
v-if="showNodeDetails"
:node="node"
:node-details="nodeDetails"
:node-edit-allowed="nodeEditAllowed"
:node-actions-allowed="nodeActionsAllowed"
/>
<div
v-if="isNodeDetailsFailed"
......@@ -181,5 +105,5 @@ export default {
{{ errorMessage }}
</p>
</div>
</li>
</div>
</template>
......@@ -118,7 +118,7 @@
<span
v-else
v-tooltip
class="node-sync-settings inline"
class="node-sync-settings"
data-placement="bottom"
:title="syncStatusTooltip"
>
......
......@@ -23,7 +23,7 @@ export default {
</script>
<template>
<ul class="well-list geo-nodes">
<div class="panel panel-default">
<geo-node-item
v-for="(node, index) in nodes"
:key="index"
......@@ -32,5 +32,5 @@ export default {
:node-actions-allowed="nodeActionsAllowed"
:node-edit-allowed="nodeEditAllowed"
/>
</ul>
</div>
</template>
......@@ -56,7 +56,6 @@ describe('AppComponent', () => {
expect(vm.modalKind).toBe('warning');
expect(vm.modalMessage).toBe('');
expect(vm.modalActionLabel).toBe('');
expect(vm.errorMessage).toBe('');
});
});
......@@ -91,10 +90,9 @@ describe('AppComponent', () => {
spyOn(vm.store, 'setNodes');
vm.fetchGeoNodes();
expect(vm.hasError).toBeFalsy();
setTimeout(() => {
expect(vm.store.setNodes).toHaveBeenCalledWith(mockNodes);
expect(vm.isLoading).toBeFalsy();
expect(vm.isLoading).toBe(false);
done();
}, 0);
});
......@@ -104,10 +102,9 @@ describe('AppComponent', () => {
statusCode = 500;
vm.fetchGeoNodes();
expect(vm.hasError).toBeFalsy();
setTimeout(() => {
expect(vm.hasError).toBeTruthy();
expect(vm.errorMessage.response.data).toBe(response);
expect(vm.isLoading).toBe(false);
expect(document.querySelector('.flash-text').innerText.trim()).toBe('Something went wrong while fetching nodes');
done();
}, 0);
});
......@@ -380,39 +377,13 @@ describe('AppComponent', () => {
});
describe('template', () => {
it('renders container elements correctly', () => {
expect(vm.$el.classList.contains('panel', 'panel-default')).toBeTruthy();
expect(vm.$el.querySelectorAll('.panel-heading').length).not.toBe(0);
expect(vm.$el.querySelector('.panel-heading').innerText.trim()).toBe('Geo nodes (0)');
it('renders container element with class `geo-nodes-container`', () => {
expect(vm.$el.classList.contains('geo-nodes-container')).toBe(true);
});
it('renders loading animation when `isLoading` is true', () => {
vm.isLoading = true;
expect(vm.$el.querySelectorAll('.loading-animation.prepend-top-20.append-bottom-20').length).not.toBe(0);
});
it('renders list of nodes', (done) => {
vm.store.setNodes(mockNodes);
vm.isLoading = false;
Vue.nextTick(() => {
expect(vm.$el.querySelectorAll('.loading-animation.prepend-top-20.append-bottom-20').length).toBe(0);
expect(vm.$el.querySelectorAll('ul.geo-nodes').length).not.toBe(0);
done();
});
});
it('renders error message', (done) => {
vm.hasError = true;
vm.isLoading = false;
vm.errorMessage = 'Something went wrong.';
Vue.nextTick(() => {
const errEl = 'p.health-message.prepend-left-15.append-right-15';
expect(vm.$el.querySelectorAll(errEl).length).not.toBe(0);
expect(vm.$el.querySelector(errEl).innerText.trim()).toBe(vm.errorMessage);
done();
});
});
});
});
......@@ -46,16 +46,6 @@ describe('GeoNodeDetailItemComponent', () => {
vm.$destroy();
});
it('renders health status item value', () => {
const vm = createComponent({
itemValueType: VALUE_TYPE.CUSTOM,
customType: CUSTOM_TYPE.STATUS,
itemValue: rawMockNodeDetails.health,
});
expect(vm.$el.querySelectorAll('.node-health-status').length).not.toBe(0);
vm.$destroy();
});
it('renders sync settings item value', () => {
const vm = createComponent({
itemValueType: VALUE_TYPE.CUSTOM,
......
......@@ -2,14 +2,21 @@ import Vue from 'vue';
import geoNodeDetailsComponent from 'ee/geo_nodes/components/geo_node_details.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockNodes, mockNodeDetails } from '../mock_data';
const createComponent = (nodeDetails = mockNodeDetails) => {
import { mockNode, mockNodeDetails } from '../mock_data';
const createComponent = ({
node = mockNode,
nodeDetails = mockNodeDetails,
nodeActionsAllowed = true,
nodeEditAllowed = true,
}) => {
const Component = Vue.extend(geoNodeDetailsComponent);
return mountComponent(Component, {
node,
nodeDetails,
node: mockNodes[1],
nodeActionsAllowed,
nodeEditAllowed,
});
};
......@@ -17,7 +24,7 @@ describe('GeoNodeDetailsComponent', () => {
let vm;
beforeEach(() => {
vm = createComponent();
vm = createComponent({});
});
afterEach(() => {
......@@ -28,7 +35,6 @@ describe('GeoNodeDetailsComponent', () => {
it('returns default data props', () => {
expect(vm.showAdvanceItems).toBeFalsy();
expect(vm.errorMessage).toBe('');
expect(Array.isArray(vm.nodeDetailItems)).toBeTruthy();
});
});
......@@ -40,7 +46,7 @@ describe('GeoNodeDetailsComponent', () => {
health: 'Something went wrong.',
healthy: false,
});
const vmX = createComponent(nodeDetails);
const vmX = createComponent({ nodeDetails });
expect(vmX.errorMessage).toBe('Something went wrong.');
expect(vmX.hasError).toBeTruthy();
vmX.$destroy();
......@@ -57,7 +63,7 @@ describe('GeoNodeDetailsComponent', () => {
primaryVersion: '10.3.0-pre',
primaryRevision: 'b93c51850b',
});
const vmX = createComponent(nodeDetails);
const vmX = createComponent({ nodeDetails });
expect(vmX.errorMessage).toBe('GitLab version does not match the primary node version');
expect(vmX.hasVersionMismatch).toBeTruthy();
vmX.$destroy();
......@@ -66,147 +72,11 @@ describe('GeoNodeDetailsComponent', () => {
expect(vm.hasVersionMismatch).toBeFalsy();
});
});
describe('advanceButtonIcon', () => {
it('returns button icon name', () => {
vm.showAdvanceItems = true;
expect(vm.advanceButtonIcon).toBe('angle-up');
vm.showAdvanceItems = false;
expect(vm.advanceButtonIcon).toBe('angle-down');
});
});
describe('nodeVersion', () => {
it('returns `Unknown` when `version` and `revision` are null', () => {
const nodeDetailsVersionNull = Object.assign({}, mockNodeDetails, {
version: null,
revision: null,
});
const vmVersionNull = createComponent(nodeDetailsVersionNull);
expect(vmVersionNull.nodeVersion).toBe('Unknown');
vmVersionNull.$destroy();
});
it('returns version string', () => {
expect(vm.nodeVersion).toBe('10.4.0-pre (b93c51849b)');
});
});
describe('replicationSlotWAL', () => {
it('returns replication slot WAL in Megabytes', () => {
expect(vm.replicationSlotWAL).toBe('479.37 MiB');
});
});
describe('dbReplicationLag', () => {
it('returns DB replication lag time duration', () => {
expect(vm.dbReplicationLag).toBe('0m');
});
it('returns `Unknown` when `dbReplicationLag` is null', () => {
const nodeDetailsLagNull = Object.assign({}, mockNodeDetails, {
dbReplicationLag: null,
});
const vmLagNull = createComponent(nodeDetailsLagNull);
expect(vmLagNull.dbReplicationLag).toBe('Unknown');
vmLagNull.$destroy();
});
});
describe('lastEventStatus', () => {
it('returns event status object', () => {
expect(vm.lastEventStatus.eventId).toBe(mockNodeDetails.lastEvent.id);
expect(vm.lastEventStatus.eventTimeStamp).toBe(mockNodeDetails.lastEvent.timeStamp);
});
});
describe('cursorLastEventStatus', () => {
it('returns event status object', () => {
expect(vm.cursorLastEventStatus.eventId).toBe(mockNodeDetails.cursorLastEvent.id);
expect(vm.cursorLastEventStatus.eventTimeStamp)
.toBe(mockNodeDetails.cursorLastEvent.timeStamp);
});
});
});
describe('methods', () => {
describe('nodeHealthStatus', () => {
it('returns health status string', () => {
// With altered mock data for Unhealthy status
const nodeDetails = Object.assign({}, mockNodeDetails, {
healthStatus: 'Unhealthy',
healthy: false,
});
const vmX = createComponent(nodeDetails);
expect(vmX.nodeHealthStatus()).toBe('Unhealthy');
vmX.$destroy();
// With default mock data
expect(vm.nodeHealthStatus()).toBe('Healthy');
});
});
describe('storageShardsStatus', () => {
it('returns storage shard status string', () => {
// With altered mock data for Unhealthy status
let nodeDetails = Object.assign({}, mockNodeDetails, {
storageShardsMatch: null,
});
let vmX = createComponent(nodeDetails);
expect(vmX.storageShardsStatus()).toBe('Unknown');
vmX.$destroy();
nodeDetails = Object.assign({}, mockNodeDetails, {
storageShardsMatch: true,
});
vmX = createComponent(nodeDetails);
expect(vmX.storageShardsStatus()).toBe('OK');
vmX.$destroy();
// With default mock data
expect(vm.storageShardsStatus()).toBe('Does not match the primary storage configuration');
});
});
describe('plainValueCssClass', () => {
it('returns CSS class for plain value item', () => {
expect(vm.plainValueCssClass()).toBe('node-detail-value-bold');
expect(vm.plainValueCssClass(true)).toBe('node-detail-value-bold node-detail-value-error');
});
});
describe('syncSettings', () => {
it('returns sync settings object', () => {
const nodeDetailsUnknownSync = Object.assign({}, mockNodeDetails, {
syncStatusUnavailable: true,
});
const vmUnknownSync = createComponent(nodeDetailsUnknownSync);
const syncSettings = vmUnknownSync.syncSettings();
expect(syncSettings.syncStatusUnavailable).toBe(true);
expect(syncSettings.namespaces).toBe(mockNodeDetails.namespaces);
expect(syncSettings.lastEvent).toBe(mockNodeDetails.lastEvent);
expect(syncSettings.cursorLastEvent).toBe(mockNodeDetails.cursorLastEvent);
vmUnknownSync.$destroy();
});
});
describe('onClickShowAdvance', () => {
it('toggles `showAdvanceItems` prop', () => {
vm.showAdvanceItems = true;
vm.onClickShowAdvance();
expect(vm.showAdvanceItems).toBeFalsy();
vm.showAdvanceItems = false;
vm.onClickShowAdvance();
expect(vm.showAdvanceItems).toBeTruthy();
});
});
});
describe('template', () => {
it('renders container elements correctly', () => {
expect(vm.$el.querySelectorAll('.node-details-list').length).not.toBe(0);
expect(vm.$el.querySelectorAll('.btn-show-advanced').length).not.toBe(0);
expect(vm.$el.classList.contains('panel-body')).toBe(true);
});
});
});
......@@ -4,15 +4,17 @@ import geoNodeEventStatusComponent from 'ee/geo_nodes/components/geo_node_event_
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockNodeDetails } from '../mock_data';
const createComponent = (
const createComponent = ({
eventId = mockNodeDetails.lastEvent.id,
eventTimeStamp = mockNodeDetails.lastEvent.timeStamp,
) => {
eventTypeLogStatus = false,
}) => {
const Component = Vue.extend(geoNodeEventStatusComponent);
return mountComponent(Component, {
eventId,
eventTimeStamp,
eventTypeLogStatus,
});
};
......@@ -20,7 +22,7 @@ describe('GeoNodeEventStatus', () => {
let vm;
beforeEach(() => {
vm = createComponent();
vm = createComponent({});
});
afterEach(() => {
......@@ -39,6 +41,18 @@ describe('GeoNodeEventStatus', () => {
expect(vm.timeStampString).toContain('Nov 21, 2017');
});
});
describe('eventString', () => {
it('returns computed event string when `eventTypeLogStatus` prop is true', () => {
const vmWithLogStatus = createComponent({ eventTypeLogStatus: true });
expect(vmWithLogStatus.eventString).toBe(`${mockNodeDetails.lastEvent.id} events behind`);
vmWithLogStatus.$destroy();
});
it('returns event ID as it is when `eventTypeLogStatus` prop is false', () => {
expect(vm.eventString).toBe(mockNodeDetails.lastEvent.id);
});
});
});
describe('template', () => {
......@@ -50,7 +64,10 @@ describe('GeoNodeEventStatus', () => {
});
it('renders empty state when timestamp is not present', () => {
const vmWithoutTimestamp = createComponent(0, 0);
const vmWithoutTimestamp = createComponent({
eventId: 0,
eventTimeStamp: 0,
});
expect(vmWithoutTimestamp.$el.querySelectorAll('strong').length).not.toBe(0);
expect(vmWithoutTimestamp.$el.querySelectorAll('.event-status-timestamp').length).toBe(0);
expect(vmWithoutTimestamp.$el.querySelector('strong').innerText.trim()).toBe('Not available');
......
......@@ -50,10 +50,13 @@ describe('GeoNodeHealthStatusComponent', () => {
describe('template', () => {
it('renders container elements correctly', () => {
const vm = createComponent('Healthy');
expect(vm.$el.classList.contains('node-detail-value', 'node-health-status', 'geo-node-healthy')).toBeTruthy();
expect(vm.$el.querySelectorAll('svg').length).not.toBe(0);
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('#status_success');
expect(vm.$el.querySelector('.status-text').innerText.trim()).toBe('Healthy');
expect(vm.$el.classList.contains('detail-section-item')).toBe(true);
expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('Health status:');
const iconContainerEl = vm.$el.querySelector('.node-detail-value.node-health-status');
expect(iconContainerEl).not.toBeNull();
expect(iconContainerEl.querySelector('svg use').getAttribute('xlink:href')).toContain('#status_success');
expect(iconContainerEl.querySelector('.status-text').innerText.trim()).toBe('Healthy');
vm.$destroy();
});
});
......
......@@ -3,9 +3,9 @@ import Vue from 'vue';
import geoNodeItemComponent from 'ee/geo_nodes/components/geo_node_item.vue';
import eventHub from 'ee/geo_nodes/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockNodes, mockNodeDetails } from '../mock_data';
import { mockNode, mockNodeDetails } from '../mock_data';
const createComponent = (node = mockNodes[0]) => {
const createComponent = (node = mockNode) => {
const Component = Vue.extend(geoNodeItemComponent);
return mountComponent(Component, {
......@@ -29,8 +29,8 @@ describe('GeoNodeItemComponent', () => {
describe('data', () => {
it('returns default data props', () => {
expect(vm.isNodeDetailsLoading).toBeTruthy();
expect(vm.isNodeDetailsFailed).toBeFalsy();
expect(vm.isNodeDetailsLoading).toBe(true);
expect(vm.isNodeDetailsFailed).toBe(false);
expect(vm.nodeHealthStatus).toBe('');
expect(vm.errorMessage).toBe('');
expect(typeof vm.nodeDetails).toBe('object');
......@@ -39,57 +39,20 @@ describe('GeoNodeItemComponent', () => {
describe('computed', () => {
let vmHttps;
let mockNode;
let httpsNode;
beforeEach(() => {
// Altered mock data for secure URL
mockNode = Object.assign({}, mockNodes[0], {
httpsNode = Object.assign({}, mockNode, {
url: 'https://127.0.0.1:3001/',
});
vmHttps = createComponent(mockNode);
vmHttps = createComponent(httpsNode);
});
afterEach(() => {
vmHttps.$destroy();
});
describe('isNodeNonHTTPS', () => {
it('returns `true` if Node URL protocol is non-HTTPS', () => {
// With default mock data
expect(vm.isNodeNonHTTPS).toBeTruthy();
});
it('returns `false` is Node URL protocol is HTTPS', () => {
// With altered mock data
expect(vmHttps.isNodeNonHTTPS).toBeFalsy();
});
});
describe('showNodeStatusIcon', () => {
it('returns `false` if Node details are still loading', () => {
vm.isNodeDetailsLoading = true;
expect(vm.showNodeStatusIcon).toBeFalsy();
});
it('returns `true` if Node details failed to load', () => {
vm.isNodeDetailsLoading = false;
vm.isNodeDetailsFailed = true;
expect(vm.showNodeStatusIcon).toBeTruthy();
});
it('returns `true` if Node details loaded and Node URL is non-HTTPS', () => {
vm.isNodeDetailsLoading = false;
vm.isNodeDetailsFailed = false;
expect(vm.showNodeStatusIcon).toBeTruthy();
});
it('returns `false` if Node details loaded and Node URL is HTTPS', () => {
vmHttps.isNodeDetailsLoading = false;
vmHttps.isNodeDetailsFailed = false;
expect(vmHttps.showNodeStatusIcon).toBeFalsy();
});
});
describe('showNodeDetails', () => {
it('returns `false` if Node details are still loading', () => {
vm.isNodeDetailsLoading = true;
......@@ -108,50 +71,17 @@ describe('GeoNodeItemComponent', () => {
expect(vm.showNodeDetails).toBeTruthy();
});
});
describe('nodeStatusIconClass', () => {
it('returns `node-status-icon-failure` along with default classes if Node details failed to load', () => {
vm.isNodeDetailsFailed = true;
expect(vm.nodeStatusIconClass).toBe('prepend-left-10 pull-left node-status-icon-failure');
});
it('returns `node-status-icon-warning` along with default classes if Node details loaded and Node URL is non-HTTPS', () => {
vm.isNodeDetailsFailed = false;
expect(vm.nodeStatusIconClass).toBe('prepend-left-10 pull-left node-status-icon-warning');
});
});
describe('nodeStatusIconName', () => {
it('returns `warning` if Node details loaded and Node URL is non-HTTPS', () => {
vm.isNodeDetailsFailed = false;
expect(vm.nodeStatusIconName).toBe('warning');
});
it('returns `status_failed_borderless` if Node details failed to load', () => {
vm.isNodeDetailsFailed = true;
expect(vm.nodeStatusIconName).toBe('status_failed_borderless');
});
});
describe('nodeStatusIconTooltip', () => {
it('returns empty string if Node details failed to load', () => {
vm.isNodeDetailsFailed = true;
expect(vm.nodeStatusIconTooltip).toBe('');
});
it('returns tooltip string if Node details loaded and Node URL is non-HTTPS', () => {
vm.isNodeDetailsFailed = false;
expect(vm.nodeStatusIconTooltip).toBe('You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS.');
});
});
});
describe('methods', () => {
describe('handleNodeDetails', () => {
it('intializes props based on provided `nodeDetails`', () => {
// With altered mock data with matching ID
const mockNode = Object.assign({}, mockNodes[1]);
const vmNodePrimary = createComponent(mockNode);
const mockNodeSecondary = Object.assign({}, mockNode, {
id: mockNodeDetails.id,
primary: false,
});
const vmNodePrimary = createComponent(mockNodeSecondary);
vmNodePrimary.handleNodeDetails(mockNodeDetails);
expect(vmNodePrimary.isNodeDetailsLoading).toBeFalsy();
......@@ -212,21 +142,8 @@ describe('GeoNodeItemComponent', () => {
});
describe('template', () => {
it('renders node URL', () => {
expect(vm.$el.querySelectorAll('.node-url').length).not.toBe(0);
});
it('renders node details loading animation', () => {
vm.isNodeDetailsLoading = true;
expect(vm.$el.querySelectorAll('.node-details-loading').length).not.toBe(0);
});
it('renders node badge `Current node`', () => {
expect(vm.$el.querySelectorAll('.node-badge.current-node').length).not.toBe(0);
});
it('renders node badge `Primary`', () => {
expect(vm.$el.querySelectorAll('.node-badge.primary-node').length).not.toBe(0);
it('renders container element', () => {
expect(vm.$el.classList.contains('panel', 'panel-default', 'geo-node-item')).toBe(true);
});
it('renders node error message', (done) => {
......
......@@ -18,7 +18,7 @@ describe('GeoNodesListComponent', () => {
describe('template', () => {
it('renders container element correctly', () => {
const vm = createComponent();
expect(vm.$el.classList.contains('well-list', 'geo-nodes')).toBeTruthy();
expect(vm.$el.classList.contains('panel', 'panel-default')).toBe(true);
vm.$destroy();
});
});
......
......@@ -145,6 +145,7 @@ export const mockNodeDetails = {
replicationSlotWAL: 502658737,
missingOAuthApplication: false,
storageShardsMatch: false,
repositoryVerificationEnabled: true,
replicationSlots: {
totalCount: null,
successCount: null,
......@@ -175,6 +176,16 @@ export const mockNodeDetails = {
successCount: 0,
failureCount: 0,
},
verifiedRepositories: {
totalCount: 0,
successCount: 0,
failureCount: 0,
},
verifiedWikis: {
totalCount: 0,
successCount: 0,
failureCount: 0,
},
lastEvent: {
id: 3,
timeStamp: 1511255200,
......
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