Seitenleiste bleibt stehen, Benutzermenue wieder erreichbar
Am Schreibtisch war "Mein Konto" praktisch nicht zu finden - und damit auch nicht Passwortwechsel, zweiter Faktor und Passkey-Verwaltung. Die Seitenleiste hatte keine feste Hoehe und wuchs mit der Seite mit. Ihr Fuss mit dem Benutzermenue landete deshalb am Ende der SEITE statt am unteren Bildschirmrand: auf /heute bei 1632 Pixel in einem 900 Pixel hohen Fenster. Nachgemessen, nicht geraten - im Browser auf fuenf Seiten. Der Fehler sah nach nichts aus. Kein Element fehlte, nichts war kaputt, die Seite sah richtig aus. Man musste nur ganz nach unten scrollen. Behoben mit lg:sticky, lg:top-0, lg:h-dvh und lg:self-start. Das self-start gehoert dazu, weil ein Flex-Kind sonst auf die volle Zeilenhoehe gestreckt wird und die feste Hoehe wieder verliert. Die Navigation dazwischen hat bereits overflow-y-auto und scrollt bei Bedarf in sich selbst. Am Handy war es nie ein Problem - dort sitzt das Menue in der ohnehin festen Kopfzeile. Neu: e2e/benutzermenue.spec.ts fragt nicht, ob das Menue im DOM ist, sondern ob es im sichtbaren Bereich steht - auf fuenf Seiten, an drei Scrollpositionen, auf einem flachen Laptopfenster und am Handy. Die Position in der Mitte ist Absicht: Am Seitenende wird eine mitwachsende Leiste zufaellig doch sichtbar, der Test waere gruen und der Fehler trotzdem da. Beide Sichtbarkeitstests sind gegen den kaputten Zustand gegengeprueft und schlagen dort an. Refs #1
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { anmelden } from './hilfen'
|
||||
|
||||
/**
|
||||
* Das Benutzermenü muss erreichbar sein, ohne danach zu suchen.
|
||||
*
|
||||
* Anlass ist Issue #1: Am Schreibtisch war „Mein Konto" praktisch nicht
|
||||
* zu finden — und damit auch nicht der Passwortwechsel, der zweite Faktor
|
||||
* und die Passkey-Verwaltung. Die Seitenleiste hatte keine feste Höhe und
|
||||
* wuchs mit der Seite mit; ihr Fuß landete deshalb am Ende der Seite statt
|
||||
* am unteren Bildschirmrand. Auf `/heute` waren das 1632 Pixel bei 900
|
||||
* Pixel Fensterhöhe.
|
||||
*
|
||||
* Der Fehler sah nach nichts aus: Kein Element fehlte, nichts war kaputt,
|
||||
* die Seite sah richtig aus. Man musste nur ans Seitenende scrollen — und
|
||||
* darauf kommt niemand, der eine Seitenleiste vor sich hat.
|
||||
*
|
||||
* Geprüft wird deshalb nicht „ist das Element im DOM", sondern „steht es
|
||||
* im sichtbaren Bereich".
|
||||
*/
|
||||
|
||||
/** Der Auslöser des Benutzermenüs in der Seitenleiste am Schreibtisch. */
|
||||
function ausloeserInSeitenleiste(seite: Page) {
|
||||
return seite.locator('aside button[aria-haspopup="menu"]')
|
||||
}
|
||||
|
||||
/** Steht das Element vollständig im Fenster, ohne zu scrollen? */
|
||||
async function imSichtbarenBereich(seite: Page, auswahl: string): Promise<boolean> {
|
||||
return seite.evaluate((sel) => {
|
||||
const el = document.querySelector(sel)
|
||||
if (!el) return false
|
||||
const k = el.getBoundingClientRect()
|
||||
return k.top >= 0 && k.bottom <= window.innerHeight && k.height > 0
|
||||
}, auswahl)
|
||||
}
|
||||
|
||||
test.describe('Benutzermenü am Schreibtisch', () => {
|
||||
test.skip(({ isMobile }) => Boolean(isMobile), 'Am Handy sitzt es in der Kopfzeile.')
|
||||
|
||||
test('steht auch auf langen Seiten im Bild, ohne zu scrollen', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
|
||||
// Seiten, die laenger sind als das Fenster - genau dort trat der
|
||||
// Fehler auf. /fristen ist als Gegenprobe kurz.
|
||||
for (const ziel of ['/heute', '/vorgaenge', '/dokumente', '/finanzen', '/fristen']) {
|
||||
await page.goto(ziel)
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
const sichtbar = await imSichtbarenBereich(page, 'aside button[aria-haspopup="menu"]')
|
||||
expect(sichtbar, `Benutzermenü nicht im Bild auf ${ziel}`).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
test('bleibt an jeder Scrollposition stehen', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/heute')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Bewusst auch die Mitte pruefen. Nur oben und unten zu messen genuegt
|
||||
// nicht: Eine mitwachsende Leiste ist am Seitenende zufaellig doch
|
||||
// sichtbar - der Test waere dann gruen und der Fehler trotzdem da.
|
||||
for (const anteil of [0, 0.5, 1]) {
|
||||
await page.evaluate((a) => {
|
||||
const hoehe = document.documentElement.scrollHeight - window.innerHeight
|
||||
window.scrollTo(0, hoehe * a)
|
||||
}, anteil)
|
||||
await page.waitForTimeout(250)
|
||||
|
||||
const sichtbar = await imSichtbarenBereich(page, 'aside button[aria-haspopup="menu"]')
|
||||
expect(sichtbar, `Benutzermenü nicht im Bild bei ${anteil * 100}% Scrollposition`).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
test('führt in zwei Klicks nach „Mein Konto"', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/heute')
|
||||
|
||||
await ausloeserInSeitenleiste(page).click()
|
||||
await page.getByRole('menuitem', { name: 'Mein Konto' }).click()
|
||||
|
||||
await page.waitForURL('**/konto')
|
||||
// Und dort steht auch, worum es im Issue ging.
|
||||
await expect(page.getByRole('heading', { name: 'Mein Konto' })).toBeVisible()
|
||||
await expect(page.getByRole('heading', { name: 'Passwort ändern' })).toBeVisible()
|
||||
await expect(page.getByRole('heading', { name: 'Passkeys' })).toBeVisible()
|
||||
})
|
||||
|
||||
test('bleibt auf einem flachen Fenster erreichbar', async ({ page }) => {
|
||||
// Ein Laptop mit 1280x620 ist knapp: Kopf, Betreutenauswahl,
|
||||
// Navigation und Fuss muessen zusammen hineinpassen. Die Navigation
|
||||
// dazwischen darf scrollen, der Fuss nicht verschwinden.
|
||||
await anmelden(page)
|
||||
await page.setViewportSize({ width: 1280, height: 620 })
|
||||
await page.goto('/heute')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
expect(await imSichtbarenBereich(page, 'aside button[aria-haspopup="menu"]')).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Benutzermenü am Handy', () => {
|
||||
test.skip(({ isMobile }) => !isMobile, 'Am Schreibtisch sitzt es in der Seitenleiste.')
|
||||
|
||||
test('steht in der Kopfzeile und bleibt beim Scrollen stehen', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/heute')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
expect(await imSichtbarenBereich(page, 'header button[aria-haspopup="menu"]')).toBe(true)
|
||||
|
||||
await page.evaluate(() => window.scrollTo(0, document.documentElement.scrollHeight))
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
expect(await imSichtbarenBereich(page, 'header button[aria-haspopup="menu"]')).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user