Refactor and enhance various components and tests
All checks were successful
Chore App Build and Push Docker Images / build-and-push (push) Successful in 1m23s
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.
This commit is contained in:
153
frontend/vue-app/src/router/__tests__/authGuard.spec.ts
Normal file
153
frontend/vue-app/src/router/__tests__/authGuard.spec.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
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')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user