Skip to content

Commit

Permalink
feat(logout): add logout button and logic with error handling
Browse files Browse the repository at this point in the history
- Added a logout button in the header.
- added `useLogoutService` to call the `/logout` endpoint.
- Added error handling with `useErrorToast` to display error messages on logout failure.
- Redirects user to the home page on successful logout.
- adds unit tests

RISDEV-5806
  • Loading branch information
hamo225 committed Jan 7, 2025
1 parent 61cde71 commit 34edf8b
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 3 deletions.
48 changes: 46 additions & 2 deletions frontend/src/components/controls/RisNavbar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
import { render, screen } from "@testing-library/vue"
import { describe, expect, it } from "vitest"
import { render, screen, fireEvent } from "@testing-library/vue"
import { describe, expect, it, vi } from "vitest"
import RisNavbar from "./RisNavbar.vue"
import { createRouter, createWebHashHistory } from "vue-router"

const add = vi.fn()
vi.mock("primevue/usetoast", () => {
return {
useToast: () => ({
add: add,
}),
}
})

const pushMock = vi.fn()
vi.mock("vue-router", async () => {
const actual = await vi.importActual("vue-router")
return {
...actual,
useRouter: () => ({
push: pushMock,
}),
}
})

describe("risNavbar", () => {
it("should show 'Rechtsinformationen' and 'des Bundes'", () => {
const router = createRouter({
Expand All @@ -14,4 +34,28 @@ describe("risNavbar", () => {
expect(screen.getByText("Rechtsinformationen")).toBeInTheDocument()
expect(screen.getByText("des Bundes")).toBeInTheDocument()
})

it("should render the logout button", () => {
const router = createRouter({
history: createWebHashHistory(),
routes: [{ name: "Home", path: "/", component: () => {} }],
})

render(RisNavbar, { global: { plugins: [router] } })
expect(screen.getByRole("button", { name: "Logout" })).toBeInTheDocument()
})

it("should redirect to Home on logout button click", async () => {
const router = createRouter({
history: createWebHashHistory(),
routes: [{ name: "Home", path: "/", component: () => {} }],
})

render(RisNavbar, { global: { plugins: [router] } })

const logoutButton = screen.getByRole("button", { name: "Logout" })
await fireEvent.click(logoutButton)

expect(pushMock).toHaveBeenCalledWith({ name: "Home" })
})
})
19 changes: 18 additions & 1 deletion frontend/src/components/controls/RisNavbar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
<script setup lang="ts">
import neurisLogo from "@/assets/neuRIS-logo.svg"
import { RouterLink } from "vue-router"
import { RouterLink, useRouter } from "vue-router"
import Button from "primevue/button"
import { useLogoutService } from "@/services/logoutService"
import { useErrorToast } from "@/lib/errorToast"
const { addErrorToast } = useErrorToast()
const router = useRouter()
async function handleLogout() {
const { error } = useLogoutService()
if (error?.value) {
addErrorToast(error.value)
} else {
await router.push({ name: "Home" })
}
}
</script>

<template>
Expand All @@ -14,5 +30,6 @@ import { RouterLink } from "vue-router"
<span class="ris-label3-regular block">des Bundes</span>
</span>
</RouterLink>
<Button label="Logout" severity="primary" @click="handleLogout"></Button>
</nav>
</template>
31 changes: 31 additions & 0 deletions frontend/src/services/logoutService.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, it, expect, vi, beforeEach } from "vitest"

describe("logoutService", () => {
beforeEach(() => {
vi.resetModules()
vi.resetAllMocks()
})

it("calls the /logout endpoint with POST method", async () => {
const useFetchMock = {
error: { value: null },
isFetching: { value: false },
}

vi.doMock("@/services/apiService", () => ({
useApiFetch: vi.fn().mockReturnValue(useFetchMock),
}))

const { useLogoutService } = await import("@/services/logoutService")

const result = useLogoutService()

expect(result.error.value).toBe(null)
expect(result.isFetching.value).toBe(false)

const { useApiFetch } = await import("@/services/apiService")
expect(useApiFetch).toHaveBeenCalledWith("/logout", {
method: "POST",
})
})
})
13 changes: 13 additions & 0 deletions frontend/src/services/logoutService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { UseFetchReturn } from "@vueuse/core"
import { useApiFetch } from "@/services/apiService"

/**
* Service to log out the user.
*
* Calls the backend `/logout` endpoint.
*/
export function useLogoutService(): UseFetchReturn<void> {
return useApiFetch("/logout", {
method: "POST",
})
}

0 comments on commit 34edf8b

Please sign in to comment.