test apollo provider, apollo provider as plugin

This commit is contained in:
Moriz Wahl 2021-12-07 19:11:31 +01:00
parent 9942537472
commit ee248ec86a
4 changed files with 216 additions and 107 deletions

View File

@ -11,11 +11,8 @@ import addNavigationGuards from './router/guards'
import i18n from './i18n'
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo'
import CONFIG from './config'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
@ -23,37 +20,7 @@ import 'bootstrap-vue/dist/bootstrap-vue.css'
import moment from 'vue-moment'
import Toasted from 'vue-toasted'
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
const authLink = new ApolloLink((operation, forward) => {
const token = store.state.token
operation.setContext({
headers: {
Authorization: token && token.length > 0 ? `Bearer ${token}` : '',
},
})
return forward(operation).map((response) => {
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
response.errors[0].message = i18n.t('error.session-expired')
store.dispatch('logout', null)
if (router.currentRoute.path !== '/logout') router.push('/logout')
return response
}
const newToken = operation.getContext().response.headers.get('token')
if (newToken) store.commit('token', newToken)
return response
})
})
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
import { apolloProvider } from './plugins/apolloProvider'
Vue.use(BootstrapVue)

View File

@ -101,77 +101,4 @@ describe('main', () => {
}),
)
})
describe('ApolloLink', () => {
// mock store
const storeDispatchMock = jest.fn()
store.state = {
token: 'some-token',
}
store.dispatch = storeDispatchMock
// mock i18n.t
i18n.t = jest.fn((t) => t)
// mock apllo response
const responseMock = {
errors: [{ message: '403.13 - Client certificate revoked' }],
}
// mock router
const routerPushMock = jest.fn()
router.push = routerPushMock
router.currentRoute = {
path: '/overview',
}
// mock context
const setContextMock = jest.fn()
const getContextMock = jest.fn(() => {
return {
response: {
headers: {
get: jest.fn(),
},
},
}
})
// mock apollo link function params
const operationMock = {
setContext: setContextMock,
getContext: getContextMock,
}
const forwardMock = jest.fn(() => {
return [responseMock]
})
// get apollo link callback
const middleware = ApolloLink.mock.calls[0][0]
beforeEach(() => {
jest.clearAllMocks()
// run the callback with mocked params
middleware(operationMock, forwardMock)
})
it('sets authorization header', () => {
expect(setContextMock).toBeCalledWith({
headers: {
Authorization: 'Bearer some-token',
},
})
})
describe('apollo response is 403.13', () => {
it.skip('dispatches logout', () => {
expect(storeDispatchMock).toBeCalledWith('logout', null)
})
it.skip('redirects to logout', () => {
expect(routerPushMock).toBeCalledWith('/logout')
})
})
})
})

View File

@ -0,0 +1,37 @@
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo'
import CONFIG from '../config'
import store from '../store/store'
import router from '../router/router'
import i18n from '../i18n'
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
const authLink = new ApolloLink((operation, forward) => {
const token = store.state.token
operation.setContext({
headers: {
Authorization: token && token.length > 0 ? `Bearer ${token}` : '',
},
})
return forward(operation).map((response) => {
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
response.errors[0].message = i18n.t('error.session-expired')
store.dispatch('logout', null)
if (router.currentRoute.path !== '/login') router.push('/login')
return response
}
const newToken = operation.getContext().response.headers.get('token')
if (newToken) store.commit('token', newToken)
return response
})
})
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
export const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})

View File

@ -0,0 +1,178 @@
import { ApolloClient, ApolloLink, HttpLink } from 'apollo-boost'
import './apolloProvider'
import CONFIG from '../config'
import VueApollo from 'vue-apollo'
import store from '../store/store'
import router from '../router/router'
import i18n from '../i18n'
jest.mock('vue-apollo')
jest.mock('../store/store')
jest.mock('../router/router')
jest.mock('../i18n')
jest.mock('apollo-boost', () => {
return {
__esModule: true,
ApolloClient: jest.fn(),
ApolloLink: jest.fn(() => {
return { concat: jest.fn() }
}),
InMemoryCache: jest.fn(),
HttpLink: jest.fn(),
}
})
describe('apolloProvider', () => {
it('calls the HttpLink', () => {
expect(HttpLink).toBeCalledWith({ uri: CONFIG.GRAPHQL_URI })
})
it('calls the ApolloLink', () => {
expect(ApolloLink).toBeCalled()
})
it('calls the ApolloClient', () => {
expect(ApolloClient).toBeCalled()
})
it('calls the VueApollo', () => {
expect(VueApollo).toBeCalled()
})
describe('ApolloLink', () => {
// mock store
const storeDispatchMock = jest.fn()
const storeCommitMock = jest.fn()
store.state = {
token: 'some-token',
}
store.dispatch = storeDispatchMock
store.commit = storeCommitMock
// mock i18n.t
i18n.t = jest.fn((t) => t)
// mock apllo response
const responseMock = {
errors: [{ message: '403.13 - Client certificate revoked' }],
}
// mock router
const routerPushMock = jest.fn()
router.push = routerPushMock
router.currentRoute = {
path: '/overview',
}
// mock context
const setContextMock = jest.fn()
const getContextMock = jest.fn(() => {
return {
response: {
headers: {
get: jest.fn(() => 'another-token'),
},
},
}
})
// mock apollo link function params
const operationMock = {
setContext: setContextMock,
getContext: getContextMock,
}
const forwardMock = jest.fn(() => {
return [responseMock]
})
// get apollo link callback
const middleware = ApolloLink.mock.calls[0][0]
describe('with token in store', () => {
it('sets authorization header with token', () => {
// run the apollo link callback with mocked params
middleware(operationMock, forwardMock)
expect(setContextMock).toBeCalledWith({
headers: {
Authorization: 'Bearer some-token',
},
})
})
})
describe('without token in store', () => {
beforeEach(() => {
store.state.token = null
})
it('sets authorization header empty', () => {
middleware(operationMock, forwardMock)
expect(setContextMock).toBeCalledWith({
headers: {
Authorization: '',
},
})
})
})
describe('apollo response is 403.13', () => {
beforeEach(() => {
// run the apollo link callback with mocked params
middleware(operationMock, forwardMock)
})
it('dispatches logout', () => {
expect(storeDispatchMock).toBeCalledWith('logout', null)
})
describe('current route is not login', () => {
it('redirects to logout', () => {
expect(routerPushMock).toBeCalledWith('/login')
})
})
describe('current route is login', () => {
beforeEach(() => {
jest.clearAllMocks()
router.currentRoute.path = '/login'
})
it('does not redirect to login', () => {
expect(routerPushMock).not.toBeCalled()
})
})
})
describe('apollo response is with new token', () => {
beforeEach(() => {
delete responseMock.errors
middleware(operationMock, forwardMock)
})
it('commits new token to store', () => {
expect(storeCommitMock).toBeCalledWith('token', 'another-token')
})
})
describe('apollo response is without new token', () => {
beforeEach(() => {
jest.clearAllMocks()
getContextMock.mockReturnValue({
response: {
headers: {
get: jest.fn(() => null),
},
},
})
middleware(operationMock, forwardMock)
})
it('does not commit token to store', () => {
expect(storeCommitMock).not.toBeCalled()
})
})
})
})