Compare commits

...

2 Commits

6 changed files with 50 additions and 9 deletions
@@ -508,7 +508,7 @@ export default {
this.handlePersonUrl(); this.handlePersonUrl();
}, },
template: /* html */` template: /* html */`
<div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }"> <div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }" data-cy="studentenverwaltung">
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow"> <header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center"> <div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center">
<button <button
@@ -632,14 +632,14 @@ export default {
</app-menu> </app-menu>
</div> </div>
</aside> </aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100"> <nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100" data-cy="stv-sidebar">
<div class="offcanvas-header justify-content-end px-1 d-md-none"> <div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button> <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div> </div>
<stv-verband :preselectedKey="studiengangKz ? '' + studiengangKz : null" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband> <stv-verband :preselectedKey="studiengangKz ? '' + studiengangKz : null" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="studiensemesterKurzbz" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester> <stv-studiensemester v-model:studiensemester-kurzbz="studiensemesterKurzbz" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav> </nav>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10"> <main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10" data-cy="stv-main">
<vertical-split> <vertical-split>
<template #top> <template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list> <stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
@@ -86,7 +86,7 @@ export default {
this.loadConfig(); this.loadConfig();
}, },
template: ` template: `
<div class="stv-details h-100 d-flex flex-column"> <div class="stv-details h-100 d-flex flex-column" data-cy="stv-details">
<div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center"> <div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center">
{{$p.t('ui', 'chooseStudent')}} {{$p.t('ui', 'chooseStudent')}}
</div> </div>
@@ -108,6 +108,7 @@ export default {
<fhc-tabs <fhc-tabs
v-if="students.length == 1" v-if="students.length == 1"
ref="tabs" ref="tabs"
data-cy="stv-details-tabs"
:useprimevue="true" :useprimevue="true"
:modelValue="students[0]" :modelValue="students[0]"
:config="config" :config="config"
@@ -116,7 +117,17 @@ export default {
@changed="reload" @changed="reload"
> >
</fhc-tabs> </fhc-tabs>
<fhc-tabs v-else ref="tabs" :useprimevue="true" :modelValue="students" :config="config" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="reload"></fhc-tabs> <fhc-tabs
v-else
ref="tabs"
data-cy="stv-details-tabs"
:useprimevue="true"
:modelValue="students"
:config="config"
:default="$route.params.tab"
style="flex: 1 1 0%; height: 0%"
@changed="reload">
</fhc-tabs>
</div> </div>
<div v-else> <div v-else>
Loading... Loading...
@@ -13,7 +13,7 @@ export default {
} }
}, },
template: ` template: `
<div class="stv-details-multistatus h-100"> <div class="stv-details-multistatus h-100" data-cy="stv-tab-multistatus">
<tbl-multi-status :model-value="modelValue" :config="config"></tbl-multi-status> <tbl-multi-status :model-value="modelValue" :config="config"></tbl-multi-status>
</div> </div>
` `
@@ -152,6 +152,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action'; button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-forward"></i>'; button.innerHTML = '<i class="fa fa-forward"></i>';
button.title = this.$p.t('ui', 'btn_statusVorruecken'); button.title = this.$p.t('ui', 'btn_statusVorruecken');
button.setAttribute('data-cy', 'status-btn-advance');
button.addEventListener('click', () => button.addEventListener('click', () =>
this.actionAdvanceStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester) this.actionAdvanceStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
); );
@@ -164,6 +165,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action'; button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-check"></i>'; button.innerHTML = '<i class="fa fa-check"></i>';
button.title = this.$p.t('ui', 'btn_confirmStatus'); button.title = this.$p.t('ui', 'btn_confirmStatus');
button.setAttribute('data-cy', 'status-btn-confirm');
button.addEventListener('click', () => button.addEventListener('click', () =>
this.actionConfirmStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester) this.actionConfirmStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
); );
@@ -175,6 +177,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action'; button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>'; button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'btn_editStatus'); button.title = this.$p.t('ui', 'btn_editStatus');
button.setAttribute('data-cy', 'status-btn-edit');
button.addEventListener('click', () => button.addEventListener('click', () =>
this.actionEditStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester) this.actionEditStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
); );
@@ -186,6 +189,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action'; button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>'; button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'btn_deleteStatus'); button.title = this.$p.t('ui', 'btn_deleteStatus');
button.setAttribute('data-cy', 'status-btn-delete');
button.addEventListener('click', () => button.addEventListener('click', () =>
this.actionDeleteStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester) this.actionDeleteStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
); );
@@ -204,6 +208,11 @@ export default{
{ {
row.getElement().classList.add('text-black','text-opacity-50','fst-italic'); row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
} }
// data-cy: unique per status entry for Cypress assertions
const stdsem = rowData.studiensemester_kurzbz ?? 'unknown';
const status = rowData.status_kurzbz ?? 'unknown';
const sem = rowData.ausbildungssemester ?? '0';
row.getElement().setAttribute('data-cy', `status-row-${stdsem}-${status}-${sem}`);
}, },
layout: 'fitDataStretchFrozen', layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false, layoutColumnsOnNewData: false,
@@ -393,7 +402,7 @@ export default{
.catch(this.$fhcAlert.handleSystemError); .catch(this.$fhcAlert.handleSystemError);
}, },
template: ` template: `
<div class="stv-multistatus h-100 pt-3"> <div class="stv-multistatus h-100 pt-3" data-cy="stv-multistatus">
<status-modal <status-modal
ref="test" ref="test"
:meldestichtag="new Date(dataMeldestichtag)" :meldestichtag="new Date(dataMeldestichtag)"
@@ -405,6 +414,7 @@ export default{
<core-filter-cmpt <core-filter-cmpt
v-if="!this.modelValue.length" v-if="!this.modelValue.length"
ref="table" ref="table"
data-cy="stv-multistatus-table"
:tabulator-options="tabulatorOptions" :tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents" :tabulator-events="tabulatorEvents"
table-only table-only
@@ -142,6 +142,10 @@ export default {
row.getElement().classList.add('text-black','text-opacity-50','fst-italic'); row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
} }
row.getElement().draggable = true row.getElement().draggable = true
// data-cy for Cypress e2e: prefer prestudent_id, fall back to uid or person_id
const d = row.getData();
const id = d.prestudent_id ?? d.uid ?? d.person_id ?? 'unknown';
row.getElement().setAttribute('data-cy', 'student-row-' + id);
}, },
ajaxRequestFunc: (url, config, params) => { ajaxRequestFunc: (url, config, params) => {
@@ -600,11 +604,12 @@ export default {
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features // TODO(chris): filter component column chooser has no accessibilty features
template: ` template: `
<div class="stv-list h-100 pt-3"> <div class="stv-list h-100 pt-3" data-cy="stv-list">
<div <div
class="tabulator-container d-flex flex-column h-100" class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}" :class="{'has-filter': filter.length}"
tabindex="0" tabindex="0"
data-cy="stv-list-table-container"
@focusin="onFocus" @focusin="onFocus"
@keydown="onKeydown" @keydown="onKeydown"
v-draggable:copyLink.capture="selectedDragObject" v-draggable:copyLink.capture="selectedDragObject"
@@ -61,6 +61,15 @@ export default {
}, },
noSemReloadNodes() { noSemReloadNodes() {
return this.nodes.reduce(this.mapNodesToNoSemReloadNodes, []); return this.nodes.reduce(this.mapNodesToNoSemReloadNodes, []);
},
colPt() {
return {
rowToggler: ( options ) => {
return {
'data-cy': `tree-toggle-${options?.parent?.props?.node?.key ?? 'unknown'}`
}
}
}
} }
}, },
watch: { watch: {
@@ -319,7 +328,7 @@ export default {
.catch(this.$fhcAlert.handleSystemError); .catch(this.$fhcAlert.handleSystemError);
}, },
template: /* html */` template: /* html */`
<div class="overflow-auto" tabindex="-1"> <div class="overflow-auto" tabindex="-1" data-cy="stv-verband-tree">
<pv-treetable <pv-treetable
ref="tree" ref="tree"
class="stv-verband p-treetable-sm" class="stv-verband p-treetable-sm"
@@ -339,6 +348,7 @@ export default {
field="name" field="name"
expander expander
class="text-break" class="text-break"
:pt="colPt"
> >
<template #header> <template #header>
<div class="text-right"> <div class="text-right">
@@ -349,6 +359,7 @@ export default {
v-model="filters['global']" v-model="filters['global']"
class="form-control ps-5" class="form-control ps-5"
placeholder="Search" placeholder="Search"
data-cy="verband-search"
> >
</div> </div>
</div> </div>
@@ -358,6 +369,7 @@ export default {
v-if="['semester', 'verband', 'gruppe', 'gruppe_kurzbz'].some(key => node.data.hasOwnProperty(key))" v-if="['semester', 'verband', 'gruppe', 'gruppe_kurzbz'].some(key => node.data.hasOwnProperty(key))"
:data-tree-item-key="node.key" :data-tree-item-key="node.key"
:title="node.data.studiengang_kz" :title="node.data.studiengang_kz"
:data-cy="'student-collection-'+node.key"
v-drag-click="() => toggleTreeNode(node)" v-drag-click="() => toggleTreeNode(node)"
v-drop:link-strict.student-collection="(evt, students) => dropStudents(node, students)" v-drop:link-strict.student-collection="(evt, students) => dropStudents(node, students)"
> >
@@ -367,6 +379,7 @@ export default {
v-else v-else
:data-tree-item-key="node.key" :data-tree-item-key="node.key"
:title="node.data.studiengang_kz" :title="node.data.studiengang_kz"
:data-cy="'stg-node-label-'+node.key"
v-drag-click="() => toggleTreeNode(node)" v-drag-click="() => toggleTreeNode(node)"
> >
{{ node.data.name }} {{ node.data.name }}
@@ -383,6 +396,7 @@ export default {
v-if="favorites.on || favorites.list.length" v-if="favorites.on || favorites.list.length"
href="#" href="#"
@click.prevent="filterFav" @click.prevent="filterFav"
data-cy="favorite-icon-header"
> >
<i <i
:class="favorites.on ? 'fa-solid' : 'fa-regular'" :class="favorites.on ? 'fa-solid' : 'fa-regular'"
@@ -398,6 +412,7 @@ export default {
data-link-fav-add data-link-fav-add
@click.prevent="markFav(node)" @click.prevent="markFav(node)"
@keydown.enter.stop.prevent="markFav(node)" @keydown.enter.stop.prevent="markFav(node)"
:data-cy="'favorite-icon-'+node.data.studiengang_kz"
> >
<i <i
:class="favorites.list.includes(node.data.link + '') ? 'fa-solid' : 'fa-regular'" :class="favorites.list.includes(node.data.link + '') ? 'fa-solid' : 'fa-regular'"