154 lines
5.6 KiB
JavaScript
154 lines
5.6 KiB
JavaScript
// @ts-check
|
||
const { test, expect } = require('@playwright/test')
|
||
|
||
const BENUTZER = process.env.E2E_USER || 'e2e'
|
||
const PASSWORT = process.env.E2E_PASSWORD || 'e2e-testpasswort'
|
||
|
||
/**
|
||
* Meldet sich an. Die Anwendung leitet nicht angemeldete Aufrufe auf /login um;
|
||
* bei einer frischen Installation stattdessen auf /einrichtung.
|
||
*/
|
||
async function anmelden(page) {
|
||
await page.goto('/login')
|
||
|
||
if (page.url().includes('/einrichtung')) {
|
||
throw new Error(
|
||
'Instanz ist noch nicht eingerichtet. Zuerst einen Benutzer anlegen: ' +
|
||
'docker compose exec -u www-data php php scripts/e2e-benutzer.php'
|
||
)
|
||
}
|
||
|
||
await page.fill('#benutzername', BENUTZER)
|
||
await page.fill('#passwort', PASSWORT)
|
||
await page.click('button[type="submit"]')
|
||
await expect(page).toHaveURL(/\/$/)
|
||
}
|
||
|
||
test.describe('Grundgerüst', () => {
|
||
test('Anmeldeseite ist erreichbar', async ({ page }) => {
|
||
await page.goto('/login')
|
||
await expect(page.locator('h1, .marke')).toContainText(/Finanzmanager|Anmeldung/)
|
||
await expect(page.locator('#benutzername')).toBeVisible()
|
||
})
|
||
|
||
test('Ohne Anmeldung führt jeder Weg zur Anmeldung', async ({ page }) => {
|
||
await page.goto('/einstellungen')
|
||
await expect(page).toHaveURL(/\/(login|einrichtung)/)
|
||
})
|
||
|
||
test('Falsches Passwort wird abgewiesen', async ({ page }) => {
|
||
await page.goto('/login')
|
||
test.skip(page.url().includes('/einrichtung'), 'Instanz noch nicht eingerichtet')
|
||
|
||
await page.fill('#benutzername', BENUTZER)
|
||
await page.fill('#passwort', 'ganz sicher falsch')
|
||
await page.click('button[type="submit"]')
|
||
|
||
await expect(page.locator('.meldung-fehler')).toBeVisible()
|
||
await expect(page).toHaveURL(/\/login/)
|
||
})
|
||
|
||
test('Anmeldung führt ins Cockpit', async ({ page }) => {
|
||
await anmelden(page)
|
||
await expect(page.locator('h1')).toContainText('Cockpit')
|
||
await expect(page.locator('.seitenleiste')).toContainText('Kontenübersicht')
|
||
})
|
||
|
||
test('Vermögensentwicklung zeigt zwölf gestapelte Monatsbalken', async ({ page }) => {
|
||
await anmelden(page)
|
||
|
||
const kachel = page.locator('.kachel', { hasText: 'Vermögensentwicklung' })
|
||
await expect(kachel.locator('svg.diagramm')).toBeVisible()
|
||
|
||
// Zwölf Monate, jeder mit eigener Beschriftung unter dem Balken.
|
||
await expect(kachel.locator('.diagramm-beschriftung:not(.schwach)')).toHaveCount(12)
|
||
await expect(kachel.locator('.diagramm rect').first()).toBeVisible()
|
||
|
||
// Jedes Balkenstück gehört zu einem Eintrag der Legende, keiner doppelt.
|
||
const farben = await kachel.locator('.legendenfarbe:not(.leer)').evaluateAll(
|
||
knoten => knoten.map(k => getComputedStyle(k).backgroundColor)
|
||
)
|
||
expect(farben.length).toBeGreaterThan(0)
|
||
expect(new Set(farben).size).toBe(farben.length)
|
||
})
|
||
|
||
test('„Übrige Konten“ führt eine Ebene tiefer und wieder zurück', async ({ page }) => {
|
||
await anmelden(page)
|
||
|
||
const kachel = page.locator('.kachel', { hasText: 'Vermögensentwicklung' })
|
||
const sammel = kachel.locator('.legendenname.tiefer')
|
||
|
||
// Der Testbestand hat mehr Konten als Farbtöne; sonst gäbe es nichts
|
||
// aufzuklappen und der Test hätte hier nichts zu prüfen.
|
||
await expect(sammel).toHaveText(/Übrige Konten \(\d+\)/)
|
||
|
||
const oben = await kachel.locator('.legendenname').allTextContents()
|
||
await sammel.click()
|
||
await expect(page).toHaveURL(/ebene=1/)
|
||
await expect(kachel.locator('.diagrammpfad')).toContainText('Ebene 2 von')
|
||
|
||
// Eine Ebene tiefer stehen andere Konten in der Legende.
|
||
const unten = await kachel.locator('.legendenname').allTextContents()
|
||
expect(unten.filter(name => oben.includes(name) && !name.startsWith('Übrige'))).toHaveLength(0)
|
||
|
||
await kachel.locator('.diagrammpfad a').first().click()
|
||
await expect(page).toHaveURL(/ebene=0/)
|
||
expect(await kachel.locator('.legendenname').allTextContents()).toEqual(oben)
|
||
})
|
||
|
||
test('Schalter „nur aktive“ behält seinen Zustand', async ({ page }) => {
|
||
await anmelden(page)
|
||
|
||
const schalter = page.locator('.kachel', { hasText: 'Vermögensentwicklung' })
|
||
.locator('.schalter')
|
||
await expect(schalter).toContainText('nur aktive')
|
||
|
||
// Ein früherer Lauf kann den Schalter umgelegt haben.
|
||
if (await schalter.getAttribute('aria-checked') === 'false') {
|
||
await schalter.click()
|
||
}
|
||
await expect(schalter).toHaveAttribute('aria-checked', 'true')
|
||
|
||
await schalter.click()
|
||
await expect(schalter).toHaveAttribute('aria-checked', 'false')
|
||
|
||
// Gespeichert, nicht nur angezeigt: Der Zustand überlebt den Neuaufruf.
|
||
await page.goto('/')
|
||
await expect(schalter).toHaveAttribute('aria-checked', 'false')
|
||
|
||
await schalter.click()
|
||
await expect(schalter).toHaveAttribute('aria-checked', 'true')
|
||
})
|
||
|
||
test('Einstellungsdialog zeigt alle Reiter', async ({ page }) => {
|
||
await anmelden(page)
|
||
await page.click('a[href="/einstellungen"]')
|
||
|
||
await expect(page.locator('h1')).toContainText('Einstellungen')
|
||
for (const reiter of ['Allgemein', 'Darstellung', 'Buchhaltung', 'Diagnose']) {
|
||
await expect(page.locator('.tableiste')).toContainText(reiter)
|
||
}
|
||
})
|
||
|
||
test('Einstellung lässt sich speichern', async ({ page }) => {
|
||
await anmelden(page)
|
||
await page.goto('/einstellungen?bereich=darstellung')
|
||
|
||
await page.fill('#zeilen_pro_seite', '250')
|
||
// Eindeutig das Einstellungsformular treffen – im Kopfbereich steht
|
||
// ebenfalls ein Submit-Knopf (Abmelden), und der kommt im DOM zuerst.
|
||
await page.click('.formular.breit button[type="submit"]')
|
||
|
||
await expect(page.locator('.meldung-erfolg')).toBeVisible()
|
||
await expect(page.locator('#zeilen_pro_seite')).toHaveValue('250')
|
||
})
|
||
|
||
test('Abmelden beendet die Sitzung', async ({ page }) => {
|
||
await anmelden(page)
|
||
await page.click('form[action="/logout"] button')
|
||
|
||
await expect(page).toHaveURL(/\/login/)
|
||
await page.goto('/')
|
||
await expect(page).toHaveURL(/\/login/)
|
||
})
|
||
})
|