diff --git a/CHANGELOG.md b/CHANGELOG.md index 58767cd..09ba8e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ยท versioned wi ## [Unreleased] ### Added +- **Retry UI Regression Coverage.** Desktop smoke tests now verify that a Retry button is rendered only when the + authenticated job history explicitly advertises a safe live retry and that the retry endpoint is invoked once. - **Safe Background Retries.** Failed live transcription, voice-design, casting, and consistent-image jobs now expose bounded retry actions from stripped in-memory inputs; retry payloads are never returned or persisted and expire on restart. - **Canonical Pair Asset Digests.** Generated profile/reference pairs now retain SHA-256 asset digests in character diff --git a/VERSION b/VERSION index eb716f7..c4bdc90 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.24.9 +1.24.10 diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js index 1028043..81dcdea 100644 --- a/tests/smoke.spec.js +++ b/tests/smoke.spec.js @@ -83,3 +83,22 @@ test('background job history is rendered without payloads', async ({ page }) => await expect(page.locator('#background-jobs-list')).toContainText('Cancel'); await expect(page.locator('#background-jobs-list')).not.toContainText('prompt'); }); + +test('background job history exposes retry only when the server allows it', async ({ page }) => { + let retryRequests = 0; + await page.route('**/api/jobs?limit=50', route => route.fulfill({ + status: 200, contentType: 'application/json', + body: JSON.stringify({ jobs: [{ job_id: 'live-failed', kind: 'voice_design', stage: 'error', error: 'Temporary provider failure', done: true, elapsed: 3.1, retry_available: true }] }), + })); + await page.route('**/api/voice-design/retry/live-failed', async route => { + retryRequests += 1; + await route.fulfill({ json: { job_id: 'new-job' } }); + }); + await page.goto('/', { waitUntil: 'domcontentloaded' }); + await navTo(page, 's-performance'); + const jobs = page.locator('#background-jobs-list'); + await expect(jobs).toContainText('Temporary provider failure'); + await expect(jobs.getByRole('button', { name: 'Retry' })).toBeVisible(); + await jobs.getByRole('button', { name: 'Retry' }).click(); + await expect.poll(() => retryRequests).toBe(1); +});