Jedes Zahlenfeld (inputmode="decimal") zeigt rechts ein Rechnersymbol. Klick darauf oder Alt+R öffnet einen Taschenrechner mit Grundrechenarten und Klammern, komplett per Tastatur bedienbar: Ziffern, + - * / ( ), Backspace, Entf, Pfeiltasten, C löscht alles, = setzt das Ergebnis ein, Enter überträgt ins Feld, Escape schließt und fokussiert das Feld. Das Ergebnis steht nach jedem Tastendruck unter der Rechnung. Das Symbol ist ein Hintergrundbild (kein Wrapper-Element, kein Eingriff in die Layouts); gerechnet wird mit eigenem Parser statt eval. In modalen Dialogen wandert der Rechner mit hinein. Stück/Kurs übernehmen 6, Zinssätze 3 Nachkommastellen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 lines
5.2 KiB
JavaScript
146 lines
5.2 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'
|
||
|
||
async function anmelden(page) {
|
||
await page.goto('/login')
|
||
await page.fill('#benutzername', BENUTZER)
|
||
await page.fill('#passwort', PASSWORT)
|
||
await page.click('button[type="submit"]')
|
||
await expect(page).toHaveURL(/\/$/)
|
||
}
|
||
|
||
/* Klick auf das Rechnersymbol – es sitzt rechts im Feld. */
|
||
async function symbolKlicken(feld) {
|
||
const box = await feld.boundingBox()
|
||
if (!box) throw new Error('Feld nicht sichtbar')
|
||
await feld.page().mouse.click(box.x + box.width - 12, box.y + box.height / 2)
|
||
}
|
||
|
||
test.describe('Taschenrechner an Betragsfeldern (#126)', () => {
|
||
test.beforeEach(async ({ page }) => {
|
||
await anmelden(page)
|
||
})
|
||
|
||
test('Symbol im Feld öffnet den Rechner, Enter überträgt das Ergebnis', async ({ page }) => {
|
||
await page.goto('/konten/neu')
|
||
const feld = page.locator('#anfangssaldo')
|
||
await feld.fill('')
|
||
|
||
const bild = await feld.evaluate((el) => getComputedStyle(el).backgroundImage)
|
||
expect(bild).toContain('data:image/svg+xml')
|
||
|
||
await symbolKlicken(feld)
|
||
const rechner = page.locator('.rechner')
|
||
await expect(rechner).toBeVisible()
|
||
const eingabe = rechner.locator('.rechner-eingabe')
|
||
const ergebnis = rechner.locator('.rechner-ergebnis')
|
||
await expect(eingabe).toBeFocused()
|
||
|
||
// Am Feld, rechtsbündig – darunter oder, fehlt dort der Platz, darüber.
|
||
const feldBox = await feld.boundingBox()
|
||
const rechnerBox = await rechner.boundingBox()
|
||
if (!feldBox || !rechnerBox) throw new Error('nicht sichtbar')
|
||
const darunter = rechnerBox.y - (feldBox.y + feldBox.height)
|
||
const darueber = feldBox.y - (rechnerBox.y + rechnerBox.height)
|
||
expect((darunter >= 0 && darunter < 20) || (darueber >= 0 && darueber < 20)).toBe(true)
|
||
expect(Math.abs(rechnerBox.x + rechnerBox.width - (feldBox.x + feldBox.width))).toBeLessThan(2)
|
||
|
||
// Ergebnis nach jedem Tastendruck, Punkt vor Strich, Klammern.
|
||
await page.keyboard.type('12,5*2')
|
||
await expect(ergebnis).toHaveText('25')
|
||
await page.keyboard.type('+(3-1)/4')
|
||
await expect(eingabe).toHaveValue('12,5×2+(3−1)÷4')
|
||
await expect(ergebnis).toHaveText('25,5')
|
||
|
||
// Unvollständig: das letzte Ergebnis bleibt gedämpft stehen.
|
||
await page.keyboard.type('+')
|
||
await expect(ergebnis).toHaveClass(/veraltet/)
|
||
await expect(ergebnis).toHaveText('25,5')
|
||
|
||
// Buchstaben gehören nicht in eine Rechnung.
|
||
await page.keyboard.type('q')
|
||
await expect(eingabe).toHaveValue('12,5×2+(3−1)÷4+')
|
||
|
||
// Backspace links, Entf rechts der Schreibmarke, Pfeile bewegen sie.
|
||
await page.keyboard.press('Backspace')
|
||
await page.keyboard.press('ArrowLeft')
|
||
await page.keyboard.press('ArrowLeft')
|
||
await page.keyboard.press('Delete')
|
||
await expect(eingabe).toHaveValue('12,5×2+(3−1)4')
|
||
await expect(ergebnis).toHaveClass(/fehler/)
|
||
await page.keyboard.type('*')
|
||
await expect(eingabe).toHaveValue('12,5×2+(3−1)×4')
|
||
await expect(ergebnis).toHaveText('33')
|
||
|
||
await page.keyboard.press('Enter')
|
||
await expect(rechner).toBeHidden()
|
||
await expect(feld).toHaveValue('33,00')
|
||
await expect(feld).toBeFocused()
|
||
})
|
||
|
||
test('C löscht, Escape schließt ohne Übernahme, Alt+R öffnet mit dem Feldwert', async ({ page }) => {
|
||
await page.goto('/konten/neu')
|
||
const feld = page.locator('#anfangssaldo')
|
||
await feld.fill('1.234,50')
|
||
|
||
await feld.focus()
|
||
await page.keyboard.press('Alt+r')
|
||
const rechner = page.locator('.rechner')
|
||
const eingabe = rechner.locator('.rechner-eingabe')
|
||
await expect(rechner).toBeVisible()
|
||
await expect(eingabe).toHaveValue('1234,5')
|
||
|
||
await page.keyboard.press('c')
|
||
await expect(eingabe).toHaveValue('')
|
||
await page.keyboard.type('7/0')
|
||
await expect(rechner.locator('.rechner-ergebnis')).toHaveText('Division durch 0')
|
||
|
||
await page.keyboard.press('Escape')
|
||
await expect(rechner).toBeHidden()
|
||
await expect(feld).toBeFocused()
|
||
await expect(feld).toHaveValue('1.234,50')
|
||
})
|
||
|
||
test('Tasten lassen sich auch anklicken', async ({ page }) => {
|
||
await page.goto('/konten/neu')
|
||
const feld = page.locator('#kreditlimit')
|
||
await feld.fill('')
|
||
await symbolKlicken(feld)
|
||
|
||
const rechner = page.locator('.rechner')
|
||
for (const taste of ['9', '÷', '4', '=']) {
|
||
await rechner.locator(`button[data-taste="${taste}"]`).click()
|
||
}
|
||
await expect(rechner.locator('.rechner-eingabe')).toHaveValue('2,25')
|
||
await expect(rechner.locator('.rechner-eingabe')).toBeFocused()
|
||
await rechner.getByRole('button', { name: 'Übernehmen' }).click()
|
||
await expect(feld).toHaveValue('2,25')
|
||
})
|
||
|
||
test('Im modalen Dialog: Escape schließt nur den Rechner', async ({ page }) => {
|
||
await page.goto('/vorlagen')
|
||
await page.click('#neu-knopf')
|
||
const dialog = page.locator('#neudialog')
|
||
await expect(dialog).toBeVisible()
|
||
|
||
const feld = dialog.locator('#betrag')
|
||
await feld.fill('')
|
||
await symbolKlicken(feld)
|
||
const rechner = dialog.locator('.rechner')
|
||
await expect(rechner).toBeVisible()
|
||
|
||
await page.keyboard.type('19,99*3')
|
||
await page.keyboard.press('Escape')
|
||
await expect(rechner).toBeHidden()
|
||
await expect(dialog).toBeVisible()
|
||
|
||
await symbolKlicken(feld)
|
||
await page.keyboard.type('19,99*3')
|
||
await page.keyboard.press('Enter')
|
||
await expect(dialog).toBeVisible()
|
||
await expect(feld).toHaveValue('59,97')
|
||
})
|
||
})
|