diff --git a/app/Support/SettingsMailTemplates.php b/app/Support/SettingsMailTemplates.php index 71f0ec692..6d335a44c 100644 --- a/app/Support/SettingsMailTemplates.php +++ b/app/Support/SettingsMailTemplates.php @@ -130,7 +130,7 @@ public static function all(?string $locale = null): array ], 'loan_expiring_warning' => [ 'label' => __('Promemoria scadenza'), - 'description' => __("Promemoria agli utenti tre giorni prima della scadenza del prestito."), + 'description' => __("Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate."), 'subject' => '⚠️ Il tuo prestito sta per scadere', 'placeholders' => ['utente_nome', 'libro_titolo', 'data_scadenza', 'giorni_rimasti'], 'body' => <<<'HTML' @@ -234,7 +234,7 @@ public static function all(?string $locale = null): array ], 'loan_approved' => [ 'label' => __('Prestito approvato'), - 'description' => __("Inviata all'utente quando un amministratore approva una richiesta di prestito."), + 'description' => __("Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro»."), 'subject' => '✅ La tua richiesta di prestito è stata approvata!', 'placeholders' => ['utente_nome', 'libro_titolo', 'data_inizio', 'data_fine', 'giorni_prestito', 'pickup_instructions', 'sezione_calendario'], 'body' => <<<'HTML' @@ -364,7 +364,7 @@ public static function all(?string $locale = null): array ], 'loan_pickup_ready' => [ 'label' => __('Pronto per il ritiro'), - 'description' => __("Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro."), + 'description' => __("Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato."), 'subject' => '📦 Libro pronto per il ritiro!', 'placeholders' => ['utente_nome', 'libro_titolo', 'data_inizio', 'data_fine', 'giorni_prestito', 'scadenza_ritiro', 'pickup_instructions', 'sezione_calendario'], 'body' => <<<'HTML' diff --git a/locale/da_DK.json b/locale/da_DK.json index 07719c1ac..c9ff8b2dd 100644 --- a/locale/da_DK.json +++ b/locale/da_DK.json @@ -2683,11 +2683,11 @@ "Inviata ai nuovi utenti per impostare la password del loro account.": "Sendes til nye brugere for at indstille deres kontoadgangskode.", "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.": "Sendes til brugeren ved afslutningen af registreringen for at bekræfte modtagelsen og afventningen af godkendelse.", "Inviata all'utente quando un amministratore approva l'account.": "Sendes til brugeren, når en administrator godkender kontoen.", - "Inviata all'utente quando un amministratore approva una richiesta di prestito.": "Sendes til brugeren, når en administrator godkender en låneanmodning.", + "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».": "Sendes, når en administrator godkender eller opretter et lån, der starter på en fremtidig dato. For et lån, der starter med det samme, sendes «Klar til afhentning» i stedet.", "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.": "Sendes til brugeren, når en administrator afviser en låneanmodning.", "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.": "Sendes, når tiden til at afhente en bog er udløbet, og udlånet er blevet annulleret.", "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.": "Sendes, når en reserveret bog bliver tilgængelig og konverteres til et afventende udlån.", - "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.": "Sendes, når et udlån er blevet godkendt, og bogen er klar til afhentning.", + "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.": "Sendes, når bogen er klar til afhentning: når en administrator godkender et lån, der starter med det samme, eller opretter et lån til afhentning, og på startdagen for et planlagt lån.", "Inviata quando un ritiro viene annullato dall'amministratore.": "Sendes, når en afhentning annulleres af administratoren.", "Inviata quando un utente viene invitato come amministratore.": "Sendes, når en bruger inviteres som administrator.", "Inviato": "Sendt", @@ -3823,7 +3823,7 @@ "Programmati": "Planlagte", "Programmato": "Planlagt", "Project Gutenberg": "Project Gutenberg", - "Promemoria agli utenti tre giorni prima della scadenza del prestito.": "Påmindelse til brugere tre dage før lånets udløbsdato.", + "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.": "Påmindelse til brugere før lånets udløb, så mange dage i forvejen som angivet i de avancerede indstillinger.", "Promemoria scadenza": "Udløbspåmindelse", "Pronti per il Ritiro": "Klar til afhentning", "Pronti per il prestito": "Klar til udlån", diff --git a/locale/de_DE.json b/locale/de_DE.json index e025f1102..60433b038 100644 --- a/locale/de_DE.json +++ b/locale/de_DE.json @@ -2683,11 +2683,11 @@ "Inviata ai nuovi utenti per impostare la password del loro account.": "Wird an neue Benutzer gesendet, um das Passwort für ihr Konto festzulegen.", "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.": "Wird nach der Registrierung an den Benutzer gesendet, um den Eingang zu bestätigen und auf die Genehmigung hinzuweisen.", "Inviata all'utente quando un amministratore approva l'account.": "Wird an den Benutzer gesendet, wenn ein Administrator das Konto genehmigt.", - "Inviata all'utente quando un amministratore approva una richiesta di prestito.": "Wird an den Benutzer gesendet, wenn ein Administrator eine Ausleihanfrage genehmigt.", + "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».": "Wird gesendet, wenn ein Administrator eine Ausleihe genehmigt oder anlegt, die an einem späteren Datum beginnt. Bei einer Ausleihe, die sofort beginnt, wird stattdessen «Zur Abholung bereit» gesendet.", "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.": "Wird an den Benutzer gesendet, wenn ein Administrator eine Ausleihanfrage ablehnt.", "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.": "Wird gesendet, wenn die Abholfrist abgelaufen ist und die Ausleihe storniert wurde.", "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.": "Wird gesendet, wenn ein vorgemerktes Buch verfügbar wird und in eine ausstehende Ausleihe umgewandelt wird.", - "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.": "Wird gesendet, wenn eine Ausleihe genehmigt wurde und das Buch zur Abholung bereit ist.", + "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.": "Wird gesendet, wenn das Buch zur Abholung bereitliegt: wenn ein Administrator eine sofort beginnende Ausleihe genehmigt oder eine abzuholende Ausleihe anlegt, und am Starttag einer geplanten Ausleihe.", "Inviata quando un ritiro viene annullato dall'amministratore.": "Wird gesendet, wenn eine Abholung vom Administrator storniert wird.", "Inviata quando un utente viene invitato come amministratore.": "Wird gesendet, wenn ein Benutzer als Administrator eingeladen wird.", "Inviato": "Gesendet", @@ -3823,7 +3823,7 @@ "Programmati": "Geplant", "Programmato": "Geplant", "Project Gutenberg": "Project Gutenberg", - "Promemoria agli utenti tre giorni prima della scadenza del prestito.": "Erinnerung an die Benutzer drei Tage vor dem Fälligkeitsdatum der Ausleihe.", + "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.": "Erinnerung an Benutzer vor dem Ablauf der Ausleihe, so viele Tage im Voraus wie in den erweiterten Einstellungen festgelegt.", "Promemoria scadenza": "Fälligkeitserinnerung", "Pronti per il Ritiro": "Zur Abholung bereit", "Pronti per il prestito": "Zur Ausleihe bereit", diff --git a/locale/en_US.json b/locale/en_US.json index ffcce127e..114f36767 100644 --- a/locale/en_US.json +++ b/locale/en_US.json @@ -2683,11 +2683,11 @@ "Inviata ai nuovi utenti per impostare la password del loro account.": "Sent to new users to set up their account password.", "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.": "Sent to the user after registration to confirm receipt and waiting for approval.", "Inviata all'utente quando un amministratore approva l'account.": "Sent to the user when an administrator approves the account.", - "Inviata all'utente quando un amministratore approva una richiesta di prestito.": "Sent to the user when an administrator approves a loan request.", + "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».": "Sent when an administrator approves or creates a loan that starts on a future date. For a loan that starts right away, «Ready for Pickup» is sent instead.", "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.": "Sent to the user when an administrator rejects a loan request.", "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.": "Sent when the pickup deadline has passed and the loan has been cancelled.", "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.": "Sent when a reserved book becomes available and is converted to a pending loan.", - "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.": "Sent when a loan has been approved and the book is ready for pickup.", + "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.": "Sent when the book is ready for pickup: when an administrator approves a loan that starts right away or creates one to be picked up, and on the start day of a scheduled loan.", "Inviata quando un ritiro viene annullato dall'amministratore.": "Sent when a pickup is cancelled by an administrator.", "Inviata quando un utente viene invitato come amministratore.": "Sent when a user is invited as an administrator.", "Inviato": "Sent", @@ -3823,7 +3823,7 @@ "Programmati": "Scheduled", "Programmato": "Scheduled", "Project Gutenberg": "Project Gutenberg", - "Promemoria agli utenti tre giorni prima della scadenza del prestito.": "Reminder to users three days before the loan due date.", + "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.": "Reminder sent to users before a loan is due, as many days ahead as set in the advanced settings.", "Promemoria scadenza": "Due-date reminder", "Pronti per il Ritiro": "Ready for Pickup", "Pronti per il prestito": "Ready for loan", diff --git a/locale/fr_FR.json b/locale/fr_FR.json index 9236cf5dc..de529e362 100644 --- a/locale/fr_FR.json +++ b/locale/fr_FR.json @@ -2683,11 +2683,11 @@ "Inviata ai nuovi utenti per impostare la password del loro account.": "Envoyée aux nouveaux utilisateurs pour définir le mot de passe de leur compte.", "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.": "Envoyée à l'utilisateur à la fin de l'inscription pour confirmer la réception et l'attente d'approbation.", "Inviata all'utente quando un amministratore approva l'account.": "Envoyée à l'utilisateur lorsqu'un administrateur approuve le compte.", - "Inviata all'utente quando un amministratore approva una richiesta di prestito.": "Envoyée à l'utilisateur lorsqu'un administrateur approuve une demande d'emprunt.", + "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».": "Envoyé lorsqu'un administrateur approuve ou crée un emprunt qui commence à une date future. Pour un emprunt qui commence tout de suite, c'est «Prêt à récupérer» qui est envoyé.", "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.": "Envoyée à l'utilisateur lorsqu'un administrateur refuse une demande d'emprunt.", "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.": "Envoyée lorsque le délai de récupération est dépassé et que l'emprunt a été annulé.", "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.": "Envoyée lorsqu'un livre réservé devient disponible et est converti en emprunt en attente.", - "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.": "Envoyée lorsqu'un emprunt a été approuvé et que le livre est prêt à être récupéré.", + "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.": "Envoyé lorsque le livre est prêt à être retiré : quand un administrateur approuve un emprunt qui commence tout de suite ou en crée un à retirer, et le jour de début d'un emprunt programmé.", "Inviata quando un ritiro viene annullato dall'amministratore.": "Envoyée lorsqu'une récupération est annulée par l'administrateur.", "Inviata quando un utente viene invitato come amministratore.": "Envoyée lorsqu'un utilisateur est invité en tant qu'administrateur.", "Inviato": "Envoyé", @@ -3823,7 +3823,7 @@ "Programmati": "Programmés", "Programmato": "Programmé", "Project Gutenberg": "Project Gutenberg", - "Promemoria agli utenti tre giorni prima della scadenza del prestito.": "Rappel aux utilisateurs trois jours avant la date d'échéance de l'emprunt.", + "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.": "Rappel envoyé aux utilisateurs avant l'échéance de l'emprunt, avec le nombre de jours de préavis défini dans les paramètres avancés.", "Promemoria scadenza": "Rappel d'échéance", "Pronti per il Ritiro": "Prêts à récupérer", "Pronti per il prestito": "Prêts à l'emprunt", diff --git a/locale/it_IT.json b/locale/it_IT.json index 7999fda19..1c73a5189 100644 --- a/locale/it_IT.json +++ b/locale/it_IT.json @@ -2683,11 +2683,11 @@ "Inviata ai nuovi utenti per impostare la password del loro account.": "Inviata ai nuovi utenti per impostare la password del loro account.", "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.": "Inviata all'utente al termine della registrazione per confermare la ricezione e l'attesa di approvazione.", "Inviata all'utente quando un amministratore approva l'account.": "Inviata all'utente quando un amministratore approva l'account.", - "Inviata all'utente quando un amministratore approva una richiesta di prestito.": "Inviata all'utente quando un amministratore approva una richiesta di prestito.", + "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».": "Inviata quando un amministratore approva o crea un prestito che inizia in una data futura. Per un prestito che inizia subito parte invece «Pronto per il ritiro».", "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.": "Inviata all'utente quando un amministratore rifiuta una richiesta di prestito.", "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.": "Inviata quando il tempo per ritirare un libro è scaduto e il prestito è stato annullato.", "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.": "Inviata quando un libro prenotato diventa disponibile e viene convertito in prestito pendente.", - "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.": "Inviata quando un prestito è stato approvato e il libro è pronto per il ritiro.", + "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.": "Inviata quando il libro è pronto per il ritiro: quando un amministratore approva un prestito che inizia subito o ne crea uno da ritirare, e il giorno d'inizio di un prestito programmato.", "Inviata quando un ritiro viene annullato dall'amministratore.": "Inviata quando un ritiro viene annullato dall'amministratore.", "Inviata quando un utente viene invitato come amministratore.": "Inviata quando un utente viene invitato come amministratore.", "Inviato": "Inviato", @@ -3823,7 +3823,7 @@ "Programmati": "Programmati", "Programmato": "Programmato", "Project Gutenberg": "Project Gutenberg", - "Promemoria agli utenti tre giorni prima della scadenza del prestito.": "Promemoria agli utenti tre giorni prima della scadenza del prestito.", + "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.": "Promemoria agli utenti prima della scadenza del prestito, con i giorni di preavviso impostati nelle impostazioni avanzate.", "Promemoria scadenza": "Promemoria scadenza", "Pronti per il Ritiro": "Pronti per il Ritiro", "Pronti per il prestito": "Pronti per il prestito", diff --git a/tests/mail-template-approval.spec.js b/tests/mail-template-approval.spec.js new file mode 100644 index 000000000..c5442612c --- /dev/null +++ b/tests/mail-template-approval.spec.js @@ -0,0 +1,121 @@ +// @ts-check +// An admin who edits a mail template must be able to tell which template a reader +// receives. Approving a loan that starts today sends «Pronto per il ritiro» +// (loan_pickup_ready), not «Prestito approvato» (loan_approved), which only goes +// out for a loan that starts on a later date. The editor said otherwise, so a +// library edited loan_approved and kept receiving the untouched pickup mail. +// Both templates are edited through the settings form, the loan is approved from +// the pending list, and the mail is read from Mailpit. +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const MAILPIT_API = process.env.MAILPIT_API || 'http://localhost:8025/api/v1'; +const RUN = Date.now().toString(36); + +/** + * Runs one statement against the test database and returns its tab-separated output, + * trimmed. CI connects over TCP (E2E_DB_HOST/E2E_DB_PORT, no socket); a local run may + * use a socket. + * @param {string} sql + * @returns {string} + */ +function db(sql) { + const args = []; + if (process.env.E2E_DB_HOST) { + args.push('-h', process.env.E2E_DB_HOST); + if (process.env.E2E_DB_PORT) args.push('-P', process.env.E2E_DB_PORT); + } else if (process.env.E2E_DB_SOCKET) { + args.push('-S', process.env.E2E_DB_SOCKET); + } + args.push('-u', process.env.E2E_DB_USER || '', process.env.E2E_DB_NAME || '', '-N', '-B', '-e', sql); + return execFileSync('mysql', args, { + encoding: 'utf-8', env: { ...process.env, MYSQL_PWD: process.env.E2E_DB_PASS }, + }).trim(); +} + +test.skip(!process.env.E2E_ADMIN_EMAIL || !process.env.E2E_DB_USER || !process.env.E2E_DB_NAME, 'E2E credentials not configured'); + +test('approving a loan that starts today sends the edited «Pronto per il ritiro», as the editor says', async ({ page }) => { + const mailpitUp = await fetch(`${MAILPIT_API}/info`).then((r) => r.ok).catch(() => false); + test.skip(!mailpitUp, 'Mailpit is not running on :8025'); + test.setTimeout(120000); + // Whole rows, kept in the database: the restore must also drop the keys the + // INSERT below adds, and keep NULLs and backslashes exactly as they were. + db(`DROP TABLE IF EXISTS zz_mail_backup_${RUN}`); + db(`CREATE TABLE zz_mail_backup_${RUN} AS SELECT * FROM system_settings WHERE category='email'`); + db(`DROP TABLE IF EXISTS zz_tpl_backup_${RUN}`); + db(`CREATE TABLE zz_tpl_backup_${RUN} AS SELECT * FROM email_templates WHERE locale='it_IT' AND name IN ('loan_approved','loan_pickup_ready')`); + const userEmail = `probe-${RUN}@test.local`; + let userId = '', bookId = '', loanId = ''; + try { + db(`INSERT INTO system_settings (category, setting_key, setting_value) VALUES + ('email','type','smtp'),('email','driver_mode','smtp'),('email','smtp_host','localhost'),('email','smtp_port','1025'), + ('email','smtp_security',''),('email','smtp_username',''),('email','smtp_password',''),('email','from_email','test@biblioteca.local'),('email','from_name','Probe') + ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)`); + await page.request.get(`${BASE}/_e2e/flush-cache`); + + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', process.env.E2E_ADMIN_EMAIL || ''); + await page.fill('input[name="password"]', process.env.E2E_ADMIN_PASS || ''); + await page.locator('button[type="submit"]').click(); + await page.waitForURL(/admin|\/$/, { timeout: 15000 }).catch(() => {}); + + for (const name of ['loan_approved', 'loan_pickup_ready']) { + await page.goto(`${BASE}/admin/settings?tab=templates&template_locale=it_IT&template=${name}`); + const form = page.locator(`form:has(textarea[data-template="${name}"])`); + await form.scrollIntoViewIfNeeded(); + await form.locator('input[name="subject"]').fill(`SUBJ-${name}-${RUN}`); + const frame = form.frameLocator('iframe').first(); + await frame.locator('body').click(); + await page.keyboard.press('ControlOrMeta+End'); + await page.keyboard.type(` BODY-${name}-${RUN}`); + await form.getByRole('button', { name: /Salva template/ }).click(); + await page.waitForLoadState('domcontentloaded'); + expect(db(`SELECT LOCATE('BODY-${name}-${RUN}', body) > 0 FROM email_templates WHERE name='${name}' AND locale='it_IT'`)).toBe('1'); + } + + await page.goto(`${BASE}/admin/settings?tab=templates&template_locale=it_IT`); + const approvedCard = page.locator('div.overflow-hidden:has(textarea[data-template="loan_approved"])').last(); + await expect(approvedCard).toContainText('approva o crea un prestito che inizia in una data futura'); + await expect(approvedCard).toContainText('Pronto per il ritiro'); + + db(`INSERT INTO utenti (nome, cognome, email, password, codice_tessera, stato, tipo_utente, email_verificata, locale) VALUES ('Probe','Reader','${userEmail}','x','PRB${RUN}','attivo','standard',1,'it_IT')`); + userId = db(`SELECT id FROM utenti WHERE email='${userEmail}'`); + bookId = db(`INSERT INTO libri (titolo, copie_totali, copie_disponibili, stato) VALUES ('Probe Book ${RUN}',1,1,'disponibile'); SELECT LAST_INSERT_ID()`); + db(`INSERT INTO copie (libro_id, numero_inventario, stato) VALUES (${bookId}, 'PRB-${RUN}', 'disponibile')`); + loanId = db(`INSERT INTO prestiti (libro_id, utente_id, stato, data_prestito, data_scadenza, attivo, created_at) VALUES (${bookId}, ${userId}, 'pendente', CURDATE(), CURDATE() + INTERVAL 30 DAY, 1, NOW()); SELECT LAST_INSERT_ID()`); + await fetch(`${MAILPIT_API}/messages`, { method: 'DELETE' }); + + await page.goto(`${BASE}/admin/loans/pending`); + await page.locator(`.approve-btn[data-loan-id="${loanId}"]`).click(); + await page.locator('.swal2-confirm').click(); + await expect.poll(() => db(`SELECT stato FROM prestiti WHERE id=${loanId}`), { timeout: 10000 }).toBe('da_ritirare'); + + let messages = []; + for (let i = 0; i < 20 && messages.length === 0; i++) { + const list = await (await fetch(`${MAILPIT_API}/search?query=${encodeURIComponent('to:' + userEmail)}`)).json(); + messages = list.messages || []; + if (messages.length === 0) await page.waitForTimeout(500); + } + expect(messages).toHaveLength(1); + const mail = await (await fetch(`${MAILPIT_API}/message/${messages[0].ID}`)).json(); + expect(mail.Subject).toBe(`SUBJ-loan_pickup_ready-${RUN}`); + expect(mail.HTML).toContain(`BODY-loan_pickup_ready-${RUN}`); + expect(mail.HTML).not.toContain(`BODY-loan_approved-${RUN}`); + } finally { + if (loanId) db(`DELETE FROM prestiti WHERE id=${loanId}`); + if (bookId) { db(`DELETE FROM copie WHERE libro_id=${bookId}`); db(`DELETE FROM libri WHERE id=${bookId}`); } + if (userId) db(`DELETE FROM utenti WHERE id=${userId}`); + // One transaction in one mysql call: on any error the client stops and disconnects + // without COMMIT, so nothing is half restored and the backups are still there. + db(`START TRANSACTION; + DELETE FROM system_settings WHERE category='email'; + INSERT INTO system_settings SELECT * FROM zz_mail_backup_${RUN}; + DELETE FROM email_templates WHERE locale='it_IT' AND name IN ('loan_approved','loan_pickup_ready'); + INSERT INTO email_templates SELECT * FROM zz_tpl_backup_${RUN}; + COMMIT`); + db(`DROP TABLE zz_mail_backup_${RUN}, zz_tpl_backup_${RUN}`); + await page.request.get(`${BASE}/_e2e/flush-cache`); + } +});