Skip to content
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
29763c1
add course title to course breadcrumbs
odalys-dataport Jun 4, 2025
3f56e00
Merge branch 'main' of https://github.com/hpi-schul-cloud/nuxt-client…
odalys-dataport Sep 6, 2025
c77201b
add folderName to breadcrumbs in folder page
odalys-dataport Sep 6, 2025
2b5f4eb
correct breadcrumbs for room admin pages
odalys-dataport Sep 6, 2025
01fe080
add board title to breadcrumbs
odalys-dataport Sep 11, 2025
71bac58
page title with actual boardName
odalys-dataport Sep 11, 2025
66a517e
Merge branch 'main' of https://github.com/hpi-schul-cloud/nuxt-client…
odalys-dataport Sep 29, 2025
1921564
sort routes alphabetically by pathname
odalys-dataport Sep 29, 2025
b8a89c9
remove wrong admin breadcrumbs again from room admin pages
odalys-dataport Sep 29, 2025
28a8752
correct breadcrumbs on admin pages
odalys-dataport Sep 30, 2025
47e3e67
fix unit tests
odalys-dataport Sep 30, 2025
794eab0
fix unit tests
odalys-dataport Sep 30, 2025
a483617
refactor BoardPageInformation composable
odalys-dataport Oct 1, 2025
b2ccb8e
simplify folderName return
odalys-dataport Oct 1, 2025
d413bfa
Merge branch 'main' of https://github.com/hpi-schul-cloud/nuxt-client…
odalys-dataport Oct 1, 2025
431de31
fix static board title
odalys-dataport Oct 1, 2025
deda6aa
remove logger
odalys-dataport Oct 1, 2025
a2417fb
adjust pageTitle format
odalys-dataport Oct 1, 2025
86444c9
refactor composable computed properties
odalys-dataport Oct 2, 2025
5abcfeb
Merge branch 'main' of https://github.com/hpi-schul-cloud/nuxt-client…
odalys-dataport Oct 2, 2025
7e912f4
solve merge conflicts after eslint config changes
odalys-dataport Oct 6, 2025
ddee535
move pageTitle into Folder.state
odalys-dataport Oct 6, 2025
098ab83
add fallback for page title parent
odalys-dataport Oct 6, 2025
788b03a
correct pageTitle order on room members page
odalys-dataport Oct 6, 2025
d5f3fa6
Merge branch 'BC-9604-consistent-breadcrumbs' of https://github.com/h…
odalys-dataport Oct 8, 2025
cd16cf6
Merge branch 'main' into BC-9604-consistent-breadcrumbs
odalys-dataport Oct 10, 2025
6746b9b
Merge branch 'main' into BC-9604-consistent-breadcrumbs
odalys-dataport Oct 13, 2025
c9d98cd
simplify pageTitle.ts
odalys-dataport Oct 13, 2025
95c6054
adjust boardPageInformation composable tests
odalys-dataport Oct 13, 2025
c0ff5f9
replace pageTitle mock function
odalys-dataport Oct 13, 2025
fe958a3
fix unit tests
odalys-dataport Oct 14, 2025
fbfb7d4
add tests for new pageTitle function
odalys-dataport Oct 15, 2025
21c18d2
Make folder state a shared composable
dyedwiper Oct 17, 2025
045d533
Remove mock of buildPageTitle where not needed
dyedwiper Oct 17, 2025
5f09dcf
Remove other buildPageTitle mocks by setting up pinia
dyedwiper Oct 17, 2025
c1a2058
Move pinia setup to beforeAll
dyedwiper Oct 17, 2025
21092d0
Adjust page titles on school admin pages
dyedwiper Oct 17, 2025
2d429c4
Adjust descriptions for pageTitle tests
dyedwiper Oct 17, 2025
ee016d2
Revert "Adjust page titles on school admin pages"
dyedwiper Oct 17, 2025
0a292a8
Merge branch 'main' into BC-9604-consistent-breadcrumbs
dyedwiper Oct 17, 2025
14db379
Merge branch 'main' into BC-9604-consistent-breadcrumbs
dyedwiper Oct 17, 2025
58f503d
Fix test of folder page
dyedwiper Oct 17, 2025
46b4a9d
Fix throwApplicationError in folder state
dyedwiper Oct 17, 2025
f9b4b5b
Fix breadcrumb test in folder state
dyedwiper Oct 17, 2025
9ab5ebd
Refactor setting of pageTitle for folder
dyedwiper Oct 17, 2025
272c8fd
Fix folder test
dyedwiper Oct 17, 2025
43bd806
updated unit tests
luejoh Oct 20, 2025
f3b9f06
Merge branch 'main' into BC-9604-consistent-breadcrumbs
odalys-dataport Oct 20, 2025
dc6c2a5
add missing page title parents
odalys-dataport Oct 20, 2025
d34b465
Merge branch 'BC-9604-consistent-breadcrumbs' of https://github.com/h…
odalys-dataport Oct 20, 2025
efeccc4
use else if
odalys-dataport Oct 22, 2025
78aba85
solve merge conflict in routes
odalys-dataport Oct 22, 2025
58b337f
Merge branch 'main' into BC-9604-consistent-breadcrumbs
odalys-dataport Oct 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions src/components/administration/ProvisioningOptionsPage.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ import { VCheckboxBtn } from "vuetify/lib/components/index";

