Finanzmanager/tests/e2e/specs/bankinstitute.spec.js
fds2610-bot 0b4a216954 Sammel-Commit: PayPal-Bankverbindung und Bankleitzahlen-Verzeichnis
Zwei Features an derselben Stelle, die im Arbeitsverzeichnis
nebeneinander lagen und sich dieselben Dateien teilen (Routen,
BankingController, Bankzugang, Zugangsformular, Dokumentation) –
getrennte Commits ließen sich daraus nur mit einem kaputten
Zwischenstand schneiden.

PayPal als zweite Art von Bankverbindung
----------------------------------------
„Bankverbindung anlegen“ führt zur Wahl der Art: HBCI/FinTS oder
PayPal (bank_zugaenge.typ, Migration 014). Die Abfrage läuft über die
Classic-NVP-API (Services\PaypalNvp, TransactionSearch und GetBalance).
Kein Dreischritt, da PayPal genau ein Guthabenkonto führt –
Zugangsdaten und Kontenzuordnung stehen in einem Formular. Konten,
Umsätze, Protokoll, Regeln und Abgleich bleiben dieselben.

Bankleitzahlen-Verzeichnis
--------------------------
Verwaltung → Bankleitzahlen (/verwaltung/bankinstitute, Migration 015)
nimmt die CSV-Institutsliste der Deutschen Kreditwirtschaft auf und
beantwortet damit die eine Angabe, die beim Anlegen einer
FinTS-Verbindung regelmäßig fehlt: den PIN/TAN-Zugangspunkt.

Reine Nachschlagedaten ohne benutzer_id; ein Import ersetzt den
gesamten Bestand, statt ihn zu ergänzen. „Nur prüfen“ und „Übernehmen“
sind zwei Knöpfe an derselben Datei – abgelegt wird sie nicht, ihr
Inhalt steht ja in der Tabelle.

Services\FintsInstituteImport nimmt der Datei ihre Eigenheiten ab:
ISO-8859-1 statt UTF-8, ein Institutsname über zwei Zeilen in
Anführungszeichen, dieselbe Bankleitzahl vielfach (eine Zeile je
Filiale), Hunderte leerer Zeilen am Ende, Text statt Adresse, wo es
keinen PIN/TAN-Zugang gibt. Spalten werden über ihre Überschrift
angesprochen; fehlen BLZ und Institut, bricht der Import ab.

Gesucht wird über Bankleitzahl, BIC, Name und Ort in einem Zug: „1822“
steht bei der 1822direkt im Namen und bei der Frankfurter Sparkasse im
BIC, in keiner der beiden Bankleitzahlen.

Aus der Trefferliste führt „Verbindung anlegen“ ins gefüllte
FinTS-Formular. Die FinTS-Adresse ist damit kein Pflichtfeld mehr –
bleibt sie leer, wird sie zur Bankleitzahl nachgeschlagen. Findet auch
das nichts, weist Bankzugang den Zugang ab, statt ihn ohne Gegenstelle
zu speichern.

Tests: 31_PaypalZugangTest, 32_BankinstituteTest,
e2e/specs/banking.spec.js, e2e/specs/bankinstitute.spec.js.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 08:48:14 +02:00

98 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @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')
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(/\/$/)
}
// Die Zeile im Format der echten Institutsliste – mit allen Spalten in der
// Reihenfolge, in der die Deutsche Kreditwirtschaft sie ausgibt.
const KOPF = 'Nr.;BLZ;BIC;Institut;Ort;RZ;Organisation;HBCI-Zugang DNS;' +
'HBCI- Zugang IP-Adresse;HBCI-Version;DDV;RDH-1;RDH-2;RDH-3;RDH-4;RDH-5;' +
'RDH-6;RDH-7;RDH-8;RDH-9;RDH-10;RAH-7;RAH-9;RAH-10;PIN/TAN-Zugang URL;Version;' +
'Datum letzte Änderung'
const ZEILE = '1526;50050222;HELADEF1822;E2E-Institut (PIN/TAN);Frankfurt am Main;' +
'Finanz Informatik GmbH & Co. KG;DSGV;;;;;;;;;;;;;;;;;;' +
'https://fints.beispiel.invalid/fints/hbci;FinTS V3.0;09.02.2022'
/**
* Das Bankleitzahlen-Verzeichnis im Browser.
*
* Geprüft wird nur der Weg – Upload, Prüfbericht, Suche –, nicht das Einlesen
* selbst: Das steckt in der PHP-Suite. Und übernommen wird hier nichts, denn
* ein Import ersetzt den gesamten Bestand der Instanz; der Prüflauf lässt ihn
* unangetastet.
*/
test.describe('Bankleitzahlen-Verzeichnis', () => {
test.beforeEach(async ({ page }) => {
await anmelden(page)
})
test('Die Verwaltung führt zum Verzeichnis', async ({ page }) => {
await page.goto('/verwaltung')
const kachel = page.locator('.kachel', { hasText: 'Bankleitzahlen' })
await kachel.getByRole('link', { name: /Nachschlagen/ }).click()
await expect(page).toHaveURL(/\/verwaltung\/bankinstitute$/)
await expect(page.locator('h1')).toContainText('Bankleitzahlen')
})
test('Der Prüflauf berichtet, ohne den Bestand anzurühren', async ({ page }) => {
await page.goto('/verwaltung/bankinstitute')
await page.setInputFiles('#datei', {
name: 'e2e-institute.csv',
mimeType: 'text/csv',
buffer: Buffer.from([KOPF, ZEILE, ''].join('\r\n'), 'latin1'),
})
await page.getByRole('button', { name: 'Nur prüfen' }).click()
const bericht = page.locator('.kachel', { hasText: 'Prüfung' })
await expect(bericht).toContainText('e2e-institute.csv')
await expect(bericht).toContainText('Es wurde nichts geschrieben')
await expect(bericht).toContainText('50050222')
await expect(bericht).toContainText('fints.beispiel.invalid')
})
test('Eine fremde CSV-Datei wird abgewiesen', async ({ page }) => {
await page.goto('/verwaltung/bankinstitute')
await page.setInputFiles('#datei', {
name: 'kontoauszug.csv',
mimeType: 'text/csv',
buffer: Buffer.from('Datum;Betrag;Empfänger\r\n01.02.2026;-12,50;Bäcker\r\n', 'utf8'),
})
await page.getByRole('button', { name: 'Nur prüfen' }).click()
await expect(page.locator('.meldung-fehler')).toContainText('keine FinTS-Institutsliste')
})
test('Die Suche nimmt Bankleitzahl wie Namen', async ({ page }) => {
await page.goto('/verwaltung/bankinstitute')
await page.fill('#suche', '50050222')
await page.getByRole('button', { name: 'Suchen' }).click()
await expect(page).toHaveURL(/suche=50050222/)
// Ob es einen Treffer gibt, hängt vom eingelesenen Bestand ab – die
// Seite muss in beiden Fällen antworten, statt leer zu bleiben.
await expect(page.locator('table.liste, .platzhalter').first()).toBeVisible()
})
})