I'm create user login page in vue js and consuming data from django with axios. I have utilized jwt to create token session in client-side
The problem is the session is not saved when the page is refreshed. I have frustated because it. This is my source code :
In '../src/store/modules/auth.js'
import Vue from 'vue'
import Axios from 'axios'
import 'es6-promise/auto'
// In order that Axios work nice with Django CSRF
Axios.defaults.xsrfCookieName = 'csrftoken'
Axios.defaults.xsrfHeaderName = 'X-CSRFToken'
const state = {
authUser: {},
users: [],
isAuthenticated: false,
jwt: localStorage.getItem('token'),
endpoints: {
obtainJWT: 'http://127.0.0.1:8000/api/auth/obtain_token/',
refreshJWT: 'http://127.0.0.1:8000/api/auth/refresh_token/',
baseUrl: 'http://127.0.0.1:8000/api/auth/',
register: 'http://127.0.0.1:8000/signup/'
}
}
const mutations = {
setAuthUser: (state, {
authUser,
isAuthenticated
}) => {
Vue.set(state, 'authUser', authUser)
Vue.set(state, 'isAuthenticated', isAuthenticated)
},
updateToken: (state, newToken) => {
localStorage.setItem('token', newToken);
state.jwt = newToken;
},
removeToken: (state) => {
localStorage.removeItem('token');
state.jwt = null;
},
}
const actions = {
refreshToken(){
const payload = {
token: this.state.jwt
}
Axios.post(state.endpoints.refreshJWT, payload)
.then((response)=>{
this.commit('updateToken', response.data.token)
})
.catch((error)=>{
console.log(error)
})
}
}
export default {
state,
mutations,
actions,
}
In '../src/store/index.js'
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import auth from './modules/auth'
Vue.use(Vuex)
// Make Axios play nice with Django CSRF
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
export default new Vuex.Store({
modules: {
auth
},
})
In '../src/components/login.vue'
<template>
<div class="login">
<form>
<label for="username">Username</label>
<input
type="text"
name="username"
v-model="username"
/><br>
<label for="password">Password</label>
<input
type="password"
name="password"
v-model="password"
/><br>
<input
type="button"
#click="login()"
value="Login"
/>
</form>
</template>
<script>
import axios from 'axios'
/* eslint-disable */
export default {
name: 'Login',
data(){
return {
username: '',
password: ''
}
},
methods: {
login(){
const payload = {
username: this.username,
password: this.password
}
axios.post(this.$store.state.auth.endpoints.obtainJWT, payload)
.then((response) => {
this.$store.commit('updateToken', response.data.token)
this.$store.commit('setAuthUser',
{
authUser: response.data,
isAuthenticated: true
}
)
this.$router.push({path: 'dashboard-user/id/list-vendor'})
})
.catch((error) => {
//NOTE: erase this when production
console.log(error);
console.debug(error);
console.dir(error);
alert("The username or password is incorrect");
})
}
}
}
</script>
In 'main.js'
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import 'tachyons'
import routes from './routes'
import './styles.css'
import store from '#/store'
Vue.config.productionTip = false
Vue.use(VueRouter)
import '#/assets/fonts/all.css';
const router = new VueRouter({
mode: 'history',
routes
})
router.beforeEach((to, from, next) => {
// to and from are both route objects. must call `next`.
if(to.fullPath === '/dashboard-user/id/list-vendor') {
if(!store.state.jwt) {
next('/login')
}
}
if(to.fullPath === '/login') {
if(store.state.jwt) {
next('/dashboard-user/id/list-vendor')
}
}
next();
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Since use register auth as a module, you should use store.state.auth.jwt instead of store.state.jwt
router.beforeEach((to, from, next) => {
// to and from are both route objects. must call `next`.
if(to.fullPath === '/dashboard-user/id/list-vendor') {
if(!store.state.auth.jwt) {
next('/login')
}
}
if(to.fullPath === '/login') {
if(store.state.auth.jwt) {
next('/dashboard-user/id/list-vendor')
}
}
next();
})
Related
I was trying to develop a login page with the Django rest framework as the backend. The backend is working perfectly whereas I can't even set up react js. I am getting an error in the Index.js file of react. It tells "Invalid hook call. Hooks can only be called inside of the body of a function component"
This is what the error I get
App.js
import React from 'react';
import './App.css';
import Paperbase from './Layout/Paperbase'
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Login from './Layout/Login/Login'
import Register from './Layout/Register/Register'
export function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/dashboard" render={() => <Paperbase /> } />
<Route path="/account/login" render={() =><Login />} />
<Route path="/account/register" render={() => <Register />} />
</Switch>
</BrowserRouter>
)
}
export default App
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import store from './store';
import { Provider } from 'react-redux';
import { render } from 'react-dom';
ReactDOM.render(
(<Provider store={store}>
<App/>
</Provider>),
document.getElementById('root') || document.createElement('div') // for testing purposes
);
serviceWorker.unregister();
Login.js
import React from 'react';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import Grid from '#material-ui/core/Grid';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import Container from '#material-ui/core/Container';
import { withStyles } from '#material-ui/core/styles';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { login } from '../../actions/auth';
const styles = theme => ({
'#global': {
body: {
backgroundColor: theme.palette.common.white,
},
},
paper: {
marginTop: theme.spacing(25),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.primary.light,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
backgroundColor: theme.palette.primary.light,
},
});
class SignIn extends React.Component {
state = {
email: '',
password: '',
};
static propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool,
};
onSubmit = (e) => {
e.preventDefault();
this.props.login(this.state.email, this.state.password);
};
onChange = (e) => this.setState({ [e.target.name]: e.target.value });
render() {
const { classes } = this.props;
const { email, password } = this.state;
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} onSubmit={this.onSubmit}>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
onChange={this.onChange}
value={email}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={this.onChange}
value={password}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
</Grid>
</form>
</div>
</Container>
);
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated,
});
export default (withStyles(styles)(SignIn));
authreducer.js
import {
USER_LOADED,
USER_LOADING,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
REGISTER_FAIL,
} from '../actions/types';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: false,
user: null,
};
export default function (state = initialState, action) {
switch (action.type) {
case USER_LOADING:
return {
...state,
isLoading: true,
};
case USER_LOADED:
return {
...state,
isAuthenticated: true,
isLoading: false,
user: action.payload,
};
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
isLoading: false,
};
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT_SUCCESS:
case REGISTER_FAIL:
localStorage.removeItem('token');
return {
...state,
token: null,
user: null,
isAuthenticated: false,
isLoading: false,
};
default:
return state;
}
}
authactions.js
import axios from 'axios';
import {
USER_LOADED,
USER_LOADING,
LOGIN_SUCCESS,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
} from './types';
// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
// User Loading
dispatch({ type: USER_LOADING });
axios
.get('http://localhost:8000/api/auth/user', tokenConfig(getState))
.then((res) => {
dispatch({
type: USER_LOADED,
payload: res.data,
});
})
.catch((err) => {
console.log(err)
});
};
// LOGIN USER
export const login = (email, password) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({ email, password });
axios
.post('http://localhost:8000/api/auth/login', body, config)
.then((res) => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// REGISTER USER
export const register = ({ username, password, email }) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// Request Body
const body = JSON.stringify({ username, email, password });
axios
.post('http://localhost:8000/api/auth/register', body, config)
.then((res) => {
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// LOGOUT USER
export const logout = () => (dispatch, getState) => {
axios
.post('http://localhost:8000/api/auth/logout/', null, tokenConfig(getState))
.then((res) => {
dispatch({ type: 'CLEAR_LEADS' });
dispatch({
type: LOGOUT_SUCCESS,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// Setup config with token - helper function
export const tokenConfig = (getState) => {
// Get token from state
const token = getState().auth.token;
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// If token, add to headers config
if (token) {
config.headers['Authorization'] = `Token ${token}`;
}
return config;
};
Based on my experience, this error often occurs due to the fact that one of the packages in package.json (often it's React) is in dependencies (not in devDependencies). And this version conflicts with yours.
I'm trying to use apollo (+ vue, django) but for some reason it won't get loaded/used in a component,this.$apollo is always undefined.
<script>
import { GET_ALL_USERS_QUERY } from '../js/graphql/queries/userQueries.js'
export default {
name: 'GraphQLTest',
data() {
return {
users: [],
loading: true
}
},
async mounted() {
this.loading = true
this.users = await this.$apollo.query({
query: GET_ALL_USERS_QUERY
})
this.loading = false
}
}
</script>
[Vue warn]: Error in mounted hook (Promise/async): "TypeError: Cannot read property 'query' of undefined"
main.js
import Vue from 'vue'
import { router } from './routes.js'
import store from './store.js'
import { createProvider } from './apollo.js'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
router,
store,
provide: createProvider(),
render: h => h(App),
}).$mount('#app')
apollo.js
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
// Install the vue plugin
Vue.use(VueApollo)
// Name of the localStorage item
const AUTH_TOKEN = 'jwt-token'
// Config
const defaultOptions = {
httpEndpoint: '/graphql',
wsEndpoint: null,
tokenName: AUTH_TOKEN,
persisting: false,
websocketsOnly: false,
ssr: false
}
// Call this in the Vue app file
export function createProvider (options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options
})
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
loadingKey: 'loading',
fetchPolicy: 'cache-and-network'
}
},
errorHandler (error) {
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
}
})
return apolloProvider
}
// Manually call this when user log in
export async function onLogin (apolloClient, token) {
localStorage.setItem(AUTH_TOKEN, token)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (login)', 'color: orange;', e.message)
}
}
// Manually call this when user log out
export async function onLogout (apolloClient) {
localStorage.removeItem(AUTH_TOKEN)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
}
}
Why is apollo never loaded? something missing in the config?
[edit: same thing happens when following the tutorial : ]
import Vue from "vue"
import App from "./App.vue"
import { router } from './routes.js'
import ApolloClient from "apollo-boost"
import VueApollo from "vue-apollo"
const apolloProvider = new VueApollo({
defaultClient: new ApolloClient({
uri: "http://localhost:8000/graphql/"
})
})
Vue.use(VueApollo)
new Vue({
router,
el: "#app",
provide: apolloProvider.provide(),
render: h => h(App)
})
if you created your VUE project using vue-cli > 3 this will cause the issue with this.$apollo equal to undefined.
Maybe I misunderstood what this package does, but I assumed that it would read cached responses and help with offline application functionality.
import React from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
export const DATA_QUERY = gql`
query Data {
me {
name
bestFriend {
name
}
}
}
`
const options = () => ({
fetchPolicy: 'cache-only'
})
const withData = graphql(DATA_QUERY, { options })
export const Start = ({ data }) =>
data.loading ? (
'loading!'
) : data.me ? (
<div>
{console.log('data', data)}
<h3>Me: {data.me.name}</h3>
<p>Best friend: {data.me.bestFriend.name}</p>
</div>
) : (
'no data'
)
export default withData(Start)
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { ApolloProvider } from 'react-apollo'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { persistCache } from 'apollo-cache-persist'
const cache = new InMemoryCache()
persistCache({
cache,
storage: window.localStorage,
debug: true
})
export const client = new ApolloClient({
link: new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' }),
cache
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'));
registerServiceWorker();
I do have the cache in my localStorage
apollo-cache-persist: "{"$ROOT_QUERY.me":{"name":"Bob","bestFriend":{"type":"id","id`enter code here`":"$ROOT_QUERY.me.bestFriend","generated":true}"
When running the above example with fetchPolicy: 'cache-only' the component renders 'no data'. If I do the default fetchPolicy, cache-first, then I get the expected result but I can see the network request is being made.
EDIT: Now works with Daniels answer and this workaround waits for cache to be restored before running the query.
import Start from './Start'
class App extends Component {
state = {
show: false
}
toggle = () =>
this.setState({ show: !this.state.show })
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<br/><br/>
<button onClick={this.toggle}>Show it</button>
<br/><br/>
{this.state.show && <Start />}
</div>
);
}
}
In order to correctly cache and later retrieve the data from the cache, Apollo needs an id (or _id) to work with. If you want to use a different property as the id (like name), you can pass a dataIdFromObject function to your configuration for the in-memory cache:
const cache = new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
//User is whatever type "me" query resolves to
case 'User': return object.name;
default: return object.id || object._id;
}
}
});
Something like this works, though I wonder if there should be a more elegant solution. Maybe the Retry Link.
https://github.com/apollographql/apollo-cache-persist/issues?utf8=%E2%9C%93&q=is%3Aissue+
export class Index extends Component {
state = {
client: null
}
async componentWillMount() {
const httpLink = new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' })
const link = ApolloLink.from([ httpLink ])
const cache = new InMemoryCache({
dataIdFromObject: (object) => {
switch (object.__typename) {
// User is whatever type "me" query resolves to
case 'User':
return object.name
default:
return object.id || object._id
}
}
})
await persistCache({
cache,
storage: window.localStorage,
debug: true
})
const client = new ApolloClient({
link,
cache
})
this.setState({ client })
}
render() {
return !this.state.client ? (
null
) : (
<ApolloProvider client={this.state.client}>
<App />
</ApolloProvider>
)
}
}
ReactDOM.render(<Index />, document.getElementById('root'))
I am trying to test the following App.vue component when a click event is fired on the logout vue-router link...
App.vue
<template>
<div id="app">
<header id="header">
<nav>
<ul class="navigation">
<li id="home"><router-link :to="{ name: 'home' }">Home</router-link></li>
<li id="login" v-if="!isAuthenticated"><router-link :to="{ name: 'login' }">Login</router-link></li>
<li id="shoppinglists" v-if="isAuthenticated"><router-link :to="{ name: 'shoppinglists' }" >Shopping Lists</router-link></li>
<li id="logout" v-if="isAuthenticated">Logout</li>
</ul>
</nav>
</header><!-- /#header -->
<section id="page">
<router-view></router-view>
</section><!-- /#page -->
</div>
</template>
<script>
import store from '#/vuex/store'
import router from '#/router/index'
import { mapGetters } from 'vuex'
export default {
name: 'app',
computed: {
...mapGetters({ isAuthenticated: 'isAuthenticated' })
},
methods: {
logout () {
this. $store.dispatch('logout')
.then(() => {
window.localStorage.removeItem('vue-authenticate.vueauth_token')
this/$router.push({ name: 'home' })
})
}
},
store,
router
}
</script>
To test the logout click, I preset the isAuthenticated state to true, so the logout router link show up and I trigger the click event on it.
LOG: 'navigation: ', <ul class="navigation"><li id="home">
Home</li> <!---->
<li id="shoppinglists">Shopping Lists
</li> <li id="logout">Logout</li></ul>
I expect the action logout to have been called .. but it's not ... why ?
App.spec.js
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import App from '#/App'
import router from '#/router/index'
import { mount } from 'avoriaz'
import sinon from 'sinon'
Vue.use(Vuex)
Vue.use(VueRouter)
describe('App.vue', () => {
let actions
let getters
let store
beforeEach(() => {
getters = {
isAuthenticated: (state) => {
return state.isAuthenticated
}
}
actions = {
logout: sinon.stub().returns(Promise.resolve(true))
}
store = new Vuex.Store({
getters,
actions,
state: {
isAuthenticated: true,
currentUserId: ''
}
})
router
})
it('calls logout method', () => {
const wrapper = mount(App, { router, store })
console.log('navigation: ', wrapper.find('ul.navigation')[0].element)
const logoutLink = wrapper.find('#logout a')[0]
logoutLink.trigger('click')
expect(actions.logout.calledOnce).to.equal(true)
})
})
vuex/actions.js
import { IS_AUTHENTICATED, CURRENT_USER_ID } from './mutation_types'
import getters from './getters'
export default {
logout: ({commit}) => {
commit(IS_AUTHENTICATED, { isAuthenticated: false })
commit(CURRENT_USER_ID, { currentUserId: '' })
return true
}
}
vuex/mutations.js
import * as types from './mutation_types'
import getters from './getters'
export default {
[types.IS_AUTHENTICATED] (state, payload) {
state.isAuthenticated = payload.isAuthenticated
},
[types.CURRENT_USER_ID] (state, payload) {
state.currentUserId = payload.currentUserId
}
}
vuex/getters.js
export default {
isAuthenticated: (state) => {
return state.isAuthenticated
}
}
Finally , I found a way to test it :
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import App from '#/App'
import router from '#/router/index'
import { mount } from 'avoriaz'
import sinon from 'sinon'
Vue.use(Vuex)
Vue.use(VueRouter)
describe('App.vue', () => {
let actions
let getters
let store
let sandbox
let routerPush
beforeEach(() => {
sandbox = sinon.sandbox.create()
getters = {
isAuthenticated: (state) => {
return state.isAuthenticated
}
}
actions = {
logout: sandbox.stub().returns(Promise.resolve(true))
}
store = new Vuex.Store({
getters,
state: {
isAuthenticated: true,
currentUserId: ''
},
actions
})
router
})
afterEach(() => {
sandbox.restore()
})
it('calls logout method', (done) => {
const wrapper = mount(App, { store, router })
routerPush = sandbox.spy(wrapper.vm.$router, 'push')
const logoutLink = wrapper.find('#logout a')[0]
logoutLink.trigger('click')
wrapper.vm.$nextTick(() => {
expect(actions.logout.calledOnce).to.equal(true)
actions.logout().then(() => {
expect(routerPush).to.have.been.calledWith('/home')
})
done()
})
})
})
Is there any way to unit test the navigation guards in a router file ?
Could not find any post or link on this topic ... ant tips, trick or feedback welcome..
Here is the router/index.js , and I would like to test the router.beforeEach()
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '#/pages/HomePage'
import Login from '#/pages/LoginPage'
import ShoppingLists from '#/pages/ShoppingListsPage'
import vueAuthInstance from '../services/auth.js'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: { auth: false, title: 'Home' }
},
{
path: '/login',
name: 'login',
component: Login,
meta: { auth: false, title: 'Login' }
},
{
path: '/shoppinglists',
name: 'shoppinglists',
component: ShoppingLists,
meta: { auth: true, title: 'Shopping Lists' }
},
{
path: '/logout',
name: 'logout'
}
]
})
router.beforeEach(function (to, from, next) {
if (to.meta && to.meta.title) {
document.title = to.meta.title
}
if (to.meta && to.meta.auth !== undefined) {
if (to.meta.auth) {
if (vueAuthInstance.isAuthenticated()) {
next()
} else {
router.push({ name: 'login' })
}
} else {
next()
}
} else {
next()
}
})
export default router
I found a way to do it, importing the router and using simply router.push) to navigate. I also need to stub the vueAuthInstance to authenticate or not the request
import VueRouter from 'vue-router'
import Vue from 'vue'
import sinon from 'sinon'
import router from '#/router/index'
import vueAuthInstance from '#/services/auth.js'
Vue.use(VueRouter)
describe('Router', () => {
let sandbox
beforeEach(() => {
sandbox = sinon.sandbox.create()
router
})
afterEach(() => {
sandbox.restore()
})
it('should be in history mode', () => {
sandbox.stub(vueAuthInstance, 'isAuthenticated').returns(false)
expect(router.mode).to.eql('history')
})
it('should be able to navigate without authentication', () => {
sandbox.stub(vueAuthInstance, 'isAuthenticated').returns(false)
router.push('/')
expect(router.history.current.path).to.eql('/')
expect(router.getMatchedComponents('/')[0].name).to.eql('HomePage')
router.push('/login')
expect(router.history.current.path).to.eql('/login')
expect(router.getMatchedComponents('/login')[0].name).to.eql('LoginPage')
})
it('should not be able to navigate to protected page when not authenticated', () => {
sandbox.stub(vueAuthInstance, 'isAuthenticated').returns(false)
router.push('/shoppinglists')
expect(router.history.current.path).to.eql('/login')
expect(router.getMatchedComponents('/login')[0].name).to.eql('LoginPage')
})
it('should be able to navigate to protected page when authenticated', () => {
sandbox.stub(vueAuthInstance, 'isAuthenticated').returns(true)
router.push('/shoppinglists')
expect(router.history.current.path).to.eql('/shoppinglists')
expect(router.getMatchedComponents('/shoppinglists')[0].name).to.eql('ShoppingListPage')
})
it('should be able to navigate to unprotected page when authenticated', () => {
sandbox.stub(vueAuthInstance, 'isAuthenticated').returns(true)
router.push('/home')
expect(router.history.current.path).to.eql('/home')
expect(router.getMatchedComponents('/')[0].name).to.eql('HomePage')
})
})