vi.mock("@data-provisioning-options");

vi.mock(
"@/utils/pageTitle",
() =>
({
buildPageTitle: (pageTitle) => pageTitle ?? "",
}) as typeof import("@/utils/pageTitle")
);

vi.mock("vue-router");
const useRouterMock = <Mock>useRouter;

Expand Down Expand Up @@ -87,9 +79,8 @@ describe("ProvisioningOptionsPage", () => {

const breadcrumbs = wrapper.findAll(".breadcrumbs-item");

expect(breadcrumbs[0].text()).toEqual("pages.administration.index.title");
expect(breadcrumbs[1].text()).toEqual("pages.administration.school.index.title");
expect(breadcrumbs[2].text()).toEqual("components.administration.provisioningOptions.page.title");
expect(breadcrumbs[0].text()).toEqual("pages.administration.school.index.title");
expect(breadcrumbs[1].text()).toEqual("components.administration.provisioningOptions.page.title");
});
});

Expand Down
17 changes: 6 additions & 11 deletions src/components/administration/ProvisioningOptionsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@
</template>

<script setup lang="ts">
import { Breadcrumb } from "../templates/default-wireframe.types";
import VCustomDialog from "@/components/organisms/vCustomDialog.vue";
import { Breadcrumb } from "@/components/templates/default-wireframe.types";
import DefaultWireframe from "@/components/templates/DefaultWireframe.vue";
import { injectStrict, THEME_KEY } from "@/utils/inject";
import { buildPageTitle } from "@/utils/pageTitle";
Expand Down Expand Up @@ -157,21 +157,16 @@ const theme = injectStrict(THEME_KEY);
const pageTitle = buildPageTitle(t("components.administration.provisioningOptions.page.title"));
useTitle(pageTitle);

const schoolSettingsPage: Breadcrumb = {
title: t("pages.administration.school.index.title"),
to: "/administration/school-settings",
};
const breadcrumbs: Breadcrumb[] = [
const breadcrumbs: ComputedRef<Breadcrumb[]> = computed(() => [
{
title: t("pages.administration.index.title"),
disabled: true,
title: t("pages.administration.school.index.title"),
to: "/administration/school-settings",
},
schoolSettingsPage,
{
title: t("components.administration.provisioningOptions.page.title"),
disabled: true,
},
];
]);

const provisioningOptions: ComputedRef<ProvisioningOptions> = computed(() => provisioningOptionsData.value);

