Generate JHipster 9.3.0 baseline application
This commit is contained in:
1 parent
0a9b7b923f
commit
1d97cc4e31
345 files changed
+25563
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { computed } from 'vue';
|
||||
import { type Router } from 'vue-router';
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import { useLoginModal } from '@/account/login-modal';
|
||||
import { createRouter } from '@/router';
|
||||
import { useStore } from '@/store';
|
||||
|
||||
import JhiNavbar from './jhi-navbar.vue';
|
||||
|
||||
type JhiNavbarComponentType = InstanceType<typeof JhiNavbar>;
|
||||
|
||||
const pinia = createTestingPinia({ stubActions: false });
|
||||
const store = useStore();
|
||||
|
||||
describe('JhiNavbar', () => {
|
||||
let jhiNavbar: JhiNavbarComponentType;
|
||||
let login: ReturnType<typeof useLoginModal>;
|
||||
const accountService = { hasAnyAuthorityAndCheckAuth: vi.fn().mockImplementation(() => Promise.resolve(true)) };
|
||||
const changeLanguage = vi.fn();
|
||||
let router: Router;
|
||||
|
||||
beforeEach(() => {
|
||||
router = createRouter();
|
||||
const wrapper = shallowMount(JhiNavbar, {
|
||||
global: {
|
||||
plugins: [pinia, router],
|
||||
stubs: {
|
||||
'font-awesome-icon': true,
|
||||
'b-navbar': true,
|
||||
'b-navbar-nav': true,
|
||||
'b-dropdown-item': true,
|
||||
'b-collapse': true,
|
||||
'b-nav-item': true,
|
||||
'b-nav-item-dropdown': true,
|
||||
'b-navbar-toggle': true,
|
||||
'b-navbar-brand': true,
|
||||
},
|
||||
provide: {
|
||||
currentLanguage: computed(() => 'foo'),
|
||||
changeLanguage,
|
||||
accountService,
|
||||
},
|
||||
},
|
||||
});
|
||||
jhiNavbar = wrapper.vm;
|
||||
login = useLoginModal();
|
||||
});
|
||||
|
||||
it('should not have user data set', () => {
|
||||
expect(jhiNavbar.authenticated).toBeFalsy();
|
||||
expect(jhiNavbar.openAPIEnabled).toBeFalsy();
|
||||
expect(jhiNavbar.inProduction).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should have user data set after authentication', () => {
|
||||
store.setAuthentication({ login: 'test' });
|
||||
|
||||
expect(jhiNavbar.authenticated).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have profile info set after info retrieved', () => {
|
||||
store.setActiveProfiles(['prod', 'api-docs']);
|
||||
|
||||
expect(jhiNavbar.openAPIEnabled).toBeTruthy();
|
||||
expect(jhiNavbar.inProduction).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should use login service', () => {
|
||||
jhiNavbar.showLogin();
|
||||
|
||||
expect(login.showLogin).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use account service', () => {
|
||||
jhiNavbar.hasAnyAuthority('auth');
|
||||
|
||||
expect(accountService.hasAnyAuthorityAndCheckAuth).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('logout should clear credentials and return to the home page when no provider URL is returned', async () => {
|
||||
store.setAuthentication({ login: 'test' });
|
||||
|
||||
await jhiNavbar.logout();
|
||||
|
||||
expect(jhiNavbar.authenticated).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should determine active route', async () => {
|
||||
await router.push('/forbidden');
|
||||
|
||||
expect(jhiNavbar.subIsActive('/titi')).toBeFalsy();
|
||||
expect(jhiNavbar.subIsActive('/forbidden')).toBeTruthy();
|
||||
expect(jhiNavbar.subIsActive(['/forbidden', 'forbidden'])).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call translationService when changing language', () => {
|
||||
jhiNavbar.changeLanguage('fr');
|
||||
|
||||
expect(changeLanguage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check for correct language', () => {
|
||||
expect(jhiNavbar.isActiveLanguage('en')).toBeFalsy();
|
||||
expect(jhiNavbar.isActiveLanguage('foo')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { type Ref, computed, defineComponent, inject, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import type AccountService from '@/account/account.service';
|
||||
import { useLoginModal } from '@/account/login-modal';
|
||||
import EntitiesMenu from '@/entities/entities-menu.vue';
|
||||
import languages from '@/shared/config/languages';
|
||||
import { AUTHENTICATION_TOKEN_KEY } from '@/shared/jhipster/constants';
|
||||
import { useStore } from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'JhiNavbar',
|
||||
components: {
|
||||
'entities-menu': EntitiesMenu,
|
||||
},
|
||||
setup() {
|
||||
const { showLogin } = useLoginModal();
|
||||
const accountService = inject<AccountService>('accountService');
|
||||
const currentLanguage = inject('currentLanguage', () => computed(() => navigator.language ?? 'en'), true);
|
||||
const changeLanguage = inject<(string) => Promise<void>>('changeLanguage');
|
||||
|
||||
const isActiveLanguage = (key: string) => key === currentLanguage.value;
|
||||
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
|
||||
const version = `v${APP_VERSION}`;
|
||||
const hasAnyAuthorityValues: Ref = ref({});
|
||||
|
||||
const openAPIEnabled = computed(() => store.activeProfiles.includes('api-docs'));
|
||||
const inProduction = computed(() => store.activeProfiles.includes('prod'));
|
||||
const { authenticated } = storeToRefs(store);
|
||||
|
||||
const subIsActive = (input: string | string[]) => {
|
||||
const paths = Array.isArray(input) ? input : [input];
|
||||
// current path starts with this path string
|
||||
return paths.some(path => router.currentRoute.value.path.startsWith(path));
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
localStorage.removeItem(AUTHENTICATION_TOKEN_KEY);
|
||||
sessionStorage.removeItem(AUTHENTICATION_TOKEN_KEY);
|
||||
store.logout();
|
||||
if (router.currentRoute.value.path !== '/') {
|
||||
await router.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
logout,
|
||||
subIsActive,
|
||||
accountService,
|
||||
showLogin,
|
||||
changeLanguage,
|
||||
languages: languages(),
|
||||
isActiveLanguage,
|
||||
version,
|
||||
currentLanguage,
|
||||
hasAnyAuthorityValues,
|
||||
openAPIEnabled,
|
||||
inProduction,
|
||||
authenticated,
|
||||
t$: useI18n().t,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
hasAnyAuthority(authorities: any): boolean {
|
||||
this.accountService.hasAnyAuthorityAndCheckAuth(authorities).then(value => {
|
||||
if (this.hasAnyAuthorityValues[authorities] !== value) {
|
||||
this.hasAnyAuthorityValues = { ...this.hasAnyAuthorityValues, [authorities]: value };
|
||||
}
|
||||
});
|
||||
return this.hasAnyAuthorityValues[authorities] ?? false;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<b-navbar data-cy="navbar" toggleable="md" variant="dark" data-bs-theme="dark">
|
||||
<b-navbar-brand class="logo" to="/">
|
||||
<span class="logo-img"></span>
|
||||
<span class="navbar-title">{{ t$('global.title') }}</span> <span class="navbar-version">{{ version }}</span>
|
||||
</b-navbar-brand>
|
||||
<b-navbar-toggle
|
||||
right
|
||||
class="jh-navbar-toggler d-lg-none"
|
||||
data-toggle="collapse"
|
||||
target="header-tabs"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<font-awesome-icon icon="bars" />
|
||||
</b-navbar-toggle>
|
||||
|
||||
<b-collapse is-nav id="header-tabs">
|
||||
<b-navbar-nav class="ms-auto">
|
||||
<b-nav-item to="/" exact>
|
||||
<span>
|
||||
<font-awesome-icon icon="fa-solid fa-home" />
|
||||
<span>{{ t$('global.menu.home') }}</span>
|
||||
</span>
|
||||
</b-nav-item>
|
||||
<b-nav-item-dropdown
|
||||
:no-size="true"
|
||||
end
|
||||
id="entity-menu"
|
||||
v-if="authenticated"
|
||||
active-class="active"
|
||||
class="pointer"
|
||||
data-cy="entity"
|
||||
>
|
||||
<template #button-content>
|
||||
<span class="navbar-dropdown-menu">
|
||||
<font-awesome-icon icon="th-list" />
|
||||
<span class="no-bold">{{ t$('global.menu.entities.main') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<entities-menu></entities-menu>
|
||||
<!-- jhipster-needle-add-entity-to-menu - JHipster will add entities to the menu here -->
|
||||
</b-nav-item-dropdown>
|
||||
<b-nav-item-dropdown
|
||||
right
|
||||
id="admin-menu"
|
||||
v-if="hasAnyAuthority('ROLE_ADMIN') && authenticated"
|
||||
:class="{ 'router-link-active': subIsActive('/admin') }"
|
||||
active-class="active"
|
||||
class="pointer"
|
||||
data-cy="adminMenu"
|
||||
>
|
||||
<template #button-content>
|
||||
<span class="navbar-dropdown-menu">
|
||||
<font-awesome-icon icon="users-cog" />
|
||||
<span class="no-bold">{{ t$('global.menu.admin.main') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<b-dropdown-item to="/admin/user-management" active-class="active">
|
||||
<font-awesome-icon icon="users" />
|
||||
<span>{{ t$('global.menu.admin.userManagement') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item to="/admin/metrics" active-class="active">
|
||||
<font-awesome-icon icon="tachometer-alt" />
|
||||
<span>{{ t$('global.menu.admin.metrics') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item to="/admin/health" active-class="active">
|
||||
<font-awesome-icon icon="heart" />
|
||||
<span>{{ t$('global.menu.admin.health') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item to="/admin/configuration" active-class="active">
|
||||
<font-awesome-icon icon="cogs" />
|
||||
<span>{{ t$('global.menu.admin.configuration') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item to="/admin/logs" active-class="active">
|
||||
<font-awesome-icon icon="tasks" />
|
||||
<span>{{ t$('global.menu.admin.logs') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item v-if="openAPIEnabled" to="/admin/docs" active-class="active">
|
||||
<font-awesome-icon icon="book" />
|
||||
<span>{{ t$('global.menu.admin.apidocs') }}</span>
|
||||
</b-dropdown-item>
|
||||
</b-nav-item-dropdown>
|
||||
<b-nav-item-dropdown id="languagesnavBarDropdown" end v-if="languages && Object.keys(languages).length > 1">
|
||||
<template #button-content>
|
||||
<font-awesome-icon icon="flag" />
|
||||
<span class="no-bold">{{ t$('global.menu.language') }}</span>
|
||||
</template>
|
||||
<b-dropdown-item
|
||||
v-for="(value, key) in languages"
|
||||
:key="`lang-${key}`"
|
||||
@click="changeLanguage(key)"
|
||||
:class="{ active: isActiveLanguage(key) }"
|
||||
>
|
||||
{{ value.name }}
|
||||
</b-dropdown-item>
|
||||
</b-nav-item-dropdown>
|
||||
<b-nav-item-dropdown
|
||||
right
|
||||
id="account-menu"
|
||||
:class="{ 'router-link-active': subIsActive('/account') }"
|
||||
active-class="active"
|
||||
class="pointer"
|
||||
data-cy="accountMenu"
|
||||
>
|
||||
<template #button-content>
|
||||
<span class="navbar-dropdown-menu">
|
||||
<font-awesome-icon icon="user" />
|
||||
<span class="no-bold">{{ t$('global.menu.account.main') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<b-dropdown-item data-cy="settings" to="/account/settings" v-if="authenticated" active-class="active">
|
||||
<font-awesome-icon icon="wrench" />
|
||||
<span>{{ t$('global.menu.account.settings') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item data-cy="passwordItem" to="/account/password" v-if="authenticated" active-class="active">
|
||||
<font-awesome-icon icon="lock" />
|
||||
<span>{{ t$('global.menu.account.password') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item data-cy="logout" v-if="authenticated" @click="logout()" id="logout" active-class="active">
|
||||
<font-awesome-icon icon="sign-out-alt" />
|
||||
<span>{{ t$('global.menu.account.logout') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item data-cy="login" v-if="!authenticated" @click="showLogin()" id="login" active-class="active">
|
||||
<font-awesome-icon icon="sign-in-alt" />
|
||||
<span>{{ t$('global.menu.account.login') }}</span>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item data-cy="register" to="/register" id="register" v-if="!authenticated" active-class="active">
|
||||
<font-awesome-icon icon="user-plus" />
|
||||
<span>{{ t$('global.menu.account.register') }}</span>
|
||||
</b-dropdown-item>
|
||||
</b-nav-item-dropdown>
|
||||
</b-navbar-nav>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" src="./jhi-navbar.component.ts"></script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
/* ==========================================================================
|
||||
Navbar
|
||||
========================================================================== */
|
||||
.navbar-version {
|
||||
font-size: 0.65em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.navbar .navbar-nav .nav-item {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.jh-navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) and (max-width: 1150px) {
|
||||
span span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
display: inline-block;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Logo styles
|
||||
========================================================================== */
|
||||
.navbar-brand.logo {
|
||||
padding: 0 7px;
|
||||
}
|
||||
|
||||
.logo .logo-img {
|
||||
height: 45px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
height: 100%;
|
||||
background: url('/content/images/logo-jhipster.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
width: 100%;
|
||||
filter: drop-shadow(0 0 0.05rem white);
|
||||
margin: 0 5px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user