From 847c7a55544a84eb0d86749dfda2528eb94ffcca Mon Sep 17 00:00:00 2001 From: Rijk van Zanten Date: Mon, 23 Mar 2020 14:37:31 -0400 Subject: [PATCH] Logout route (#238) * Add logout route * Don't render spinner on top of content * Use logout route instead of onBeforeEnterLogout handler * Remove onBeforeLogoutRouteEnter test * Fix warnings in tests --- src/app.vue | 4 +-- .../components/navigation/navigation.test.ts | 20 ++++++-------- src/router.test.ts | 19 +------------ src/router.ts | 11 +++----- src/routes/logout/index.ts | 4 +++ src/routes/logout/logout.test.ts | 23 ++++++++++++++++ src/routes/logout/logout.vue | 27 +++++++++++++++++++ src/routes/logout/readme.md | 7 +++++ src/stores/user/user.test.ts | 10 ++++--- .../components/header-bar/header-bar.test.ts | 2 ++ src/views/private/private-view.test.ts | 2 ++ 11 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 src/routes/logout/index.ts create mode 100644 src/routes/logout/logout.test.ts create mode 100644 src/routes/logout/logout.vue create mode 100644 src/routes/logout/readme.md diff --git a/src/app.vue b/src/app.vue index 496db0ced4..e7695f0897 100644 --- a/src/app.vue +++ b/src/app.vue @@ -1,11 +1,11 @@ diff --git a/src/modules/collections/components/navigation/navigation.test.ts b/src/modules/collections/components/navigation/navigation.test.ts index 49b23448bc..168b517c48 100644 --- a/src/modules/collections/components/navigation/navigation.test.ts +++ b/src/modules/collections/components/navigation/navigation.test.ts @@ -1,7 +1,7 @@ import CollectionsNavigation from './navigation.vue'; import VueCompositionAPI from '@vue/composition-api'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import useNavigation from '../../compositions/use-navigation'; +import * as useNavigation from '../../compositions/use-navigation'; import VList, { VListItem, VListItemContent, @@ -10,8 +10,6 @@ import VList, { } from '@/components/v-list'; import VIcon from '@/components/v-icon'; -jest.mock('../../compositions/use-navigation'); - const localVue = createLocalVue(); localVue.use(VueCompositionAPI); localVue.component('v-list', VList); @@ -22,16 +20,14 @@ localVue.component('v-list-item-icon', VListItemIcon); localVue.component('v-icon', VIcon); describe('Modules / Collections / Components / CollectionsNavigation', () => { - beforeEach(() => { - (useNavigation as jest.Mock).mockImplementation(() => ({ - navItems: { - value: [] - } - })); - }); - it('Uses useNavigation to get navigation links', () => { + jest.spyOn(useNavigation, 'default').mockImplementation( + () => + ({ + navItems: [] + } as any) + ); shallowMount(CollectionsNavigation, { localVue }); - expect(useNavigation).toHaveBeenCalled(); + expect(useNavigation.default).toHaveBeenCalled(); }); }); diff --git a/src/router.test.ts b/src/router.test.ts index 145386b5bc..cf68ad7158 100644 --- a/src/router.test.ts +++ b/src/router.test.ts @@ -1,13 +1,7 @@ import Vue from 'vue'; import VueCompositionAPI from '@vue/composition-api'; import { Route } from 'vue-router'; -import { - onBeforeEach, - onBeforeEnterProjectChooser, - replaceRoutes, - defaultRoutes, - onBeforeEnterLogout -} from './router'; +import { onBeforeEach, onBeforeEnterProjectChooser, replaceRoutes, defaultRoutes } from './router'; import api from '@/api'; import * as auth from '@/auth'; import { useProjectsStore } from '@/stores/projects'; @@ -256,17 +250,6 @@ describe('Router', () => { }); }); - describe('onBeforeEnterLogout', () => { - it('Calls logout and redirects to login page', async () => { - const to = { ...route, path: '/my-project/logout', params: { project: 'my-project' } }; - const from = route; - const next = jest.fn(); - await onBeforeEnterLogout(to, from, next); - expect(auth.logout).toHaveBeenCalled(); - expect(next).toHaveBeenCalledWith('/my-project/login'); - }); - }); - describe('replaceRoutes', () => { it('Calls the handler with the default routes', async () => { const handler = jest.fn(() => []); diff --git a/src/router.ts b/src/router.ts index 826c2c65e4..37debedfb5 100644 --- a/src/router.ts +++ b/src/router.ts @@ -2,8 +2,9 @@ import VueRouter, { NavigationGuard, RouteConfig } from 'vue-router'; import Debug from '@/routes/debug.vue'; import { useProjectsStore } from '@/stores/projects'; import LoginRoute from '@/routes/login'; +import LogoutRoute from '@/routes/logout'; import ProjectChooserRoute from '@/routes/project-chooser'; -import { checkAuth, logout } from '@/auth'; +import { checkAuth } from '@/auth'; import { hydrate, dehydrate } from '@/hydrate'; import useAppStore from '@/stores/app'; @@ -13,12 +14,6 @@ export const onBeforeEnterProjectChooser: NavigationGuard = (to, from, next) => next(); }; -export const onBeforeEnterLogout: NavigationGuard = async (to, from, next) => { - const currentProjectKey = to.params.project; - await logout({ navigate: false }); - next(`/${currentProjectKey}/login`); -}; - export const defaultRoutes: RouteConfig[] = [ { name: 'project-chooser', @@ -52,7 +47,7 @@ export const defaultRoutes: RouteConfig[] = [ { name: 'logout', path: '/:project/logout', - beforeEnter: onBeforeEnterLogout + component: LogoutRoute }, /** * @NOTE diff --git a/src/routes/logout/index.ts b/src/routes/logout/index.ts new file mode 100644 index 0000000000..7623c9914c --- /dev/null +++ b/src/routes/logout/index.ts @@ -0,0 +1,4 @@ +import LogoutRoute from './logout.vue'; + +export { LogoutRoute }; +export default LogoutRoute; diff --git a/src/routes/logout/logout.test.ts b/src/routes/logout/logout.test.ts new file mode 100644 index 0000000000..b8341ef274 --- /dev/null +++ b/src/routes/logout/logout.test.ts @@ -0,0 +1,23 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import LogoutRoute from './logout.vue'; +import { logout } from '@/auth'; +import VCircularProgress from '@/components/v-progress/circular/'; +import VueCompositionAPI from '@vue/composition-api'; + +jest.mock('@/auth'); + +const localVue = createLocalVue(); +localVue.component('v-progress-circular', VCircularProgress); +localVue.use(VueCompositionAPI); + +describe('Routes / Logout', () => { + it('Calls logout on mount', async () => { + const component = shallowMount(LogoutRoute, { + localVue + }); + + await component.vm.$nextTick(); + + expect(logout).toHaveBeenCalled(); + }); +}); diff --git a/src/routes/logout/logout.vue b/src/routes/logout/logout.vue new file mode 100644 index 0000000000..829c0a0194 --- /dev/null +++ b/src/routes/logout/logout.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/routes/logout/readme.md b/src/routes/logout/readme.md new file mode 100644 index 0000000000..469de464b6 --- /dev/null +++ b/src/routes/logout/readme.md @@ -0,0 +1,7 @@ +# Logout View + +Renders an empty page with a spinner, and logs the user out. Once the user is logged out, it will redirect to login. + +## Why a separate route? + +If we were to logout on any other route that uses the private view, it would mean that there will be a bunch of errors in the devtools, as a lot of things will be trying to read from the store, which will be cleared. This extra route makes sure we can safely empty the store without any component still reading from it. diff --git a/src/stores/user/user.test.ts b/src/stores/user/user.test.ts index a476de5b40..4be701dc4f 100644 --- a/src/stores/user/user.test.ts +++ b/src/stores/user/user.test.ts @@ -23,7 +23,7 @@ describe('Stores / User', () => { }); describe('Hydrate', () => { - it('Calls the right endpoint', () => { + it('Calls the right endpoint', async () => { (api.get as jest.Mock).mockImplementation(() => Promise.resolve({ data: { @@ -36,8 +36,12 @@ describe('Stores / User', () => { projectsStore.state.currentProjectKey = 'my-project'; const userStore = useUserStore(req); - userStore.hydrate().then(() => { - expect(api.get).toHaveBeenCalledWith('/my-project/users/me'); + userStore.hydrate(); + + expect(api.get).toHaveBeenCalledWith('/my-project/users/me', { + params: { + fields: '*,avatar.data' + } }); }); }); diff --git a/src/views/private/components/header-bar/header-bar.test.ts b/src/views/private/components/header-bar/header-bar.test.ts index 75f9fdab8b..120c4cf1e1 100644 --- a/src/views/private/components/header-bar/header-bar.test.ts +++ b/src/views/private/components/header-bar/header-bar.test.ts @@ -1,12 +1,14 @@ import { mount, createLocalVue } from '@vue/test-utils'; import VueCompositionAPI from '@vue/composition-api'; import HeaderBar from './header-bar.vue'; +import PortalVue from 'portal-vue'; import VButton from '@/components/v-button'; import VIcon from '@/components/v-icon'; const localVue = createLocalVue(); localVue.use(VueCompositionAPI); +localVue.use(PortalVue); localVue.component('v-button', VButton); localVue.component('v-icon', VIcon); diff --git a/src/views/private/private-view.test.ts b/src/views/private/private-view.test.ts index 032c6abe29..45fea782b7 100644 --- a/src/views/private/private-view.test.ts +++ b/src/views/private/private-view.test.ts @@ -3,9 +3,11 @@ import VueCompositionAPI from '@vue/composition-api'; import PrivateView from './private-view.vue'; import VOverlay from '@/components/v-overlay'; import VProgressCircular from '@/components/v-progress/circular'; +import PortalVue from 'portal-vue'; const localVue = createLocalVue(); localVue.use(VueCompositionAPI); +localVue.use(PortalVue); localVue.component('v-overlay', VOverlay); localVue.component('v-progress-circular', VProgressCircular);