Expand Down Expand Up @@ -239,7 +234,7 @@ const onCancel = async () => {

const redirectToAdminPage = async () => {
await router.push({
path: schoolSettingsPage.to,
path: "/administration/school-settings",
query: { openPanels: "authentication" },
});
};
Expand Down
14 changes: 6 additions & 8 deletions src/components/error-handling/errorContent.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ import PermissionErrorSvg from "@/assets/img/PermissionErrorSvg.vue";
import ErrorContent from "@/components/error-handling/ErrorContent.vue";
import { HttpStatusCode } from "@/store/types/http-status-code.enum";
import { createTestingI18n, createTestingVuetify } from "@@/tests/test-utils/setup";
import { createTestingPinia } from "@pinia/testing";
import { mount } from "@vue/test-utils";

vi.mock(
"@/utils/pageTitle",
() =>
({
buildPageTitle: (pageTitle) => pageTitle ?? "",
}) as typeof import("@/utils/pageTitle")
);
import { setActivePinia } from "pinia";

describe("@/components/error-handling/ErrorContent.vue", () => {
beforeAll(() => {
setActivePinia(createTestingPinia());
});

const getWrapper = (errorText: string, statusCode: HttpStatusCode) =>
mount(ErrorContent, {
propsData: {
Expand Down
10 changes: 6 additions & 4 deletions src/components/lern-store/LernstoreCollectionDetailView.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import { initializeAxios } from "@/utils/api";
import { Collection } from "@@/tests/test-utils/mockDataCollection";
import { createTestingI18n, createTestingVuetify } from "@@/tests/test-utils/setup";
import setupStores from "@@/tests/test-utils/setupStores";
import { createTestingPinia } from "@pinia/testing";
import { RouterLinkStub } from "@vue/test-utils";

vi.mock("@/utils/pageTitle", () => ({
buildPageTitle: (pageTitle) => pageTitle ?? "",
}));
import { setActivePinia } from "pinia";

initializeAxios({
get: async () => ({ data: [] }),
Expand All @@ -26,6 +24,10 @@ describe("@/components/organisms/LernstoreCollectionDetailView", () => {
window.scrollTo = vi.fn();
let wrapper;

beforeAll(() => {
setActivePinia(createTestingPinia());
});

beforeEach(() => {
wrapper = shallowMount(LernstoreCollectionDetailView, {
props: { ...testProps },
Expand Down
8 changes: 4 additions & 4 deletions src/components/lern-store/LernstoreDetailView.unit.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import LernstoreDetailView from "./LernstoreDetailView";
import { Resource } from "@@/tests/test-utils/mockDataResource";
import { createTestingI18n, createTestingVuetify } from "@@/tests/test-utils/setup";

vi.mock("@/utils/pageTitle", () => ({
buildPageTitle: (pageTitle) => pageTitle ?? "",
}));
import { createTestingPinia } from "@pinia/testing";
import { setActivePinia } from "pinia";

const testProps = {
resource: Resource,
};

describe("@/components/molecules/LernstoreDetailView", () => {
setActivePinia(createTestingPinia());

const wrapper = shallowMount(LernstoreDetailView, {
props: { ...testProps },
global: {
Expand Down
1 change: 1 addition & 0 deletions src/layouts/LoggedIn.layout.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { VApp } from "vuetify/lib/components/index";

vi.mock("vue-router", () => ({
useRoute: () => ({ path: "rooms/courses-list" }),
useRouter: vi.fn(),
}));

const setup = () => {
Expand Down
94 changes: 48 additions & 46 deletions src/modules/data/board/BoardPageInformation.composable.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,78 @@
import { useBoardStore } from "./Board.store";
import { useBoardApi } from "./BoardApi.composable";
import { Breadcrumb } from "@/components/templates/default-wireframe.types";
import { BoardContextType } from "@/types/board/BoardContext";
import { createTestableSharedComposable } from "@/utils/create-shared-composable";
import { buildPageTitle } from "@/utils/pageTitle";
import { computed, ref, unref } from "vue";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";

const useBoardPageInformation = () => {
const { t } = useI18n();

const { getContextInfo } = useBoardApi();
const boardStore = useBoardStore();

const boardContext = ref<Awaited<ReturnType<typeof getContextInfo>>>();

const roomId = computed(() => boardContext.value?.id);
const contextType = computed(() => boardContext.value?.type);

const pageTitle = computed(() => {
const roomName = unref(boardContext)?.name;
const roomNameForPageTitle = roomName ? ", " + roomName : "";
const type = unref(boardContext)?.type;
const isCourse = computed(() => contextType.value === BoardContextType.Course);
const isRoom = computed(() => contextType.value === BoardContextType.Room);

const roomName = computed(() => {
const roomNameFallback = isCourse.value ? t("common.labels.course") : t("common.labels.room");

return boardContext.value?.name ?? roomNameFallback;
});

const boardTitle = computed(() => {
let fallback = t("common.words.board");

if (type === BoardContextType.Course) {
return buildPageTitle(`${t("pages.room.boardCard.label.courseBoard")}${roomNameForPageTitle}`);
if (isCourse.value) {
fallback = t("pages.room.boardCard.label.courseBoard");
}
if (type === BoardContextType.Room) {
return buildPageTitle(`${t("pages.roomDetails.board.defaultName")}${roomNameForPageTitle}`);
if (isRoom.value) {
fallback = t("pages.roomDetails.board.defaultName");
}
return "";

return boardStore.board?.title ?? fallback;
});

const breadcrumbs = computed((): Breadcrumb[] => {
const id = unref(boardContext)?.id;
const type = unref(boardContext)?.type;
const name = unref(boardContext)?.name;
const pageTitle = computed(() => buildPageTitle(boardTitle.value, roomName.value));

if (!id || !type) {
return [];
}
const breadcrumbs = computed<Breadcrumb[]>(() => {
if (!roomId.value) return [];

const crumbs: Breadcrumb[] = [
{
title: roomName.value,
to: `/rooms/${roomId.value}`,
},
{
title: boardTitle.value,
disabled: true,
},
];

if (type === BoardContextType.Course) {
return [
{
title: t("common.words.courses"),
to: "/rooms/courses-overview",
disabled: false,
},
{
title: name ?? t("common.labels.course"),
to: `/rooms/${id}`,
disabled: false,
},
];
if (isCourse.value) {
crumbs.unshift({
title: t("common.words.courses"),
to: "/rooms/courses-overview",
});
}
if (type === BoardContextType.Room) {
return [
{
title: t("pages.rooms.title"),
to: "/rooms",
disabled: false,
},
{
title: name ?? t("common.labels.room"),
to: `/rooms/${id}`,
disabled: false,
},
];

if (isRoom.value) {
crumbs.unshift({
title: t("pages.rooms.title"),
to: "/rooms",
});
}
return [];

return crumbs;
});

const createPageInformation = async (id: string): Promise<void> => {
const createPageInformation = async (id: string) => {
boardContext.value = await getContextInfo(id);
};

Expand Down
30 changes: 17 additions & 13 deletions src/modules/data/board/BoardPageInformation.composable.unit.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { useBoardStore } from "./Board.store";
import { useBoardApi } from "./BoardApi.composable";
import { useSharedBoardPageInformation } from "./BoardPageInformation.composable";
import { BoardContextType } from "@/types/board/BoardContext";
import { boardResponseFactory } from "@@/tests/test-utils";
import { mountComposable } from "@@/tests/test-utils/mountComposable";
import { createMock, DeepMocked } from "@golevelup/ts-vitest";
import { createTestingPinia } from "@pinia/testing";
import { setActivePinia } from "pinia";

vi.mock("./BoardApi.composable");
const mockedUseBoardApi = vi.mocked(useBoardApi);

vi.mock("./Board.store");
const mockedUseBoardStore = vi.mocked(useBoardStore);

vi.mock(
"@/utils/create-shared-composable",
() =>
Expand All @@ -19,17 +26,14 @@ vi.mock("vue-i18n", () => ({
useI18n: vi.fn().mockReturnValue({ t: (key: string) => key }),
}));

vi.mock(
"@/utils/pageTitle",
() =>
({
buildPageTitle: (pageTitle) => pageTitle ?? "",
}) as typeof import("@/utils/pageTitle")
);
describe("BoardPageInformation.composable", () => {
let mockedBoardApiCalls: DeepMocked<ReturnType<typeof useBoardApi>>;

beforeEach(() => {
mockedUseBoardStore.mockReturnValue({ board: boardResponseFactory.build() } as ReturnType<typeof useBoardStore>);

setActivePinia(createTestingPinia());

mockedBoardApiCalls = createMock<ReturnType<typeof useBoardApi>>();
mockedUseBoardApi.mockReturnValue(mockedBoardApiCalls);
});
Expand Down Expand Up @@ -60,14 +64,14 @@ describe("BoardPageInformation.composable", () => {
};
};

it("should return two breadcrumbs: 1. course page and and 2. course-overview page", async () => {
it("should return three breadcrumbs: course-overview-page > course-page > board-page", async () => {
const { createPageInformation, breadcrumbs } = setup();

const fakeId = "abc123-1";

await createPageInformation(fakeId);

expect(breadcrumbs.value).toHaveLength(2);
expect(breadcrumbs.value).toHaveLength(3);
});

it("should set page title", async () => {
Expand Down Expand Up @@ -122,14 +126,14 @@ describe("BoardPageInformation.composable", () => {
};
};

it("should return two breadcrumbs: 1. room page and and 2. room-overview page", async () => {
it("should return three breadcrumbs: room-overview-page > room-page > board-page", async () => {
const { createPageInformation, breadcrumbs } = setup();

const fakeId = "abc123-1";

await createPageInformation(fakeId);

expect(breadcrumbs.value).toHaveLength(2);
expect(breadcrumbs.value).toHaveLength(3);
});

it("should set page title", async () => {
Expand Down Expand Up @@ -183,14 +187,14 @@ describe("BoardPageInformation.composable", () => {
expect(breadcrumbs.value).toEqual([]);
});

it("should generate empty page title", async () => {
it("should generate page title with generic fallback", async () => {
const { createPageInformation, pageTitle } = setup();

const fakeId = "abc123";

await createPageInformation(fakeId);

expect(pageTitle.value).toEqual("");
expect(pageTitle.value).toContain("common.labels.room");
});
});
});
Loading
Loading