From 5491bb69f693394adaedc3f1f333377cefc2cf57 Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Fri, 4 Sep 2026 12:39:29 +0200 Subject: [PATCH] Fix #62: E2E-Tests an den Typeahead im Kontoblatt angepasst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwölf Tests standen auf Rot, ohne dass die Anwendung etwas falsch machte – die Specs prüften den Stand vor dem Umbau. Vier Ursachen lagen darunter: Die Kombobox lässt ihr `.** `kombobox.js` lässt das ursprüngliche Feld + zwar im Formular stehen, setzt es aber auf `hidden`; `selectOption()` wartet darauf + vergeblich, bis der Test nach 30 Sekunden abbricht. Der neue Helfer + `tests/e2e/specs/hilfen.js` bedient stattdessen das Eingabefeld: Er tippt die + Beschriftung ein – das ist zugleich die Probe auf die Filterung – und klickt den genau + passenden Treffer an. Exakt muss der Vergleich sein, weil `[Depot]` sonst auch das + `[Depot] …` des Wertpapiergeschäfts träfe. +- **Zwei Erfolgsmeldungen übereinander.** Seit dem Schnellspeichern ohne Neuladen + (Fix #40) bleibt die Meldung des Anlegens stehen und die neue kommt darunter. Die Tests + meinen die neue und greifen jetzt zu `.last()`. +- **Die Adressliste ist eine Bearbeitungstabelle.** Seit Fix #51 stehen die Namen in + Eingabefeldern, wo `toContainText` nicht hinsieht. Geprüft wird nun der Feldwert – die + Adresse selbst wurde die ganze Zeit korrekt gespeichert. +- **Zwei Folgefehler.** Playwright startet nach jedem Fehlschlag einen frischen Worker; + damit wird das Spec-Modul neu geladen, seine Marke neu gezogen und `beforeAll` legt neue, + leere Konten an. Die Tests „Kontoblatt lässt sich durchsuchen“ und „Löschen fragt nach“ + suchten deshalb eine Buchung, die auf dem Konto des vorigen Workers lag. Sie brauchten + keine Änderung. + +Die Suite läuft wieder vollständig durch: 87 Tests grün, und ohne die Timeouts in 2,7 +statt 7,1 Minuten. + ## FinTS-Bibliothek auf den aktuellen Upstream-Stand gehoben (04.09.2026) Der Fork `nemiah/php-fints` steht jetzt auf `v4.1.4` und damit auf dem Upstream-Stand vom diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 60dbf31..17cc2d1 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -51,6 +51,20 @@ Hierher gehört nur, was einen Browser braucht: Formularabläufe, Redirects, gerenderte Views, JavaScript. Rechen- und Model-Logik gehört in die PHP-Suite (`tests/NN_*.php`) – schneller und isoliert. +## Gemeinsame Helfer + +`specs/hilfen.js` liegt neben den Specs, endet aber nicht auf `.spec.js` und wird +deshalb nicht als Testdatei eingesammelt. Dort steht `komboboxWaehlen()`: Die +Typeahead-Felder aus `public/static/kombobox.js` lassen ihr ` darin bleibt hinter der + // Kombobox verborgen. + const liste = zeile.locator('#zuordnungsauswahl') await expect(liste).toBeVisible() - await liste.selectOption({ label: KATEGORIE }) + await komboboxWaehlen(liste.locator('select'), KATEGORIE) - await expect(page.locator('.meldung-erfolg')).toContainText('Zuordnung') + // Die Meldung des Anlegens steht noch da: Das Schnellspeichern lädt + // die Seite nicht neu, sondern hängt seine eigene darunter. + await expect(page.locator('.meldung-erfolg').last()).toContainText('Zuordnung') await expect(page.locator('tr', { hasText: `Ohne Kategorie ${MARKE}` })) .toContainText(KATEGORIE) }) @@ -202,9 +207,9 @@ test.describe('Buchungen', () => { const zeile = page.locator('tr', { hasText: `Kein Neuladen ${MARKE}` }) await zeile.locator('a.zuordnungsfeld').click() - await zeile.locator('#zuordnungsauswahl select').selectOption({ label: KATEGORIE }) + await komboboxWaehlen(zeile.locator('#zuordnungsauswahl select'), KATEGORIE) - await expect(page.locator('.meldung-erfolg')).toContainText('Zuordnung') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Zuordnung') await expect(zeile).toContainText(KATEGORIE) // Ein echtes Neuladen hätte sowohl die Markierung gelöscht als auch die @@ -229,7 +234,7 @@ test.describe('Buchungen', () => { await expect(liste).toBeVisible() await liste.selectOption({ label: KLASSE }) - await expect(page.locator('.meldung-erfolg')).toContainText('Klasse gespeichert') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Klasse gespeichert') await expect(page.locator('tr', { hasText: `Ohne Klasse ${MARKE}` })) .toContainText(KLASSE) @@ -238,7 +243,7 @@ test.describe('Buchungen', () => { await danach.locator('a.klassenfeld').click() await danach.locator('#klassenauswahl select').selectOption('') - await expect(page.locator('.meldung-erfolg')).toContainText('Klasse gelöscht') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Klasse gelöscht') await expect(page.locator('tr', { hasText: `Ohne Klasse ${MARKE}` })) .not.toContainText(KLASSE) }) @@ -253,9 +258,9 @@ test.describe('Buchungen', () => { const zeile = page.locator('tr', { hasText: `Nachträglich ${MARKE}` }) await zeile.locator('a.zuordnungsfeld').click() - await zeile.locator('#zuordnungsauswahl select').selectOption({ label: `[${SPAR}]` }) + await komboboxWaehlen(zeile.locator('#zuordnungsauswahl select'), `[${SPAR}]`) - await expect(page.locator('.meldung-erfolg')).toContainText('Zuordnung') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Zuordnung') await expect(page.locator('tr', { hasText: `Nachträglich ${MARKE}` })) .toContainText(`[${SPAR}]`) @@ -275,7 +280,7 @@ test.describe('Buchungen', () => { const zeile = page.locator('tr', { hasText: `Aufzuteilen ${MARKE}` }) await zeile.locator('a.zuordnungsfeld').click() - await zeile.locator('#zuordnungsauswahl select').selectOption('split') + await komboboxWaehlen(zeile.locator('#zuordnungsauswahl select'), 'Splitbuchung …') const dialog = page.locator('#splitdialog') await expect(dialog).toBeVisible() @@ -292,7 +297,7 @@ test.describe('Buchungen', () => { // Der Dialog hat zwei Submit-Knöpfe: Speichern und Löschen. await dialog.locator('button[type="submit"].primaer').click() - await expect(page.locator('.meldung-erfolg')).toContainText('Splitbuchung') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Splitbuchung') await expect(page.locator('tr', { hasText: `Aufzuteilen ${MARKE}` }).first()) .toContainText('Splitbuchung') @@ -320,19 +325,19 @@ test.describe('Buchungen', () => { const dialog = page.locator('#splitdialog') await expect(dialog).toBeVisible() - await expect(page.locator('#zuordnungsauswahl select')).toBeHidden() + await expect(page.locator('#zuordnungsauswahl')).toBeHidden() await expect(dialog.locator('input[name="split[0][betrag]"]')).toHaveValue('-30,00') await dialog.locator('#splitloeschen').click() - await expect(page.locator('.meldung-erfolg')).toContainText('Splitbuchung gelöscht') + await expect(page.locator('.meldung-erfolg').last()).toContainText('Splitbuchung gelöscht') const danach = page.locator('tr', { hasText: `Wieder einfach ${MARKE}` }) await expect(danach).not.toContainText('Splitbuchung') await expect(danach).not.toContainText(KATEGORIE) // Und die Zuordnung geht jetzt wieder über die Auswahlliste. await danach.locator('a.zuordnungsfeld').click() - await expect(danach.locator('#zuordnungsauswahl select')).toBeVisible() + await expect(danach.locator('#zuordnungsauswahl')).toBeVisible() }) test('Splitbuchung mit zwei Zeilen', async ({ page }) => { diff --git a/tests/e2e/specs/hilfen.js b/tests/e2e/specs/hilfen.js new file mode 100644 index 0000000..ce93746 --- /dev/null +++ b/tests/e2e/specs/hilfen.js @@ -0,0 +1,26 @@ +// @ts-check + +/* Gemeinsame Helfer der E2E-Specs. Die Datei endet nicht auf `.spec.js` und + * wird deshalb nicht als Testdatei eingesammelt. */ + +/** + * Wählt in einer Typeahead-Kombobox (public/static/kombobox.js) den Eintrag + * mit genau dieser Beschriftung. + * + * Seit dem Umbau steht das ursprüngliche ` + * @param {string} beschriftung Text des Eintrags, exakt + */ +async function komboboxWaehlen(select, beschriftung) { + const kombobox = select.locator('xpath=ancestor::div[contains(@class,"kombobox")][1]') + + await kombobox.locator('.kombobox-eingabe').fill(beschriftung) + // Exakt, sonst träfe „[Depot]“ auch das „[Depot] …“ des Wertpapiergeschäfts. + await kombobox.getByRole('option', { name: beschriftung, exact: true }).click() +} + +module.exports = { komboboxWaehlen } diff --git a/tests/e2e/specs/module.spec.js b/tests/e2e/specs/module.spec.js index 87822a9..6b03a66 100644 --- a/tests/e2e/specs/module.spec.js +++ b/tests/e2e/specs/module.spec.js @@ -323,8 +323,11 @@ test.describe('Module der Phasen 4 bis 7', () => { await expect(page.locator('.meldung-erfolg')).toContainText('erfasst') // Die Adresse landet wirklich im Adressbuch – nicht nur der Auftrag. + // Die Liste ist eine Bearbeitungstabelle: Die Namen stehen in + // Eingabefeldern, nicht als Text. await page.goto('/vertraege/adressen') - await expect(page.locator('table.liste')).toContainText(empfaenger) + await expect(page.locator(`table.liste input[name="name"][value="${empfaenger}"]`)) + .toHaveCount(1) }) test('Die Empfängerauswahl im Zahlungsverkehr ist ein Typeahead aus dem Adressbuch', async ({ page }) => { diff --git a/tests/e2e/specs/wertpapiergeschaeft.spec.js b/tests/e2e/specs/wertpapiergeschaeft.spec.js index 2a63a78..d675621 100644 --- a/tests/e2e/specs/wertpapiergeschaeft.spec.js +++ b/tests/e2e/specs/wertpapiergeschaeft.spec.js @@ -1,5 +1,6 @@ // @ts-check const { test, expect } = require('@playwright/test') +const { komboboxWaehlen } = require('./hilfen') /* * Der Weg, den die Wertpapierabrechnung nimmt: Sie kommt als Buchung aufs @@ -107,7 +108,7 @@ test.describe('Wertpapiergeschäft aus dem Kontoblatt', () => { const zeile = page.locator('tr', { hasText: empfaenger }) await zeile.locator('a.zuordnungsfeld').click() - await zeile.locator('#zuordnungsauswahl select').selectOption({ label: `[${DEPOT}] …` }) + await komboboxWaehlen(zeile.locator('#zuordnungsauswahl select'), `[${DEPOT}] …`) const dialog = page.locator('#wertpapierdialog') await expect(dialog).toBeVisible() @@ -166,7 +167,7 @@ test.describe('Wertpapiergeschäft aus dem Kontoblatt', () => { const zeile = page.locator('tr', { hasText: empfaenger }) await zeile.locator('a.zuordnungsfeld').click() - await zeile.locator('#zuordnungsauswahl select').selectOption({ label: `[${DEPOT}] …` }) + await komboboxWaehlen(zeile.locator('#zuordnungsauswahl select'), `[${DEPOT}] …`) await expect(page.locator('#wertpapierdialog')).toBeVisible() await page.fill('#wp_name', `Sparplan-Fonds ${MARKE}`)