diff --git a/scripts/workers-harness/shot.cjs b/scripts/workers-harness/shot.cjs index 97136d0..42f9e8b 100644 --- a/scripts/workers-harness/shot.cjs +++ b/scripts/workers-harness/shot.cjs @@ -1229,6 +1229,10 @@ async function vorschau(browser, base) { check('vorschau: Vorschau mit http-Adresse wird kein Link (Hinweis „Keine gültige Vorschau-Adresse hinterlegt.“)', !fakeLinks.some((href) => /entwurf\.project\.webklar\.com/.test(href || '')) && (await textOf(falsch.locator('.wk-pult-actions'))).includes(NO_PREVIEW_URL), fakeLinks) const falschApprove = falsch.getByRole('button', { name: 'Freigeben & senden', exact: true }) metrics.freigabeOhneVorschauLink = { knopfDa: (await falschApprove.count()) === 1, knopfFrei: (await falschApprove.count()) === 1 && (await falschApprove.isEnabled()) } + // Endprüfung W5: eine Vorschau ohne gültige Adresse lässt sich nicht freigeben (der Mensch könnte sie nicht ansehen); der Hinweis nennt den Ausweg, Verwerfen bleibt frei + check('vorschau: ohne gültige Vorschau-Adresse ist „Freigeben & senden“ gesperrt, der Hinweis nennt den Grund, „Verwerfen“ bleibt frei', + metrics.freigabeOhneVorschauLink.knopfDa && !metrics.freigabeOhneVorschauLink.knopfFrei && (await textOf(falsch.locator('.wk-pult-actions'))).includes('Freigabe nicht möglich') + && (await falsch.getByRole('button', { name: 'Verwerfen', exact: true }).isEnabled()), metrics.freigabeOhneVorschauLink) await falsch.scrollIntoViewIfNeeded() await shoot(page, 'vorschau-falsche-adresse.png') diff --git a/src/components/workers/PultList.jsx b/src/components/workers/PultList.jsx index 5512b74..3d0ce6b 100644 --- a/src/components/workers/PultList.jsx +++ b/src/components/workers/PultList.jsx @@ -147,8 +147,10 @@ function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFin run(() => api.pultAction(row.id, 'discard', {}), 'verworfen.', true) } - // Vorschau freigeben: der erste Klick fragt nach, der zweite binnen 4 s reiht den Link an den Interessenten ein (Max sendet im Sendefenster) + // Vorschau freigeben: der erste Klick fragt nach, der zweite binnen 4 s reiht den Link an den Interessenten ein (Max sendet im Sendefenster). + // Nur mit gültiger Vorschau-Adresse (https, Vorschau-Server): was der Mensch nicht ansehen kann, gibt er nicht frei (Endprüfung W5). const approve = () => { + if (!row.previewHref) return // ohne gültige Adresse sieht der Mensch die Vorschau nicht: der Knopf ist gesperrt, das hier fängt jeden anderen Weg ab if (confirming !== 'approve') { setConfirming('approve') return @@ -282,8 +284,8 @@ function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFin <> {row.previewHref ? Vorschau ansehen - : Keine gültige Vorschau-Adresse hinterlegt.} - diff --git a/tests/unit/workersPultUi.test.mjs b/tests/unit/workersPultUi.test.mjs index e4abb03..d8b0f24 100644 --- a/tests/unit/workersPultUi.test.mjs +++ b/tests/unit/workersPultUi.test.mjs @@ -578,11 +578,16 @@ test('PultList (gerendert, P4-T32): Aktionen je Art – Antworten, Freigeben, Ba assert.ok(html.includes('Freigeben & senden')); assert.ok(buttonOf(html, 'Verwerfen')); assert.ok(html.includes('sendet die Vorschau nicht')) for (const t of ['Anzeige öffnen', 'Als Mensch antworten', 'Vorschau bauen', 'Wirklich freigeben?']) assert.ok(!html.includes(t), t) assert.ok(!buttonOf(html, 'Erledigt')); assert.ok(!html.includes('prüfen')) - // fremde, unsichere oder fehlende Adresse: kein Link, Hinweis statt Link, der Knopf zum Freigeben bleibt + // gültige Adresse: der Knopf zum Freigeben ist bedienbar + assert.doesNotMatch(html, /]*disabled[^>]*>Freigeben & senden<\/button>/) + // fremde, unsichere oder fehlende Adresse: kein Link, Hinweis statt Link, und der Knopf zum Freigeben ist gesperrt (Endprüfung W5: der Mensch soll keine + // Vorschau freigeben, die er nicht ansehen kann); der Hinweis nennt den Grund und den Ausweg for (const sourceUrl of ['https://fremd.example/AAA/', 'javascript:alert(1)', 'http://entwurf.project.webklar.com/AAA/', '']) { html = show([{ ...freigabe, sourceUrl }]) assert.ok(!html.includes('Vorschau ansehen'), sourceUrl); assert.ok(!html.includes('href="javascript:'), sourceUrl); assert.ok(!html.includes('fremd.example'), sourceUrl) - assert.ok(html.includes('Keine gültige Vorschau-Adresse hinterlegt.'), sourceUrl); assert.ok(html.includes('Freigeben & senden'), sourceUrl) + assert.ok(html.includes('Keine gültige Vorschau-Adresse hinterlegt. Freigabe nicht möglich – bitte den Entwurf prüfen oder verwerfen.'), sourceUrl) + assert.match(html, /]*disabled[^>]*>Freigeben & senden<\/button>/, sourceUrl) + assert.ok(buttonOf(html, 'Verwerfen')); assert.doesNotMatch(html, /]*disabled[^>]*>Verwerfen<\/button>/, sourceUrl) // Verwerfen bleibt frei } // freigegeben (erledigt) oder von einem Kollegen übernommen: nichts mehr freizugeben assert.ok(!show([{ ...freigabe, status: 'erledigt' }]).includes('Freigeben')) @@ -841,6 +846,32 @@ test('PultList (bedient, P4-T32): Vorschau freigeben – erster Klick fragt nach } finally { ui.end() } }) +// Endprüfung P4 (W5): ohne gültige Vorschau-Adresse lässt sich nicht freigeben (auch nicht über die Rückfrage), mit gültiger Adresse wie bisher +test('PultList (bedient, W5): „Freigeben & senden“ ist ohne gültige Vorschau-Adresse gesperrt – kein Klick, keine Rückfrage, keine Aktion', async () => { + const freigabe = { ...unsicher, $id: 'f1', kind: 'vorschau_freigabe', title: 'Vorschau prüfen', draft: 'Hier ist Ihre Vorschau.' } + for (const sourceUrl of ['http://entwurf.project.webklar.com/AAA/', 'https://fremd.example/AAA/', '']) { + const api = scriptedApi() + const ui = await startList([{ ...freigabe, sourceUrl }], api) + try { + assert.deepEqual(ui.disabled('Freigeben & senden'), [true], sourceUrl) + assert.ok(ui.text().includes('Keine gültige Vorschau-Adresse hinterlegt. Freigabe nicht möglich')); assert.ok(!ui.has('Vorschau ansehen')) + await ui.click('Freigeben & senden') + assert.ok(ui.has('Freigeben & senden')); assert.ok(!ui.has('Wirklich freigeben?')); assert.equal(api.calls.length, 0) + assert.deepEqual([...ui.timers.values()], []) + // Verwerfen bleibt möglich und sendet nichts: der Mensch kann den Eintrag schließen + assert.deepEqual(ui.disabled('Verwerfen'), [false]) + } finally { ui.end() } + } + // gültige Adresse: Freigeben ist frei und fragt wie bisher nach + const api = scriptedApi() + const ui = await startList([{ ...freigabe, sourceUrl: 'https://entwurf.project.webklar.com/AAA/' }], api) + try { + assert.deepEqual(ui.disabled('Freigeben & senden'), [false]); assert.ok(!ui.text().includes('Keine gültige Vorschau-Adresse')) + await ui.click('Freigeben & senden'); assert.ok(ui.has('Wirklich freigeben?')) + await ui.click('Wirklich freigeben?'); assert.deepEqual(api.calls, [['f1', 'approve-preview']]) + } finally { ui.end() } +}) + test('PultList (bedient, P4-T32): „Antwort des Kunden einfügen“ – leeres Feld mit Hinweis, Prüfung, Einfügen, doppelt, Fehler des Dienstes, Text bleibt beim Filterwechsel', async () => { const plattform = { ...unsicher, $id: 'k1', kind: 'plattform_antwort', status: 'in_arbeit', claimedBy: 'mensch:u2', title: 'Suche Webdesigner', draft: 'Claras Entwurf', sourceUrl: 'https://www.kleinanzeigen.de/s-anzeige/x/1-2-3', prospect: { $id: 'p1', name: 'Verein' } } const api = scriptedApi(Promise.resolve({ status: 'neu', messageId: 'm1', threadId: 't1' }), Promise.resolve({ status: 'doppelt', messageId: 'm1', threadId: 't1' }), failure('Dieser Kontakt ist gesperrt.', 409))