Workers P4 Endprüfung W2: „Vorschau bauen“ fragt vor dem Start nach und fehlt bei toten Interessenten
Der Knopf startete einen Opus-Lauf mit einem einzigen Klick (Handy: voll breit zwischen anderen Knöpfen), ohne dass sich der Bau in der Oberfläche zurücknehmen lässt. Wie Freigeben und Verwerfen fragt er jetzt zuerst nach („Wirklich bauen?“, 4 s) und zeigt dabei, dass ein Opus-Lauf startet und die Vorschau gebaut wird, als hätte der Interessent Ja gesagt (ohne „nur wenn Ja“: der Knopf gilt gerade für unklare Antworten). rowModel blendet den Knopf bei gesperrtem, verlorenem oder abgeschlossenem Interessenten (gesperrt, verloren, deal) aus; ohne Kurzfassung entscheidet weiter der Dienst. Prüfstand: die Fehler- und Fokus-Läufe klicken zweimal, Szenario bauen prüft Frage, Hinweis und Verfall. Doppeltipp-Schutz und Dienst-Härtung (bauLauf nur nach Ja) bleiben offen, siehe final-fix-report-woms.md. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1536a3bbc1
commit
871b87fac0
5 files changed
+99
-19
No files matched your search
@@ -1405,18 +1405,20 @@ async function bauen(browser, base) {
|
||||
// 1. Fehler vom Dienst
|
||||
await setPultFault(page, 'start-build', { status: 409, message: 'Für diesen Interessenten gibt es schon eine Vorschau.' })
|
||||
await build.click()
|
||||
await build.click()
|
||||
await uns.locator('.msg.err').waitFor()
|
||||
check('bauen: 409 ⇒ „Für diesen Interessenten gibt es schon eine Vorschau.“ in der Zeile (rot, role=alert), Zeile bleibt', (await textOf(uns.locator('.msg.err'))) === 'Für diesen Interessenten gibt es schon eine Vorschau.' && (await uns.locator('.msg.err').getAttribute('role')) === 'alert' && (await uns.count()) === 1, await textOf(uns.locator('.msg.err')))
|
||||
check('bauen: nach dem Fehler ist „Vorschau bauen“ wieder frei', await uns.getByRole('button', { name: 'Vorschau bauen', exact: true }).isEnabled())
|
||||
await shoot(page, 'bauen-409.png')
|
||||
await setPultFault(page, 'start-build', { status: 409, message: GONE })
|
||||
await build.click()
|
||||
await build.click()
|
||||
await page.waitForFunction((t) => Array.from(document.querySelectorAll('li.wk-pult-row .msg.err')).some((el) => el.textContent === t), GONE)
|
||||
check('bauen: zweiter 409 („Dieser Kontakt ist nicht mehr aktiv.“) ersetzt die Meldung', (await uns.locator('.msg').count()) === 1 && (await textOf(uns.locator('.msg'))) === GONE)
|
||||
await setPultFault(page, 'start-build', null)
|
||||
|
||||
// 2. Fragt „Vorschau bauen“ nach? Festgestellt wird nur (metrics.bauenFragtNach): ruft der erste Klick den Dienst auf, gibt es keine Rückfrage
|
||||
// (anders als bei Freigeben und Verwerfen). Fragt der Knopf nach, bestätigt der zweite Klick, damit der Rest des Szenarios gleich läuft.
|
||||
// 2. „Vorschau bauen“ fragt wie Freigeben und Verwerfen zuerst nach (Endprüfung W2): der erste Klick ruft den Dienst nicht auf, die Frage verfällt nach 4 s,
|
||||
// erst der zweite Klick startet den Bau. metrics.bauenFragtNach hält fest, ob der erste Klick folgenlos blieb.
|
||||
await setDelay(page, 1500)
|
||||
const before = (await callsOf(page, 'start-build')).length
|
||||
await build.click()
|
||||
@@ -1424,7 +1426,12 @@ async function bauen(browser, base) {
|
||||
const afterFirst = (await callsOf(page, 'start-build')).length
|
||||
metrics.bauenFragtNach = afterFirst === before
|
||||
metrics.bauenErsterKlickRuftDienst = afterFirst === before + 1
|
||||
if (afterFirst === before) await build.click()
|
||||
check('bauen: erster Klick fragt „Wirklich bauen?“ (mit Hinweis zu Opus-Lauf), noch keine Aktion am Dienst', (await textOf(build)) === 'Wirklich bauen?' && afterFirst === before && (await textOf(uns.locator('.wk-pult-actions'))).includes('Startet einen Opus-Lauf'), await textOf(build))
|
||||
await shoot(page, 'bauen-frage.png')
|
||||
await sleep(4400)
|
||||
check('bauen: nach 4 s verfällt die Rückfrage (wieder „Vorschau bauen“, kein Hinweis), noch immer keine Aktion', (await textOf(build)) === 'Vorschau bauen' && (await callsOf(page, 'start-build')).length === before && !(await textOf(uns.locator('.wk-pult-actions'))).includes('Opus'), await textOf(build))
|
||||
await build.click()
|
||||
await build.click()
|
||||
check('bauen: während der Bau angelegt wird sind alle Knöpfe der Zeile gesperrt', (await uns.getByRole('button', { name: 'Ich kümmere mich', exact: true }).isDisabled()) && (await uns.getByRole('button', { name: 'Als Mensch antworten', exact: true }).isDisabled()) && (await uns.getByRole('button', { name: 'Erledigt', exact: true }).isDisabled()) && (await uns.getByRole('button', { name: 'Verwerfen', exact: true }).isDisabled()) && (await uns.getByRole('button', { name: 'Vorschau bauen', exact: true }).isDisabled()))
|
||||
await shoot(page, 'bauen-pending.png')
|
||||
await finishedLine(page).first().waitFor()
|
||||
@@ -1518,7 +1525,9 @@ async function fokus(browser, base) {
|
||||
check('fokus: nach „Freigeben & senden“ (Zeile verschwindet) steht der Fokus nicht auf <body>', noBody(fApprove), fApprove)
|
||||
|
||||
const uns = row4(page, T4.uns)
|
||||
await uns.getByRole('button', { name: 'Vorschau bauen', exact: true }).click()
|
||||
const buildBtn = uns.getByRole('button', { name: /^(Vorschau bauen|Wirklich bauen\?)$/ })
|
||||
await buildBtn.click()
|
||||
await buildBtn.click()
|
||||
await page.waitForFunction((t) => document.querySelector('.wk-pult-filters ~ .msg') && document.querySelector('.wk-pult-filters ~ .msg').textContent.includes(t), 'wird gebaut')
|
||||
await sleep(200)
|
||||
const fBuild = await focusInfo(page)
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
} from '../../lib/workersPult'
|
||||
import ProspectPanel from './ProspectPanel'
|
||||
|
||||
const CONFIRM_MS = 4000 // so lange gilt die Rückfrage „Wirklich verwerfen?“ bzw. „Wirklich freigeben?“ nach dem ersten Klick
|
||||
const CONFIRM_MS = 4000 // so lange gilt die Rückfrage „Wirklich verwerfen?“, „Wirklich freigeben?“ bzw. „Wirklich bauen?“ nach dem ersten Klick
|
||||
// Kosten und Wirkung von „Vorschau bauen“ (Rückfrage und Hinweis am Knopf); bewusst ohne „nur wenn der Interessent Ja gesagt hat“: der Knopf gilt gerade für unklare Antworten
|
||||
const BUILD_HINT = 'Startet einen Opus-Lauf und baut die Vorschau, als hätte der Interessent Ja gesagt.'
|
||||
const NO_FIELDS = Object.freeze({}) // Eintrag ohne bearbeitete Felder
|
||||
|
||||
// Leerzustand je Filter
|
||||
@@ -65,7 +67,7 @@ function TextPanel({ className, label, hint, value, onChange, submitLabel, onSub
|
||||
// Reihenfolge (409 mit deutschem Satz); danach wird die Liste neu geholt.
|
||||
function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFinished, onDetails }) {
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [confirming, setConfirming] = useState(null) // null | 'discard' | 'approve': welche Rückfrage gerade gilt
|
||||
const [confirming, setConfirming] = useState(null) // null | 'discard' | 'approve' | 'build': welche Rückfrage gerade gilt
|
||||
const [open, setOpen] = useState(null) // null | 'reply' | 'paste': welches Feld aufgeklappt ist
|
||||
const [note, setNote] = useState(null) // { kind: 'ok' | 'err', text }
|
||||
const busyRef = useRef(false) // sperrt schon im selben Takt, in dem busy noch nicht greift (Doppelklick)
|
||||
@@ -76,7 +78,7 @@ function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFin
|
||||
return () => { aliveRef.current = false }
|
||||
}, [])
|
||||
|
||||
// Die Rückfrage vor dem Verwerfen und vor dem Freigeben verfällt nach 4 s
|
||||
// Die Rückfrage vor dem Verwerfen, Freigeben und Bauen verfällt nach 4 s
|
||||
useEffect(() => {
|
||||
if (confirming === null) return undefined
|
||||
const timer = window.setTimeout(() => setConfirming(null), CONFIRM_MS)
|
||||
@@ -154,8 +156,15 @@ function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFin
|
||||
run(() => api.pultAction(row.id, 'approve-preview'), 'freigegeben. Max sendet den Link im nächsten Sendefenster.', true, 'approve-preview')
|
||||
}
|
||||
|
||||
// Bau starten wie bei einem „Ja“ des Interessenten (für Einträge „unsicher“)
|
||||
const startBuild = () => run(() => api.pultAction(row.id, 'start-build'), 'wird gebaut.', true, 'start-build')
|
||||
// Bau starten wie bei einem „Ja“ des Interessenten (für Einträge „unsicher“): ein Opus-Lauf, der sich nicht zurücknehmen lässt. Wie beim Freigeben
|
||||
// fragt der erste Klick nach, der zweite binnen 4 s startet den Bau.
|
||||
const startBuild = () => {
|
||||
if (confirming !== 'build') {
|
||||
setConfirming('build')
|
||||
return
|
||||
}
|
||||
run(() => api.pultAction(row.id, 'start-build'), 'wird gebaut.', true, 'start-build')
|
||||
}
|
||||
|
||||
// Die Felder „Als Mensch antworten“ und „Antwort einfügen“ sind getrennt: die Antwort ist mit dem Entwurf (oder dem bearbeiteten Text)
|
||||
// vorbelegt, das Einfüge-Feld beginnt leer. Beide liegen in der Liste, damit der Text einen Wechsel des Filters überlebt.
|
||||
@@ -280,9 +289,12 @@ function PultRow({ api, item, row, text, fields, onText, onField, refresh, onFin
|
||||
</>
|
||||
)}
|
||||
{row.canStartBuild && (
|
||||
<Button type="button" onClick={startBuild} disabled={busy}>
|
||||
Vorschau bauen
|
||||
</Button>
|
||||
<>
|
||||
<Button type="button" onClick={startBuild} disabled={busy} title={BUILD_HINT}>
|
||||
{confirming === 'build' ? 'Wirklich bauen?' : 'Vorschau bauen'}
|
||||
</Button>
|
||||
{confirming === 'build' && <span className="wk-hint">{BUILD_HINT}</span>}
|
||||
</>
|
||||
)}
|
||||
{row.canPaste && (
|
||||
<Button type="button" onClick={() => toggle('paste')} aria-expanded={open === 'paste'} disabled={busy}>
|
||||
|
||||
@@ -55,6 +55,9 @@ const DONE_VIEW = Object.freeze({
|
||||
erledigt: Object.freeze({ label: 'Erledigt', tone: 'muted' }),
|
||||
})
|
||||
|
||||
// Status eines Interessenten, mit dem nichts mehr gebaut wird (Spiegel von TOT in src/gespraeche/pultMail.js des Dienstes)
|
||||
const DEAD_PROSPECT = Object.freeze(['gesperrt', 'verloren', 'deal'])
|
||||
|
||||
const PREVIEW_HOST = 'entwurf.project.webklar.com' // Vorschauen liegen nur hier (Vorschau-Server des Dienstes)
|
||||
|
||||
const MAIL_SUBJECT_MAX = 78
|
||||
@@ -227,8 +230,9 @@ export function rowModel(item, { me, nowMs, employees }) {
|
||||
// Mail-Antwort als Mensch: braucht einen Interessenten mit Adresse („Mail ohne Faden“ und Plattform-Interessenten haben keinen Mail-Weg)
|
||||
canReply: REPLY_KINDS.includes(item.kind) && free && prospect !== null && filled(prospect.email),
|
||||
canApprovePreview: freigabe && free,
|
||||
// Bau starten wie ein „Ja“: Eintrag „unsicher“ mit Interessent; ob der noch lebt, entscheidet der Dienst (409 mit deutschem Satz)
|
||||
canStartBuild: item.kind === 'unsicher' && free && filled(item.prospectId),
|
||||
// Bau starten wie ein „Ja“: Eintrag „unsicher“ mit Interessent. Ein toter Interessent (gesperrt, verloren, Deal) bekommt den Knopf nicht; ohne
|
||||
// Kurzfassung (prospect null) oder ohne Status entscheidet der Dienst (409 mit deutschem Satz, auch bei einer schon vorhandenen Vorschau)
|
||||
canStartBuild: item.kind === 'unsicher' && free && filled(item.prospectId) && !(prospect !== null && DEAD_PROSPECT.includes(prospect.status)),
|
||||
// Die Rückmeldung des Kunden kommt meist erst, nachdem jemand Claras Entwurf auf der Plattform gesendet hat: auch bei gesendet,
|
||||
// erledigt oder von einem Kollegen übernommen. Nur ein verworfener Eintrag nimmt nichts mehr an.
|
||||
canPaste: PASTE_KINDS.includes(item.kind) && item.status !== 'verworfen' && prospect !== null,
|
||||
|
||||
@@ -368,6 +368,21 @@ test('rowModel canStartBuild (GB16): nur Art unsicher mit prospectId, bearbeitba
|
||||
for (const status of ['gesendet', 'erledigt', 'verworfen']) assert.equal(rowModel(entry({ status }), T32_CTX).canStartBuild, false, status)
|
||||
})
|
||||
|
||||
// Endprüfung P4 (W2): ein toter Interessent bekommt keinen Knopf „Vorschau bauen“ (der Dienst lehnt ihn ohnehin mit 409 ab)
|
||||
test('rowModel canStartBuild (W2): bei gesperrtem, verlorenem oder abgeschlossenem Interessenten (gesperrt, verloren, deal) kein Knopf, sonst bleibt er', () => {
|
||||
for (const status of ['gesperrt', 'verloren', 'deal']) {
|
||||
assert.equal(rowModel(entry({ prospect: { $id: 'p1', name: 'X', status } }), T32_CTX).canStartBuild, false, status)
|
||||
}
|
||||
// lebende Interessenten und Kurzfassungen ohne Status behalten den Knopf (der Dienst prüft das Übrige: doppelte Vorschau)
|
||||
for (const status of ['gefunden', 'kontaktiert', 'gespraech', 'ja', 'vorschau_bau', undefined, null, 7]) {
|
||||
assert.equal(rowModel(entry({ prospect: { $id: 'p1', name: 'X', status } }), T32_CTX).canStartBuild, true, String(status))
|
||||
}
|
||||
assert.equal(rowModel(entry({ prospect: null }), T32_CTX).canStartBuild, true) // keine Kurzfassung: der Dienst entscheidet
|
||||
// alles andere zählt weiter: nur Art unsicher, bearbeitbar, nicht von einem anderen gehalten, mit prospectId
|
||||
assert.equal(rowModel(entry({ prospect: { status: 'gesperrt' }, status: 'veraltet' }), T32_CTX).canStartBuild, false)
|
||||
assert.equal(rowModel(entry({ prospect: { status: 'verloren' }, prospectId: '' }), T32_CTX).canStartBuild, false)
|
||||
})
|
||||
|
||||
test('rowModel canPaste: Art plattform_antwort mit Interessent, in jedem Status außer verworfen, auch bei einem Kollegen', () => {
|
||||
const plattform = (over = {}) => entry({ kind: 'plattform_antwort', prospect: { name: 'X' }, ...over })
|
||||
for (const status of ['offen', 'in_arbeit', 'veraltet', 'gesendet', 'erledigt']) assert.equal(rowModel(plattform({ status }), T32_CTX).canPaste, true, status)
|
||||
|
||||
@@ -484,7 +484,7 @@ test('PultList (P4-T32): Quelltext – Aktionen über pultAction, Prüfung mit v
|
||||
for (const t of [
|
||||
"'reply'", "'approve-preview'", "'paste'", "'start-build'", 'validateReplyText', 'canReply', 'canApprovePreview', 'canStartBuild', 'canPaste', 'previewHref',
|
||||
'actionErrorText', 'pasteDoneText', 'rel="noopener noreferrer"', 'target="_blank"',
|
||||
'Als Mensch antworten', 'Jetzt senden', 'Vorschau ansehen', 'Freigeben & senden', 'Wirklich freigeben?', 'Vorschau bauen',
|
||||
'Als Mensch antworten', 'Jetzt senden', 'Vorschau ansehen', 'Freigeben & senden', 'Wirklich freigeben?', 'Vorschau bauen', 'Wirklich bauen?',
|
||||
'Antwort des Kunden einfügen', 'Einfügen', 'wk-pult-reply', 'wk-pult-paste',
|
||||
'Kopiere die Antwort aus der Plattform hierher. Nora sortiert sie ein; bei einer Frage schreibt Clara den nächsten Entwurf.',
|
||||
]) assert.ok(s.includes(t), t)
|
||||
@@ -546,6 +546,12 @@ test('PultList (gerendert, P4-T32): Aktionen je Art – Antworten, Freigeben, Ba
|
||||
// Interessent ohne Adresse (Plattform): kein Mail-Kanal ⇒ nicht antworten, aber bauen
|
||||
html = show([{ ...base, prospect: { $id: 'p1', name: 'Verein' } }])
|
||||
assert.ok(!html.includes('Als Mensch antworten')); assert.ok(html.includes('Vorschau bauen'))
|
||||
// Endprüfung P4 (W2): ein toter Interessent (gesperrt, verloren, Deal) bekommt keinen Knopf zum Bauen, ein lebender behält ihn
|
||||
for (const status of ['gesperrt', 'verloren', 'deal']) {
|
||||
html = show([{ ...base, prospect: { ...base.prospect, status } }])
|
||||
assert.ok(!html.includes('Vorschau bauen'), status); assert.ok(buttonOf(html, 'Als Mensch antworten'), status); assert.ok(buttonOf(html, 'Erledigt'), status)
|
||||
}
|
||||
assert.ok(buttonOf(show([{ ...base, prospect: { ...base.prospect, status: 'gespraech' } }]), 'Vorschau bauen'))
|
||||
// von einem Kollegen übernommen: weder antworten noch bauen, der Rest bleibt wie bisher
|
||||
html = show([{ ...base, status: 'in_arbeit', claimedBy: 'mensch:u2' }])
|
||||
assert.ok(html.includes('übernommen von Kenso')); assert.ok(!html.includes('Als Mensch antworten')); assert.ok(!html.includes('Vorschau bauen')); assert.ok(buttonOf(html, 'Erledigt'))
|
||||
@@ -752,22 +758,56 @@ test('PultList (bedient, P4-T32): während einer Anfrage sind alle Knöpfe der Z
|
||||
} finally { ui.end() }
|
||||
})
|
||||
|
||||
test('PultList (bedient, P4-T32): ein „unsicher“-Eintrag ohne Entwurf öffnet „Als Mensch antworten“ mit leerem Feld; „Vorschau bauen“ startet den Bau', async () => {
|
||||
const api = scriptedApi(failure('Für diesen Interessenten gibt es schon eine Vorschau.', 409), Promise.resolve({ item: { $id: 'e1' } }))
|
||||
test('PultList (bedient, P4-T32): ein „unsicher“-Eintrag ohne Entwurf öffnet „Als Mensch antworten“ mit leerem Feld', async () => {
|
||||
const api = scriptedApi()
|
||||
const ui = await startList([{ ...unsicher, draft: undefined }], api)
|
||||
try {
|
||||
await ui.click('Als Mensch antworten')
|
||||
assert.deepEqual(ui.value('Text der Antwort'), ['']); assert.ok(ui.text().includes('0 / 8000'))
|
||||
// Bau starten: ohne Body, Fehler des Dienstes als Satz, Erfolg oben bei der Liste
|
||||
assert.equal(api.calls.length, 0)
|
||||
} finally { ui.end() }
|
||||
})
|
||||
|
||||
// Endprüfung P4 (W2): „Vorschau bauen“ startet einen Opus-Lauf und lässt sich in der Oberfläche nicht zurücknehmen: wie Freigeben und Verwerfen
|
||||
// fragt der Knopf zuerst nach (4 s). Ein Fehlklick (Handy: der Knopf liegt in voller Breite zwischen anderen) startet keinen Bau mehr.
|
||||
test('PultList (bedient, W2): „Vorschau bauen“ – erster Klick fragt nach (4 s, Hinweis), zweiter startet den Bau; Fehler und Erfolg wie bisher', async () => {
|
||||
const api = scriptedApi(failure('Für diesen Interessenten gibt es schon eine Vorschau.', 409), Promise.resolve({ item: { $id: 'e1' } }))
|
||||
const ui = await startList([{ ...unsicher, draft: undefined }], api)
|
||||
try {
|
||||
// erster Klick: Rückfrage und Hinweis zu Kosten und Wirkung, nichts geht hinaus; die Rückfrage verfällt nach 4 s
|
||||
assert.ok(!ui.text().includes('Opus'))
|
||||
await ui.click('Vorschau bauen')
|
||||
assert.equal(api.calls.length, 0); assert.ok(ui.has('Wirklich bauen?')); assert.ok(!ui.has('Vorschau bauen'))
|
||||
assert.ok(ui.text().includes('Startet einen Opus-Lauf')); assert.ok(ui.text().includes('als hätte der Interessent Ja gesagt'))
|
||||
assert.ok(!ui.text().includes('nur wenn')) // kein „nur wenn der Interessent Ja gesagt hat“: der Knopf gilt gerade für unklare Antworten
|
||||
assert.deepEqual([...ui.timers.values()].map((t) => t.ms), [4000])
|
||||
ui.fireTimers(); await settle()
|
||||
assert.ok(ui.has('Vorschau bauen')); assert.ok(!ui.has('Wirklich bauen?')); assert.ok(!ui.text().includes('Opus')); assert.equal(api.calls.length, 0)
|
||||
// Rückfrage und zweiter Klick ⇒ Aktion ohne Body; der Dienst lehnt ab: sein Satz erscheint, die Rückfrage beginnt von vorn
|
||||
await ui.click('Vorschau bauen'); await ui.click('Wirklich bauen?')
|
||||
assert.deepEqual(api.calls, [['e1', 'start-build']])
|
||||
assert.deepEqual(ui.messages(), [{ kind: 'err', text: 'Für diesen Interessenten gibt es schon eine Vorschau.', role: 'alert' }])
|
||||
await ui.click('Vorschau bauen')
|
||||
assert.ok(ui.has('Vorschau bauen')); assert.ok(!ui.text().includes('Opus'))
|
||||
// Erfolg oben bei der Liste
|
||||
await ui.click('Vorschau bauen'); await ui.click('Wirklich bauen?')
|
||||
assert.deepEqual(ui.messages(), [{ kind: 'ok', text: '„Unklare Antwort“ wird gebaut.', role: 'status' }])
|
||||
assert.equal(api.calls.length, 2)
|
||||
} finally { ui.end() }
|
||||
})
|
||||
|
||||
test('PultList (bedient, W2): die Rückfragen vor Bauen, Verwerfen und Freigeben lösen einander ab; Verwerfen sendet nichts ohne zweiten Klick', async () => {
|
||||
const api = scriptedApi()
|
||||
const ui = await startList([unsicher], api)
|
||||
try {
|
||||
await ui.click('Vorschau bauen'); assert.ok(ui.has('Wirklich bauen?'))
|
||||
await ui.click('Verwerfen'); assert.ok(ui.has('Wirklich verwerfen?')); assert.ok(ui.has('Vorschau bauen')); assert.ok(!ui.text().includes('Opus'))
|
||||
await ui.click('Vorschau bauen'); assert.ok(ui.has('Wirklich bauen?')); assert.ok(ui.has('Verwerfen'))
|
||||
assert.equal(api.calls.length, 0)
|
||||
await ui.click('Wirklich bauen?')
|
||||
assert.deepEqual(api.calls, [['e1', 'start-build']])
|
||||
} finally { ui.end() }
|
||||
})
|
||||
|
||||
test('PultList (bedient, P4-T32): Vorschau freigeben – erster Klick fragt nach (4 s), zweiter sendet; keine Antwort heißt „Kann gesendet sein“', async () => {
|
||||
const freigabe = { ...unsicher, $id: 'f1', kind: 'vorschau_freigabe', title: 'Vorschau prüfen', draft: 'Hier ist Ihre Vorschau.', sourceUrl: 'https://entwurf.project.webklar.com/AAA/' }
|
||||
const api = scriptedApi(failure('Die Vorschau ist nicht freigabebereit.', 409), failure('Der Hustler-Dienst ist gerade nicht erreichbar.', 0), Promise.resolve({ item: { $id: 'f1' }, previewUrl: freigabe.sourceUrl }))
|
||||
|
||||
Reference in new issue
Block a user