Skip to content

Commit

Permalink
N21-2136-moin-schule-logout-in-svs (#3444)
Browse files Browse the repository at this point in the history
* N21-2166 external logout buttons
  • Loading branch information
GordonNicholasCap authored Nov 12, 2024
1 parent b802e57 commit 8701a50
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 11 deletions.
169 changes: 163 additions & 6 deletions src/modules/ui/layout/topbar/UserMenu.unit.ts
Original file line number Diff line number Diff line change
@@ -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, {
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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");
});
});
});
});
34 changes: 30 additions & 4 deletions src/modules/ui/layout/topbar/UserMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<VMenu width="215">
<VMenu :width="isExternalLogoutAllowed ? 'auto' : '215'">
<template v-slot:activator="{ props }">
<VBtn
v-bind="props"
Expand All @@ -21,19 +21,32 @@
<VListItem href="/account" data-testid="account-link">
{{ $t("global.topbar.settings") }}
</VListItem>
<VListItem
v-if="isExternalLogoutAllowed"
data-testid="external-logout"
@click="externalLogout"
>
{{ $t("common.labels.logout")
}}{{ isExternalLogoutAllowed ? ` Bildungscloud & ${systemName}` : "" }}
</VListItem>
<VListItem data-testid="logout" @click="logout">
{{ $t("common.labels.logout") }}
{{ $t("common.labels.logout")
}}{{ isExternalLogoutAllowed ? " Bildungscloud" : "" }}
</VListItem>
</VList>
</VMenu>
</template>

<script setup lang="ts">
import { computed, PropType, toRef } from "vue";
import { computed, ComputedRef, PropType, toRef } from "vue";
import { useI18n } from "vue-i18n";
import LanguageMenu from "./LanguageMenu.vue";
import { MeUserResponse } from "@/serverApi/v3";
import { injectStrict, AUTH_MODULE_KEY } from "@/utils/inject";
import {
injectStrict,
AUTH_MODULE_KEY,
ENV_CONFIG_MODULE_KEY,
} from "@/utils/inject";
const props = defineProps({
user: {
Expand All @@ -48,6 +61,7 @@ const props = defineProps({
const { t } = useI18n();
const authModule = injectStrict(AUTH_MODULE_KEY);
const envConfigModule = injectStrict(ENV_CONFIG_MODULE_KEY);
const userRole = computed(() => {
return t(`common.roleName.${toRef(props.roleNames).value[0]}`).toString();
Expand All @@ -57,9 +71,21 @@ const initials = computed(() => {
return props.user.firstName.slice(0, 1) + props.user.lastName.slice(0, 1);
});
const isExternalLogoutAllowed: ComputedRef<boolean> = computed(
() =>
envConfigModule.getEnv.FEATURE_EXTERNAL_SYSTEM_LOGOUT_ENABLED &&
!!authModule.loginSystem?.hasEndSessionEndpoint
);
const systemName: string = authModule.loginSystem?.name ?? "System";
const logout = () => {
authModule.logout();
};
const externalLogout = () => {
authModule.externalLogout();
};
</script>

<style scoped>
Expand Down
Loading

0 comments on commit 8701a50

Please sign in to comment.