summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/AppHeader/AppHeader.vue56
-rw-r--r--src/components/AppNavigation/AppNavigation.vue2
-rw-r--r--src/locales/en-US.json2
-rw-r--r--tests/unit/AppHeader.spec.js84
4 files changed, 85 insertions, 59 deletions
diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue
index 08c82565..114d6c9d 100644
--- a/src/components/AppHeader/AppHeader.vue
+++ b/src/components/AppHeader/AppHeader.vue
@@ -1,19 +1,23 @@
<template>
<div>
- <a class="link-skip-nav btn btn-light" href="#main-content">
- {{ $t('appHeader.skipToContent') }}
- </a>
<header id="page-header">
- <b-navbar variant="dark" type="dark">
+ <a role="link" class="link-skip-nav btn btn-light" href="#main-content">
+ {{ $t('appHeader.skipToContent') }}
+ </a>
+
+ <b-navbar
+ variant="dark"
+ type="dark"
+ :aria-label="$t('appHeader.applicationHeader')"
+ >
<!-- Left aligned nav items -->
<b-button
+ id="app-header-trigger"
class="nav-trigger"
aria-hidden="true"
title="Open navigation"
type="button"
variant="link"
- :aria-expanded="isNavigationOpen"
- :class="{ 'nav-open': isNavigationOpen }"
@click="toggleNavigation"
>
<icon-close v-if="isNavigationOpen" />
@@ -24,24 +28,27 @@
</b-navbar-nav>
<!-- Right aligned nav items -->
<b-navbar-nav class="ml-auto">
- <b-nav>
- <b-nav-item>
- {{ $t('appHeader.health') }}
- <status-icon :status="healthStatusIcon" />
- </b-nav-item>
- <b-nav-item>
- {{ $t('appHeader.power') }}
- <status-icon :status="hostStatusIcon" />
- </b-nav-item>
- <b-nav-item @click="refresh">
+ <b-nav-item>
+ {{ $t('appHeader.health') }}
+ <status-icon :status="healthStatusIcon" />
+ </b-nav-item>
+ <b-nav-item>
+ {{ $t('appHeader.power') }}
+ <status-icon :status="hostStatusIcon" />
+ </b-nav-item>
+ <!-- Using LI elements instead of b-nav-item to support semantic button elements -->
+ <li class="nav-item">
+ <b-button id="app-header-refresh" variant="link" @click="refresh">
{{ $t('appHeader.refresh') }}
<icon-renew />
- </b-nav-item>
- <b-nav-item @click="logout">
+ </b-button>
+ </li>
+ <li>
+ <b-button id="app-header-logout" variant="link" @click="logout">
{{ $t('appHeader.logOut') }}
<icon-avatar />
- </b-nav-item>
- </b-nav>
+ </b-button>
+ </li>
</b-navbar-nav>
</b-navbar>
</header>
@@ -138,10 +145,13 @@ export default {
}
.navbar-dark {
.navbar-text,
- .nav-link {
+ .nav-link,
+ .btn-link {
color: $white !important;
+ fill: currentColor;
}
}
+
.nav-item {
fill: $light;
}
@@ -150,6 +160,10 @@ export default {
padding: 0;
height: $header-height;
overflow: hidden;
+
+ .btn-link {
+ padding: $spacer / 2;
+ }
}
.navbar-nav {
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue
index f2f049b3..94076de3 100644
--- a/src/components/AppNavigation/AppNavigation.vue
+++ b/src/components/AppNavigation/AppNavigation.vue
@@ -1,7 +1,7 @@
<template>
<div>
<div class="nav-container" :class="{ open: isNavigationOpen }">
- <nav ref="nav">
+ <nav ref="nav" :aria-label="$t('appNavigation.primaryNavigation')">
<b-nav vertical>
<b-nav-item to="/">
<icon-overview />
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 22bb5142..b243f539 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -36,6 +36,7 @@
}
},
"appHeader": {
+ "applicationHeader": "Application header",
"bmcSystemManagement": "BMC System Management",
"health": "Health",
"logOut": "Log out",
@@ -56,6 +57,7 @@
"managePowerUsage": "@:appPageTitle.managePowerUsage",
"networkSettings": "@:appPageTitle.networkSettings",
"overview": "@:appPageTitle.overview",
+ "primaryNavigation": "Primary navigation",
"rebootBmc": "@:appPageTitle.rebootBmc",
"sensors": "@:appPageTitle.sensors",
"serverLed": "@:appPageTitle.serverLed",
diff --git a/tests/unit/AppHeader.spec.js b/tests/unit/AppHeader.spec.js
index 6dea960b..52e45437 100644
--- a/tests/unit/AppHeader.spec.js
+++ b/tests/unit/AppHeader.spec.js
@@ -1,62 +1,72 @@
-import { mount } from '@vue/test-utils';
+import { shallowMount, createLocalVue, createWrapper } from '@vue/test-utils';
import Vue from 'vue';
+import Vuex from 'vuex';
import AppHeader from '@/components/AppHeader';
-import $store from '@/store';
-import { BootstrapVue } from 'bootstrap-vue';
+
+// Silencing warnings about undefined Bootsrap-vue components
+Vue.config.silent = true;
+const localVue = createLocalVue();
+localVue.use(Vuex);
describe('AppHeader.vue', () => {
- let wrapper;
- let spy;
- Vue.use(BootstrapVue);
+ const actions = {
+ 'global/getHostStatus': sinon.spy(),
+ 'eventLog/getEventLogData': sinon.spy()
+ };
- wrapper = mount(AppHeader, {
+ const store = new Vuex.Store({ actions });
+ const wrapper = shallowMount(AppHeader, {
+ store,
+ localVue,
mocks: {
- $t: key => key,
- $store
+ $t: key => key
}
});
+ // Reset spy for each test. Otherwise mutiple actions
+ // are dispatched in each test
beforeEach(() => {
- spy = sinon.spy($store.dispatch);
+ store.dispatch = sinon.spy();
});
- describe('Component exists', () => {
- it('should check if AppHeader exists', async () => {
- expect(wrapper.exists());
+ describe('UI', () => {
+ it('should check if AppHeader exists', () => {
+ expect(wrapper.exists()).to.be.true;
});
- });
- describe('AppHeader methods', () => {
- it('should call getHostInfo and dispatch global/getHostStatus', async () => {
- wrapper.vm.getHostInfo();
- spy('global/getHostStatus');
- expect(spy).to.have.been.calledWith('global/getHostStatus');
+ it('should check if the skip navigation link exists', () => {
+ expect(wrapper.get('.link-skip-nav').exists()).to.be.true;
});
- it('should call getEvents and dispatch eventLog/getEventLogData', async () => {
- wrapper.vm.getEvents();
- spy('eventLog/getEventLogData');
- expect(spy).to.have.been.calledWith('eventLog/getEventLogData');
+ it('refresh button click should emit refresh event', async () => {
+ wrapper.get('#app-header-refresh').trigger('click');
+ await wrapper.vm.$nextTick();
+ expect(wrapper.emitted().refresh).to.exist;
});
- it('should call refresh and emit refresh', async () => {
- spy = sinon.spy(wrapper.vm.$emit);
- wrapper.vm.refresh();
- spy('refresh');
- expect(spy).to.have.been.calledWith('refresh');
+ it('nav-trigger button click should emit toggle:navigation event', async () => {
+ const rootWrapper = createWrapper(wrapper.vm.$root);
+ wrapper.get('#app-header-trigger').trigger('click');
+ await wrapper.vm.$nextTick();
+ expect(rootWrapper.emitted()['toggle:navigation']).to.exist;
});
- it('should call logout and dispatch authentication/logout', async () => {
- wrapper.vm.logout();
- spy('authentication/logout');
- expect(spy).to.have.been.calledWith('authentication/logout');
+ it('logout button should dispatch authentication/logout', async () => {
+ wrapper.get('#app-header-logout').trigger('click');
+ await wrapper.vm.$nextTick();
+ expect(store.dispatch).calledWith('authentication/logout');
});
+ });
- it('should call toggleNavigation and dispatch toggle:navigation', async () => {
- spy = sinon.spy(wrapper.vm.$root.$emit);
- wrapper.vm.toggleNavigation();
- spy('toggle:navigation');
- expect(spy).to.have.been.calledWith('toggle:navigation');
+ describe('Methods', () => {
+ it('getHostInfo should dispatch global/getHostStatus', () => {
+ wrapper.vm.getHostInfo();
+ expect(store.dispatch).calledWith('global/getHostStatus');
+ });
+
+ it('getEvents should dispatch eventLog/getEventLogData', () => {
+ wrapper.vm.getEvents();
+ expect(store.dispatch).calledWith('eventLog/getEventLogData');
});
});
});