Workers P4 Endprüfung W5: „Freigeben & senden“ ist ohne gültige Vorschau-Adresse gesperrt

Bei einer http- oder fremden Adresse zeigte die Zeile „Keine gültige Vorschau-Adresse hinterlegt.“ statt
des Links, ließ aber das Freigeben zu: der Mensch gab eine Vorschau frei, die er nicht ansehen kann. Der
Knopf ist jetzt gesperrt (auch approve() prüft previewHref), der Hinweis nennt Grund und Ausweg („Entwurf
prüfen oder verwerfen“), Verwerfen bleibt frei. Das Zeilenmodell (canApprovePreview) bleibt unverändert.
Das Sperren schließt fail-closed: weichen baseUrl des Dienstes und PREVIEW_HOST in WOMS einmal ab, ist
Freigeben gesperrt, bis WOMS angepasst ist. Prüfstand prüft den gesperrten Knopf.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 23:10:23 +00:00
1 parent 871b87fac0
commit c7061837fb
3 files changed
+42 -5

No files matched your search

+4
View File
@@ -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')
+5 -3
View File
@@ -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
? <a className="btn" href={row.previewHref} target="_blank" rel="noopener noreferrer" aria-label={rowName('Vorschau ansehen', row.title)}>Vorschau ansehen</a>
: <span className="wk-hint">Keine gültige Vorschau-Adresse hinterlegt.</span>}
<Button type="button" variant="primary" onClick={approve} disabled={busy}>
: <span className="wk-hint">Keine gültige Vorschau-Adresse hinterlegt. Freigabe nicht möglich – bitte den Entwurf prüfen oder verwerfen.</span>}
<Button type="button" variant="primary" onClick={approve} disabled={busy || !row.previewHref}>
{confirming === 'approve' ? 'Wirklich freigeben?' : 'Freigeben & senden'}
</Button>
</>
+33 -2
View File
@@ -578,11 +578,16 @@ test('PultList (gerendert, P4-T32): Aktionen je Art – Antworten, Freigeben, Ba
assert.ok(html.includes('Freigeben &amp; 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('<b>prüfen</b>'))
// 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, /<button[^>]*disabled[^>]*>Freigeben &amp; 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</a>'), 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 &amp; 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, /<button[^>]*disabled[^>]*>Freigeben &amp; senden<\/button>/, sourceUrl)
assert.ok(buttonOf(html, 'Verwerfen')); assert.doesNotMatch(html, /<button[^>]*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))