All checks were successful
Chore App Build and Push Docker Images / build-and-push (push) Successful in 1m23s
- Remove OverrideEditModal.spec.ts test file. - Update ParentPinSetup.vue to handle Enter key for code and PIN inputs. - Modify ChildEditView.vue to add maxlength for age input. - Enhance ChildView.vue with reward confirmation and cancellation dialogs. - Update ParentView.vue to handle pending rewards and confirm edits. - Revise PendingRewardDialog.vue to accept a dynamic message prop. - Expand ChildView.spec.ts to cover reward dialog interactions. - Add tests for ParentView.vue to validate pending reward handling. - Update UserProfile.vue to simplify button styles. - Adjust RewardView.vue to improve delete confirmation handling. - Modify ChildrenListView.vue to clarify child creation instructions. - Refactor EntityEditForm.vue to improve input handling and focus management. - Enhance ItemList.vue to support item selection. - Update LoginButton.vue to focus PIN input on error. - Change ScrollingList.vue empty state color for better visibility. - Remove capture attribute from ImagePicker.vue file input. - Update router/index.ts to redirect logged-in users from auth routes. - Add authGuard.spec.ts to test router authentication logic.
154 lines
5.0 KiB
TypeScript
154 lines
5.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
|
|
// Use plain objects — the guard only reads `.value`, so full Vue refs are unnecessary
|
|
const { isAuthReadyMock, isUserLoggedInMock, isParentAuthenticatedMock } = vi.hoisted(() => ({
|
|
isAuthReadyMock: { value: true },
|
|
isUserLoggedInMock: { value: false },
|
|
isParentAuthenticatedMock: { value: false },
|
|
}))
|
|
|
|
vi.mock('@/stores/auth', () => ({
|
|
isAuthReady: isAuthReadyMock,
|
|
isUserLoggedIn: isUserLoggedInMock,
|
|
isParentAuthenticated: isParentAuthenticatedMock,
|
|
}))
|
|
|
|
// Import router AFTER mocks are in place
|
|
const { default: router } = await import('../index')
|
|
|
|
// Helper — navigate and return the resolved path
|
|
async function navigate(path: string): Promise<string> {
|
|
await router.push(path)
|
|
return router.currentRoute.value.path
|
|
}
|
|
|
|
describe('router auth guard', () => {
|
|
beforeEach(async () => {
|
|
isAuthReadyMock.value = true
|
|
// Park at /auth/reset-password as a neutral starting point:
|
|
// - it is always reachable when logged out
|
|
// - it doesn't match any route a test assertion lands on
|
|
isUserLoggedInMock.value = false
|
|
isParentAuthenticatedMock.value = false
|
|
await router.push('/auth/reset-password')
|
|
})
|
|
|
|
// ── Redirect logged-in users away from /auth ──────────────────────────────
|
|
|
|
it('redirects logged-in parent user from /auth to /parent', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = true
|
|
|
|
const path = await navigate('/auth')
|
|
expect(path).toBe('/parent')
|
|
})
|
|
|
|
it('redirects logged-in child user from /auth to /child', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = false
|
|
|
|
const path = await navigate('/auth')
|
|
expect(path).toBe('/child')
|
|
})
|
|
|
|
it('redirects logged-in parent user from /auth/login to /parent', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = true
|
|
|
|
const path = await navigate('/auth/login')
|
|
expect(path).toBe('/parent')
|
|
})
|
|
|
|
it('redirects logged-in child user from /auth/signup to /child', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = false
|
|
|
|
const path = await navigate('/auth/signup')
|
|
expect(path).toBe('/child')
|
|
})
|
|
|
|
it('redirects logged-in child user from /auth/forgot-password to /child', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = false
|
|
|
|
const path = await navigate('/auth/forgot-password')
|
|
expect(path).toBe('/child')
|
|
})
|
|
|
|
// ── Unauthenticated users may access /auth ────────────────────────────────
|
|
|
|
it('allows unauthenticated user to access /auth', async () => {
|
|
isUserLoggedInMock.value = false
|
|
|
|
const path = await navigate('/auth')
|
|
expect(path).toBe('/auth')
|
|
})
|
|
|
|
it('allows unauthenticated user to access /auth/login', async () => {
|
|
isUserLoggedInMock.value = false
|
|
|
|
const path = await navigate('/auth/login')
|
|
expect(path).toBe('/auth/login')
|
|
})
|
|
|
|
// ── Unauthenticated users are redirected to /auth from protected routes ───
|
|
|
|
it('redirects unauthenticated user from /parent to /auth', async () => {
|
|
isUserLoggedInMock.value = false
|
|
|
|
const path = await navigate('/parent')
|
|
expect(path).toBe('/auth')
|
|
})
|
|
|
|
it('redirects unauthenticated user from /child to /auth', async () => {
|
|
isUserLoggedInMock.value = false
|
|
|
|
const path = await navigate('/child')
|
|
expect(path).toBe('/auth')
|
|
})
|
|
|
|
// ── Authenticated users are routed to the correct section ─────────────────
|
|
|
|
it('allows parent-authenticated user to access /parent', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = true
|
|
|
|
const path = await navigate('/parent')
|
|
expect(path).toBe('/parent')
|
|
})
|
|
|
|
it('allows child user to access /child', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = false
|
|
|
|
const path = await navigate('/child')
|
|
expect(path).toBe('/child')
|
|
})
|
|
|
|
it('redirects child user away from /parent to /child', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = false
|
|
|
|
const path = await navigate('/parent')
|
|
expect(path).toBe('/child')
|
|
})
|
|
|
|
it('redirects parent user away from /child to /parent', async () => {
|
|
isUserLoggedInMock.value = true
|
|
isParentAuthenticatedMock.value = true
|
|
|
|
const path = await navigate('/child')
|
|
expect(path).toBe('/parent')
|
|
})
|
|
|
|
// ── ParentPinSetup is always accessible ───────────────────────────────────
|
|
|
|
it('allows access to /parent/pin-setup regardless of auth state', async () => {
|
|
isUserLoggedInMock.value = false
|
|
|
|
const path = await navigate('/parent/pin-setup')
|
|
expect(path).toBe('/parent/pin-setup')
|
|
})
|
|
})
|