From 8701a5044848af42d5e4e87537ecf16074488a7e Mon Sep 17 00:00:00 2001
From: Gordon Nicholas <160246213+GordonNicholasCap@users.noreply.github.com>
Date: Tue, 12 Nov 2024 09:59:53 +0100
Subject: [PATCH] N21-2136-moin-schule-logout-in-svs (#3444)
* N21-2166 external logout buttons
---
src/modules/ui/layout/topbar/UserMenu.unit.ts | 169 +++++++++++++++++-
src/modules/ui/layout/topbar/UserMenu.vue | 34 +++-
src/serverApi/v3/api.ts | 116 ++++++++++++
src/store/auth.ts | 12 ++
src/store/auth.unit.ts | 52 +++++-
src/store/env-config-defaults.ts | 1 +
6 files changed, 373 insertions(+), 11 deletions(-)
diff --git a/src/modules/ui/layout/topbar/UserMenu.unit.ts b/src/modules/ui/layout/topbar/UserMenu.unit.ts
index 25a93766ad..45c5fe1051 100644
--- a/src/modules/ui/layout/topbar/UserMenu.unit.ts
+++ b/src/modules/ui/layout/topbar/UserMenu.unit.ts
@@ -1,23 +1,38 @@
import { mount } from "@vue/test-utils";
-import UserMenu from "./UserMenu.vue";
import {
createTestingI18n,
createTestingVuetify,
} from "@@/tests/test-utils/setup";
import { createModuleMocks } from "@@/tests/test-utils/mock-store-module";
+import { envsFactory } from "@@/tests/test-utils";
import { AUTH_MODULE_KEY, ENV_CONFIG_MODULE_KEY } from "@/utils/inject";
import AuthModule from "@/store/auth";
import EnvConfigModule from "@/store/env-config";
-import { LanguageType } from "@/serverApi/v3";
+import { LanguageType, MeSystemResponse } from "@/serverApi/v3";
+import { VBtn } from "vuetify/lib/components/index.mjs";
+import UserMenu from "./UserMenu.vue";
+
+jest.mock("@data-system");
describe("@ui-layout/UserMenu", () => {
- const setup = () => {
+ const setupWrapper = (
+ isExternalFeatureEnabled = false,
+ mockedSystem?: MeSystemResponse
+ ) => {
const authModule = createModuleMocks(AuthModule, {
getLocale: "de",
logout: jest.fn(),
+ externalLogout: jest.fn(),
+ get loginSystem(): MeSystemResponse | undefined {
+ return mockedSystem;
+ },
});
+
const envConfigModule = createModuleMocks(EnvConfigModule, {
getAvailableLanguages: [LanguageType.De, LanguageType.En],
+ getEnv: envsFactory.build({
+ FEATURE_EXTERNAL_SYSTEM_LOGOUT_ENABLED: isExternalFeatureEnabled,
+ }),
});
const wrapper = mount(UserMenu, {
@@ -41,15 +56,19 @@ describe("@ui-layout/UserMenu", () => {
return { wrapper, authModule };
};
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
it("should render with correct user initials", async () => {
- const { wrapper } = setup();
+ const { wrapper } = setupWrapper();
const initials = wrapper.findComponent("[data-testid=user-menu-btn]");
expect(initials.text()).toMatch("AD");
});
it("should render correct active user name with role", async () => {
- const { wrapper } = setup();
+ const { wrapper } = setupWrapper();
const menuBtn = wrapper.findComponent({ name: "VBtn" });
await menuBtn.trigger("click");
@@ -61,7 +80,7 @@ describe("@ui-layout/UserMenu", () => {
});
it("should trigger logout function on logout item click", async () => {
- const { wrapper, authModule } = setup();
+ const { wrapper, authModule } = setupWrapper();
const menuBtn = wrapper.findComponent({ name: "VBtn" });
await menuBtn.trigger("click");
@@ -72,4 +91,142 @@ describe("@ui-layout/UserMenu", () => {
expect(authModule.logout).toHaveBeenCalled();
});
+
+ describe("external logout", () => {
+ describe("when feature flag is enabled and end session endpoint is available for the system", () => {
+ const setup = () => {
+ const mockedSystem: MeSystemResponse = {
+ id: "testId",
+ name: "Test System",
+ hasEndSessionEndpoint: true,
+ };
+
+ const { wrapper, authModule } = setupWrapper(true, mockedSystem);
+
+ return { wrapper, authModule, mockedSystem };
+ };
+
+ it("should show the external logout button", async () => {
+ const { wrapper, mockedSystem } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const externalLogoutBtn = wrapper.findComponent(
+ "[data-testid=external-logout]"
+ );
+
+ expect(externalLogoutBtn.exists()).toBe(true);
+ expect(externalLogoutBtn.text()).toEqual(
+ `common.labels.logout Bildungscloud & ${mockedSystem.name}`
+ );
+ });
+
+ it("should trigger external logout function on logout item click", async () => {
+ const { wrapper, authModule } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const externalLogoutBtn = wrapper.findComponent(
+ "[data-testid=external-logout]"
+ );
+
+ expect(externalLogoutBtn.exists()).toBe(true);
+ await externalLogoutBtn.trigger("click");
+
+ expect(authModule.externalLogout).toHaveBeenCalled();
+ });
+
+ it("should show the correct text for the logout button", async () => {
+ const { wrapper } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const logoutBtn = wrapper.findComponent("[data-testid=logout]");
+
+ expect(logoutBtn.exists()).toBe(true);
+ expect(logoutBtn.text()).toEqual(`common.labels.logout Bildungscloud`);
+ });
+ });
+
+ describe("when feature flag is disabled", () => {
+ const setup = () => {
+ const mockedSystem: MeSystemResponse = {
+ id: "testId",
+ name: "Test System",
+ hasEndSessionEndpoint: true,
+ };
+
+ const { wrapper } = setupWrapper(false, mockedSystem);
+
+ return { wrapper };
+ };
+
+ it("should not show the external logout button", async () => {
+ const { wrapper } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const externalLogoutBtn = wrapper.findComponent(
+ "[data-testid=external-logout]"
+ );
+
+ expect(externalLogoutBtn.exists()).toBe(false);
+ });
+
+ it("should show the correct text for the logout button", async () => {
+ const { wrapper } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const logoutBtn = wrapper.findComponent("[data-testid=logout]");
+
+ expect(logoutBtn.exists()).toBe(true);
+ expect(logoutBtn.text()).toEqual("common.labels.logout");
+ });
+ });
+
+ describe("when end session endpoint is not available for the systeme", () => {
+ const setup = () => {
+ const mockedSystem: MeSystemResponse = {
+ id: "testId",
+ name: "Test System",
+ hasEndSessionEndpoint: false,
+ };
+
+ const { wrapper } = setupWrapper(true, mockedSystem);
+
+ return { wrapper };
+ };
+
+ it("should not show the external logout button", async () => {
+ const { wrapper } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const externalLogoutBtn = wrapper.findComponent(
+ "[data-testid=external-logout]"
+ );
+
+ expect(externalLogoutBtn.exists()).toBe(false);
+ });
+
+ it("should show the correct text for the logout button", async () => {
+ const { wrapper } = setup();
+
+ const menuBtn = wrapper.findComponent(VBtn);
+ await menuBtn.trigger("click");
+
+ const logoutBtn = wrapper.findComponent("[data-testid=logout]");
+
+ expect(logoutBtn.exists()).toBe(true);
+ expect(logoutBtn.text()).toEqual("common.labels.logout");
+ });
+ });
+ });
});
diff --git a/src/modules/ui/layout/topbar/UserMenu.vue b/src/modules/ui/layout/topbar/UserMenu.vue
index 40a7cf39e0..ec99654fd2 100644
--- a/src/modules/ui/layout/topbar/UserMenu.vue
+++ b/src/modules/ui/layout/topbar/UserMenu.vue
@@ -1,5 +1,5 @@
-
+
{{ $t("global.topbar.settings") }}
+
+ {{ $t("common.labels.logout")
+ }}{{ isExternalLogoutAllowed ? ` Bildungscloud & ${systemName}` : "" }}
+
- {{ $t("common.labels.logout") }}
+ {{ $t("common.labels.logout")
+ }}{{ isExternalLogoutAllowed ? " Bildungscloud" : "" }}