feat(contact): Spitznamen mit Vornamen-Fallback im Kontakt-Popup anzeigen
Beraterdaten um getrennte Namensfelder ergänzen und den Anzeigenamen für Popup und Kontaktmails zentral bestimmen. Leere Spitznamen fallen auf den Vornamen zurück; Nachname und Kontaktieren-Zusatz entfallen in der Überschrift. Integrationsdokumentation, API-Beispiele und Tests an den neuen Namensvertrag anpassen.feature/beraterprofile
parent
af88228d3b
commit
881ba21630
|
|
@ -45,7 +45,7 @@ nicht vorab leer anlegen: Das Backend erzeugt sie bei der ersten Buchung selbst.
|
||||||
|
|
||||||
## Beraterkontakt testen
|
## Beraterkontakt testen
|
||||||
|
|
||||||
Die [Kontakt-Testseite](http://localhost:5173/contact-test.html?berater-id=anna-mueller)
|
Die [Kontakt-Testseite](http://localhost:5173/contact-test.html?berater-id=relindis-agethen)
|
||||||
öffnet das neue Popup. Kundenprüfung, Mailpit-Versand, Profil-Einbindung und
|
öffnet das neue Popup. Kundenprüfung, Mailpit-Versand, Profil-Einbindung und
|
||||||
Checkliste sind in [docs/contact.md](docs/contact.md) dokumentiert.
|
Checkliste sind in [docs/contact.md](docs/contact.md) dokumentiert.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ kein eigenes Kontaktformular und keine separate E-Mail-Prüfung.
|
||||||
## Relevante Dateien
|
## Relevante Dateien
|
||||||
|
|
||||||
- `src/shared/berater-daten.json`: gemeinsame Datenquelle für Profile und Kontakt;
|
- `src/shared/berater-daten.json`: gemeinsame Datenquelle für Profile und Kontakt;
|
||||||
Array mit eindeutigen String-IDs, `name` und `email`. Die aktuellen Einträge
|
Array mit eindeutigen String-IDs, `vorname`, `nachname`, optionalem `spitzname` und `email`. Eigene öffentliche Profilfelder dürfen ergänzt werden.
|
||||||
sind Demo-Daten. Eigene öffentliche Profilfelder dürfen ergänzt werden.
|
|
||||||
- `src/shared/berater.js`: `findAdvisor(advisorId)` liefert den passenden Eintrag.
|
- `src/shared/berater.js`: `findAdvisor(advisorId)` liefert den passenden Eintrag.
|
||||||
- `src/features/contact/contact-dialog.js`: exportiert `openContactDialog({ advisorId })`.
|
- `src/features/contact/contact-dialog.js`: exportiert `openContactDialog({ advisorId })`.
|
||||||
- `src/features/contact/contact.css`: wird automatisch vom Popup-Modul importiert;
|
- `src/features/contact/contact.css`: wird automatisch vom Popup-Modul importiert;
|
||||||
|
|
@ -23,9 +22,11 @@ kein eigenes Kontaktformular und keine separate E-Mail-Prüfung.
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "anna-mueller",
|
"id": "relindis-agethen",
|
||||||
"name": "Anna Müller",
|
"vorname": "Relindis",
|
||||||
"email": "anna.mueller@tri-hub.de"
|
"nachname": "Agethen",
|
||||||
|
"spitzname": "Lilli",
|
||||||
|
"email": "relindis.agethen@tri-hub.de"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
@ -40,7 +41,7 @@ des Frontends. Nach Änderungen API neu starten und Frontend neu bauen.
|
||||||
Beispielbutton (ID aus dem jeweiligen Profil):
|
Beispielbutton (ID aus dem jeweiligen Profil):
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<button type="button" data-berater-id="anna-mueller">
|
<button type="button" data-berater-id="relindis-agethen">
|
||||||
Berater kontaktieren
|
Berater kontaktieren
|
||||||
</button>
|
</button>
|
||||||
```
|
```
|
||||||
|
|
@ -79,9 +80,13 @@ Falls die Profilseite selbst Daten aus der gemeinsamen Datei benötigt:
|
||||||
import berater from "./shared/berater-daten.json" with { type: "json" };
|
import berater from "./shared/berater-daten.json" with { type: "json" };
|
||||||
|
|
||||||
const profile = berater.find((entry) => entry.id === advisorId);
|
const profile = berater.find((entry) => entry.id === advisorId);
|
||||||
// profile.name und profile.email stehen bei bekannter ID zur Verfügung.
|
// profile.vorname, profile.nachname, profile.spitzname und profile.email sind verfügbar.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Das Popup zeigt den getrimmten `spitzname`, falls nicht leer, sonst den
|
||||||
|
getrimmten `vorname`. `nachname` und das bisherige Feld `name` werden für die
|
||||||
|
Überschrift nicht verwendet. Es wird kein „kontaktieren“ angehängt.
|
||||||
|
|
||||||
Das Popup ermittelt den Anzeigenamen selbst und sendet beim Absenden
|
Das Popup ermittelt den Anzeigenamen selbst und sendet beim Absenden
|
||||||
`{ email, advisorId, reason, simulationAccepted }` an `/api/contact`. Die
|
`{ email, advisorId, reason, simulationAccepted }` an `/api/contact`. Die
|
||||||
Profilseite muss weder Name noch E-Mail an das Popup schicken. Kundenprüfung,
|
Profilseite muss weder Name noch E-Mail an das Popup schicken. Kundenprüfung,
|
||||||
|
|
@ -91,7 +96,7 @@ implementiert. Unbekannte IDs sperren das Absenden.
|
||||||
## Testen
|
## Testen
|
||||||
|
|
||||||
Mit laufendem Frontend, API und Mailpit:
|
Mit laufendem Frontend, API und Mailpit:
|
||||||
`http://localhost:5173/contact-test.html?berater-id=anna-mueller`.
|
`http://localhost:5173/contact-test.html?berater-id=relindis-agethen`.
|
||||||
Für die Kundenadresse eine vorhandene Testbuchung verwenden. Die Prüfung
|
Für die Kundenadresse eine vorhandene Testbuchung verwenden. Die Prüfung
|
||||||
findet ausschließlich beim Absenden statt. Der Branch ist
|
findet ausschließlich beim Absenden statt. Der Branch ist
|
||||||
`feature/terminbuchung-kontaktfeld`; die Kontaktdateien müssen im Arbeitsstand
|
`feature/terminbuchung-kontaktfeld`; die Kontaktdateien müssen im Arbeitsstand
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ npm run dev:server
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Öffne <http://localhost:5173/contact-test.html?berater-id=anna-mueller>.
|
Öffne <http://localhost:5173/contact-test.html?berater-id=relindis-agethen>.
|
||||||
Der Button öffnet ein modales Fenster. Gib eine E-Mail aus einer vorhandenen
|
Der Button öffnet ein modales Fenster. Gib eine E-Mail aus einer vorhandenen
|
||||||
Buchung in `src/server/data/bookings.csv` ein, wähle einen Kontaktgrund und
|
Buchung in `src/server/data/bookings.csv` ein, wähle einen Kontaktgrund und
|
||||||
bestätige den Simulationshinweis. Beim Klick auf „Anfrage senden & Termin wählen“
|
bestätige den Simulationshinweis. Beim Klick auf „Anfrage senden & Termin wählen“
|
||||||
|
|
@ -19,7 +19,7 @@ prüft der Server die E-Mail und versendet nur bei vorhandener Buchung. Bei Beda
|
||||||
|
|
||||||
Nach dem Absenden müssen zwei getrennte Nachrichten in
|
Nach dem Absenden müssen zwei getrennte Nachrichten in
|
||||||
<http://localhost:8025> erscheinen: an die Kundenadresse und beispielsweise
|
<http://localhost:8025> erscheinen: an die Kundenadresse und beispielsweise
|
||||||
`anna.mueller@tri-hub.de`. Der Browser wechselt anschließend zur vom Auftrag
|
`relindis.agethen@tri-hub.de`. Der Browser wechselt anschließend zur vom Auftrag
|
||||||
vorgegebenen Microsoft-Bookings-Adresse. Es wird dort kein Termin automatisch
|
vorgegebenen Microsoft-Bookings-Adresse. Es wird dort kein Termin automatisch
|
||||||
gebucht. Die Checkliste spricht von Microsoft Forms; umgesetzt ist der konkret
|
gebucht. Die Checkliste spricht von Microsoft Forms; umgesetzt ist der konkret
|
||||||
angegebene Bookings-Link.
|
angegebene Bookings-Link.
|
||||||
|
|
@ -53,9 +53,7 @@ Popup-Styles und die lokalen DM-Sans-Schriftdateien mit. Die vollständige
|
||||||
Einbauanleitung für die Profilseite steht in [contact-integration.md](contact-integration.md).
|
Einbauanleitung für die Profilseite steht in [contact-integration.md](contact-integration.md).
|
||||||
|
|
||||||
Die gemeinsame Datenquelle `src/shared/berater-daten.json` enthält ein Array
|
Die gemeinsame Datenquelle `src/shared/berater-daten.json` enthält ein Array
|
||||||
mit eindeutigen String-IDs und den Feldern `id`, `name`, `email`. Die beiden
|
mit eindeutigen String-IDs und den Feldern `id`, `vorname`, `nachname`, `spitzname`, `email`. Die vorhandenen Einträge enthalten die Beraterprofile. Zusätzliche Profilfelder sind möglich. Die Datei
|
||||||
vorhandenen Einträge sind Demo-Daten und können durch die tatsächlichen
|
|
||||||
Profildaten ersetzt werden. Zusätzliche Profilfelder sind möglich. Die Datei
|
|
||||||
wird im Frontend eingebunden: ausschließlich öffentliche Profildaten eintragen.
|
wird im Frontend eingebunden: ausschließlich öffentliche Profildaten eintragen.
|
||||||
|
|
||||||
`src/shared/berater.js` stellt `findAdvisor(advisorId)` bereit. Popup und Server
|
`src/shared/berater.js` stellt `findAdvisor(advisorId)` bereit. Popup und Server
|
||||||
|
|
@ -65,8 +63,8 @@ nicht übernommen. E-Mail-Adressen müssen gültig sein und auf `@tri-hub.de`
|
||||||
enden. Eine unbekannte ID wird abgelehnt und sperrt das Absenden im Popup.
|
enden. Eine unbekannte ID wird abgelehnt und sperrt das Absenden im Popup.
|
||||||
|
|
||||||
Die Testseite liest den URL-Parameter `berater-id`, zum Beispiel
|
Die Testseite liest den URL-Parameter `berater-id`, zum Beispiel
|
||||||
`contact-test.html?berater-id=max-mustermann`. Ohne Parameter verwendet sie
|
`contact-test.html?berater-id=maren-hoffmann`. Ohne Parameter verwendet sie
|
||||||
`anna-mueller`. Nach Änderungen an der JSON den API-Server neu starten und für
|
`relindis-agethen`. Nach Änderungen an der JSON den API-Server neu starten und für
|
||||||
das Deployment das Frontend neu bauen.
|
das Deployment das Frontend neu bauen.
|
||||||
|
|
||||||
Das native `dialog` hält den Tastaturfokus im Popup. Escape und Schließen
|
Das native `dialog` hält den Tastaturfokus im Popup. Escape und Schließen
|
||||||
|
|
@ -90,7 +88,7 @@ Beide Endpunkte erwarten POST mit `Content-Type: application/json`, maximal
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"email": "kunde@example.test",
|
"email": "kunde@example.test",
|
||||||
"advisorId": "anna-mueller",
|
"advisorId": "relindis-agethen",
|
||||||
"reason": "coaching",
|
"reason": "coaching",
|
||||||
"simulationAccepted": true
|
"simulationAccepted": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { findAdvisor } from "../../shared/berater.js";
|
import { findAdvisor, advisorDisplayName } from "../../shared/berater.js";
|
||||||
import {
|
import {
|
||||||
contactReasons,
|
contactReasons,
|
||||||
contactNotice,
|
contactNotice,
|
||||||
|
|
@ -19,7 +19,7 @@ export function openContactDialog({
|
||||||
dialog.innerHTML = `
|
dialog.innerHTML = `
|
||||||
<button class="contact-close" type="button" aria-label="Kontaktfenster schließen">×</button>
|
<button class="contact-close" type="button" aria-label="Kontaktfenster schließen">×</button>
|
||||||
<p class="contact-eyebrow">DEIN NÄCHSTER SCHRITT</p>
|
<p class="contact-eyebrow">DEIN NÄCHSTER SCHRITT</p>
|
||||||
<h2 id="contact-title">Berater kontaktieren</h2>
|
<h2 id="contact-title"></h2>
|
||||||
<p class="contact-advisor"></p>
|
<p class="contact-advisor"></p>
|
||||||
<form class="contact-form">
|
<form class="contact-form">
|
||||||
<div><label for="contact-email">Deine Buchungs-E-Mail</label>
|
<div><label for="contact-email">Deine Buchungs-E-Mail</label>
|
||||||
|
|
@ -33,8 +33,10 @@ export function openContactDialog({
|
||||||
<p class="contact-status" role="status" aria-live="polite"></p>
|
<p class="contact-status" role="status" aria-live="polite"></p>
|
||||||
<button class="contact-submit" type="submit" disabled>Anfrage senden & Termin wählen</button>
|
<button class="contact-submit" type="submit" disabled>Anfrage senden & Termin wählen</button>
|
||||||
</form>`;
|
</form>`;
|
||||||
|
dialog.querySelector("#contact-title").textContent =
|
||||||
|
advisorDisplayName(advisor) || "Berater nicht gefunden";
|
||||||
dialog.querySelector(".contact-advisor").textContent = advisor
|
dialog.querySelector(".contact-advisor").textContent = advisor
|
||||||
? `Deine Anfrage an ${advisor.name}`
|
? "Sende deine Kontaktanfrage."
|
||||||
: "Die Berater-ID fehlt oder ist unbekannt. Bitte öffne das Fenster über ein Beraterprofil.";
|
: "Die Berater-ID fehlt oder ist unbekannt. Bitte öffne das Fenster über ein Beraterprofil.";
|
||||||
dialog.querySelector(".contact-consent span").textContent = contactNotice;
|
dialog.querySelector(".contact-consent span").textContent = contactNotice;
|
||||||
const form = dialog.querySelector("form");
|
const form = dialog.querySelector("form");
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { openContactDialog } from "./contact-dialog.js";
|
import { openContactDialog } from "./contact-dialog.js";
|
||||||
import { findAdvisor } from "../../shared/berater.js";
|
import { findAdvisor, advisorDisplayName } from "../../shared/berater.js";
|
||||||
import "./contact-test.css";
|
import "./contact-test.css";
|
||||||
|
|
||||||
const advisorId =
|
const advisorId =
|
||||||
new URLSearchParams(location.search).get("berater-id") ?? "anna-mueller";
|
new URLSearchParams(location.search).get("berater-id") ??
|
||||||
|
"relindis-agethen";
|
||||||
const advisor = findAdvisor(advisorId);
|
const advisor = findAdvisor(advisorId);
|
||||||
document.querySelector("#advisor-name").textContent = advisor
|
document.querySelector("#advisor-name").textContent = advisor
|
||||||
? `Dein Berater: ${advisor.name}`
|
? `Dein Berater: ${advisorDisplayName(advisor)}`
|
||||||
: "Unbekannte Berater-ID";
|
: "Unbekannte Berater-ID";
|
||||||
document
|
document
|
||||||
.querySelector("#open-contact")
|
.querySelector("#open-contact")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { advisorDisplayName } from "../../shared/berater.js";
|
||||||
import { createMailTransport } from "./email-service.js";
|
import { createMailTransport } from "./email-service.js";
|
||||||
import { BookingError } from "./booking-service.js";
|
import { BookingError } from "./booking-service.js";
|
||||||
import { contactNotice } from "../../shared/contact.js";
|
import { contactNotice } from "../../shared/contact.js";
|
||||||
|
|
@ -17,11 +18,11 @@ export function createContactEmailService(makeTransport) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
to: email,
|
to: email,
|
||||||
text: `Deine Kontaktanfrage an ${advisor.name} ist in der Simulation eingegangen.`,
|
text: `Deine Kontaktanfrage an ${advisorDisplayName(advisor)} ist in der Simulation eingegangen.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: advisor.email,
|
to: advisor.email,
|
||||||
text: `Hallo ${advisor.name}, eine simulierte Kontaktanfrage von ${email} ist eingegangen.`,
|
text: `Hallo ${advisorDisplayName(advisor)}, eine simulierte Kontaktanfrage von ${email} ist eingegangen.`,
|
||||||
},
|
},
|
||||||
].map(async ({ to, text }) => {
|
].map(async ({ to, text }) => {
|
||||||
const result = await transport.sendMail({
|
const result = await transport.sendMail({
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ function advisorDetails(advisorId) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
typeof advisor.name !== "string" ||
|
typeof advisor.vorname !== "string" ||
|
||||||
!advisor.name.trim() ||
|
!advisor.vorname.trim() ||
|
||||||
typeof advisor.email !== "string" ||
|
typeof advisor.email !== "string" ||
|
||||||
advisor.email.length > 254 ||
|
advisor.email.length > 254 ||
|
||||||
!emailPattern.test(advisor.email) ||
|
!emailPattern.test(advisor.email) ||
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@
|
||||||
},
|
},
|
||||||
"advisorId": {
|
"advisorId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "anna-mueller",
|
"example": "relindis-agethen",
|
||||||
"description": "ID aus src/shared/berater-daten.json. Name und E-Mail werden serverseitig ausschließlich dort nachgeschlagen."
|
"description": "ID aus src/shared/berater-daten.json. Name und E-Mail werden serverseitig ausschließlich dort nachgeschlagen."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@
|
||||||
"Leistungssport-Perspektive",
|
"Leistungssport-Perspektive",
|
||||||
"Wettkampforientierte Entwicklung"
|
"Wettkampforientierte Entwicklung"
|
||||||
],
|
],
|
||||||
"bildquelle": ""
|
"bildquelle": "",
|
||||||
|
"vorname": "Relindis",
|
||||||
|
"nachname": "Agethen",
|
||||||
|
"spitzname": "Lilli"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "maren-hoffmann",
|
"id": "maren-hoffmann",
|
||||||
|
|
@ -33,6 +36,9 @@
|
||||||
"Wettkampfverpflegung",
|
"Wettkampfverpflegung",
|
||||||
"Alltagstaugliche Ernährungsroutinen"
|
"Alltagstaugliche Ernährungsroutinen"
|
||||||
],
|
],
|
||||||
"bildquelle": ""
|
"bildquelle": "",
|
||||||
|
"vorname": "Maren",
|
||||||
|
"nachname": "Hoffmann",
|
||||||
|
"spitzname": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,7 @@ export function findAdvisor(advisorId) {
|
||||||
? berater.find((entry) => entry.id === advisorId)
|
? berater.find((entry) => entry.id === advisorId)
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function advisorDisplayName(advisor) {
|
||||||
|
return advisor?.spitzname?.trim() || advisor?.vorname?.trim() || "";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto";
|
||||||
import { contactBookingUrl } from "../../shared/contact.js";
|
import { contactBookingUrl } from "../../shared/contact.js";
|
||||||
|
|
||||||
async function open(page) {
|
async function open(page) {
|
||||||
await page.goto("/contact-test.html?berater-id=max-mustermann");
|
await page.goto("/contact-test.html?berater-id=maren-hoffmann");
|
||||||
await page
|
await page
|
||||||
.getByRole("button", { name: "Berater kontaktieren", exact: true })
|
.getByRole("button", { name: "Berater kontaktieren", exact: true })
|
||||||
.click();
|
.click();
|
||||||
|
|
@ -20,7 +20,7 @@ test("Popup prüft E-Mail beim Absenden und leitet nur bekannte Kunden weiter",
|
||||||
});
|
});
|
||||||
expect(booking.ok()).toBeTruthy();
|
expect(booking.ok()).toBeTruthy();
|
||||||
await open(page);
|
await open(page);
|
||||||
await expect(page.getByRole("dialog")).toContainText("Max Mustermann");
|
await expect(page.getByRole("dialog")).toContainText("Maren");
|
||||||
const submit = page.getByRole("button", { name: "Anfrage senden" });
|
const submit = page.getByRole("button", { name: "Anfrage senden" });
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole("button", { name: "E-Mail prüfen" }),
|
page.getByRole("button", { name: "E-Mail prüfen" }),
|
||||||
|
|
@ -42,7 +42,7 @@ test("Popup prüft E-Mail beim Absenden und leitet nur bekannte Kunden weiter",
|
||||||
request.url().endsWith("/api/contact"),
|
request.url().endsWith("/api/contact"),
|
||||||
);
|
);
|
||||||
await submit.click();
|
await submit.click();
|
||||||
expect((await sentRequest).postDataJSON().advisorId).toBe("max-mustermann");
|
expect((await sentRequest).postDataJSON().advisorId).toBe("maren-hoffmann");
|
||||||
await expect(page).toHaveURL(contactBookingUrl);
|
await expect(page).toHaveURL(contactBookingUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ async function fixture(t, options = {}) {
|
||||||
}
|
}
|
||||||
const input = {
|
const input = {
|
||||||
email: "kunde@example.test",
|
email: "kunde@example.test",
|
||||||
advisorId: "anna-mueller",
|
advisorId: "relindis-agethen",
|
||||||
reason: "coaching",
|
reason: "coaching",
|
||||||
simulationAccepted: true,
|
simulationAccepted: true,
|
||||||
};
|
};
|
||||||
|
|
@ -65,14 +65,14 @@ test("CSV-Abgleich normalisiert E-Mail; Versand erhält Berater und festen Redir
|
||||||
assert.equal(result.status, 201);
|
assert.equal(result.status, 201);
|
||||||
assert.equal(result.body.redirectUrl, contactBookingUrl);
|
assert.equal(result.body.redirectUrl, contactBookingUrl);
|
||||||
assert.equal(result.body.simulated, true);
|
assert.equal(result.body.simulated, true);
|
||||||
assert.equal(f.sent[0].advisor.email, "anna.mueller@tri-hub.de");
|
assert.equal(f.sent[0].advisor.email, "relindis.agethen@tri-hub.de");
|
||||||
assert.equal(f.sent[0].advisor.name, "Anna Müller");
|
assert.equal(f.sent[0].advisor.vorname, "Relindis");
|
||||||
const second = await f.post("/api/contact", {
|
const second = await f.post("/api/contact", {
|
||||||
...input,
|
...input,
|
||||||
advisorId: "max-mustermann",
|
advisorId: "maren-hoffmann",
|
||||||
});
|
});
|
||||||
assert.equal(second.status, 201);
|
assert.equal(second.status, 201);
|
||||||
assert.equal(f.sent[1].advisor.email, "max.mustermann@tri-hub.de");
|
assert.equal(f.sent[1].advisor.email, "maren.hoffmann@tri-hub.de");
|
||||||
assert.deepEqual(await f.storage.readAll(), before);
|
assert.deepEqual(await f.storage.readAll(), before);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -147,7 +147,7 @@ test("Zwei getrennte simulierte Bestätigungen; Teilfehler verhindern Erfolg", a
|
||||||
assert.equal((await f.post("/api/contact", input)).status, 201);
|
assert.equal((await f.post("/api/contact", input)).status, 201);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
messages.map((m) => m.to),
|
messages.map((m) => m.to),
|
||||||
[input.email, "anna.mueller@tri-hub.de"],
|
[input.email, "relindis.agethen@tri-hub.de"],
|
||||||
);
|
);
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
assert.match(message.text, /Simulation/);
|
assert.match(message.text, /Simulation/);
|
||||||
|
|
@ -163,3 +163,27 @@ test("Zwei getrennte simulierte Bestätigungen; Teilfehler verhindern Erfolg", a
|
||||||
assert.equal(result.status, 502);
|
assert.equal(result.status, 502);
|
||||||
assert.equal(result.body.redirectUrl, undefined);
|
assert.equal(result.body.redirectUrl, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Anzeigename bevorzugt Spitzname, sonst Vorname, ohne Nachnamen", async () => {
|
||||||
|
const { advisorDisplayName } = await import("../shared/berater.js");
|
||||||
|
assert.equal(
|
||||||
|
advisorDisplayName({
|
||||||
|
spitzname: " Lilli ",
|
||||||
|
vorname: "Relindis",
|
||||||
|
nachname: "Agethen",
|
||||||
|
}),
|
||||||
|
"Lilli",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
advisorDisplayName({
|
||||||
|
spitzname: " ",
|
||||||
|
vorname: "Maren",
|
||||||
|
nachname: "Hoffmann",
|
||||||
|
}),
|
||||||
|
"Maren",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
advisorDisplayName({ vorname: "Maren", nachname: "Hoffmann" }),
|
||||||
|
"Maren",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue