mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
feat(web): add preview delivery status feedback (#5557)
* feat(web): add preview delivery status feedback
* fix(web): keep delivery retry in chat
* feat(web): stage preview delivery progress
* refine(web): clarify preview delivery stages
* fix(web): preserve preview delivery feedback
* fix(web): address preview delivery review
* refine(web): keep preview delivery status passive
* Revert "refine(web): keep preview delivery status passive"
This reverts commit df5579da48.
---------
Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com>
This commit is contained in:
co-authored by
xiaoche-hub
parent
03617d1296
commit
167db9de20
@@ -0,0 +1,124 @@
|
||||
import { expect, test } from '@/playwright/suite';
|
||||
import { createFakeAgentRuntimes } from '@/playwright/fake-agents';
|
||||
import {
|
||||
createProjectViaApi,
|
||||
gotoEntryHome,
|
||||
gotoProject,
|
||||
putAppConfig,
|
||||
seedBrowserConfig,
|
||||
} from '@/playwright/amr';
|
||||
import { T } from '@/timeouts';
|
||||
|
||||
let codexEnv: Record<string, string>;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
const runtimes = await createFakeAgentRuntimes(['codex']);
|
||||
codexEnv = runtimes.codex.env;
|
||||
});
|
||||
|
||||
test('[P1] preview delivery status keeps persisted delivery-failure recovery in Chat', async ({ page }) => {
|
||||
test.setTimeout(T.xlong);
|
||||
const projectId = `preview-run-status-${Date.now()}`;
|
||||
const now = Date.now();
|
||||
|
||||
const browserConfig = {
|
||||
mode: 'daemon',
|
||||
apiKey: '',
|
||||
baseUrl: 'https://api.anthropic.com',
|
||||
model: 'claude-sonnet-4-5',
|
||||
agentId: 'codex',
|
||||
skillId: null,
|
||||
designSystemId: null,
|
||||
onboardingCompleted: true,
|
||||
agentModels: { codex: { model: 'default', reasoning: 'default' } },
|
||||
agentCliEnv: { codex: codexEnv },
|
||||
};
|
||||
await seedBrowserConfig(page, browserConfig);
|
||||
await putAppConfig(page, browserConfig);
|
||||
|
||||
const { conversationId } = await createProjectViaApi(page, projectId, 'Preview status recovery');
|
||||
const existingFileResponse = await page.request.post(`/api/projects/${projectId}/files`, {
|
||||
data: {
|
||||
name: 'existing-design.html',
|
||||
content: '<!doctype html><html><body><p>Existing design</p></body></html>',
|
||||
},
|
||||
});
|
||||
expect(existingFileResponse.ok(), await existingFileResponse.text()).toBeTruthy();
|
||||
const userResponse = await page.request.put(
|
||||
`/api/projects/${projectId}/conversations/${conversationId}/messages/user-preview-status`,
|
||||
{
|
||||
data: {
|
||||
role: 'user',
|
||||
content: 'Create an editorial landing page',
|
||||
createdAt: now - 50_000,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(userResponse.ok(), await userResponse.text()).toBeTruthy();
|
||||
|
||||
const failedResponse = await page.request.put(
|
||||
`/api/projects/${projectId}/conversations/${conversationId}/messages/assistant-preview-status`,
|
||||
{
|
||||
data: {
|
||||
role: 'assistant',
|
||||
content: 'The design result was not delivered.',
|
||||
agentId: 'codex',
|
||||
sessionMode: 'design',
|
||||
runId: 'run-preview-status',
|
||||
runStatus: 'succeeded',
|
||||
resultDeliveryState: 'delivery_failed',
|
||||
createdAt: now - 50_000,
|
||||
startedAt: now - 45_000,
|
||||
endedAt: now - 3_000,
|
||||
preTurnFileNames: ['existing-design.html'],
|
||||
events: [{ kind: 'status', label: 'error', detail: 'The design result was not delivered.' }],
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(failedResponse.ok(), await failedResponse.text()).toBeTruthy();
|
||||
|
||||
await gotoProject(page, projectId);
|
||||
const status = page.getByTestId('preview-run-status');
|
||||
await expect(status).toContainText('Delivery needs attention');
|
||||
await expect(status.locator('xpath=ancestor::*[contains(@class, "ws-preview-run-status-slot")]')).toHaveCount(1);
|
||||
await expect(status.locator('xpath=ancestor::*[@data-testid="design-files-empty"]')).toHaveCount(0);
|
||||
await expect(status).not.toContainText('Elapsed');
|
||||
await expect(page.getByTestId('preview-run-status-retry')).toHaveCount(0);
|
||||
await expect(page.getByTestId('preview-run-status-view-details')).toBeVisible();
|
||||
const chatRetry = page.locator('.chat-error-retry');
|
||||
await expect(chatRetry).toBeVisible();
|
||||
|
||||
// A small persisted preview hint can bring users back to the full Chat
|
||||
// failure card, which remains the sole retry entry point after navigation.
|
||||
await gotoEntryHome(page);
|
||||
await gotoProject(page, projectId);
|
||||
await expect(page.getByTestId('preview-run-status')).toContainText('Delivery needs attention');
|
||||
await expect(page.getByTestId('preview-run-status')).not.toContainText('Elapsed');
|
||||
await expect(page.locator('.chat-error-retry')).toBeVisible();
|
||||
|
||||
await page.locator('.chat-error-retry').click();
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const response = await page.request.get(
|
||||
`/api/projects/${projectId}/conversations/${conversationId}/messages`,
|
||||
);
|
||||
if (!response.ok()) return [];
|
||||
const body = (await response.json()) as { messages?: Array<{ role: string; runStatus?: string }> };
|
||||
return body.messages?.filter((message) => message.role === 'assistant') ?? [];
|
||||
}, { timeout: T.long })
|
||||
.toHaveLength(2);
|
||||
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const response = await page.request.get(`/api/projects/${projectId}/files`);
|
||||
if (!response.ok()) return [];
|
||||
const body = (await response.json()) as { files?: Array<{ name: string }> };
|
||||
return body.files?.map((file) => file.name) ?? [];
|
||||
}, { timeout: T.long })
|
||||
.toContain('fake-agent-runtime-codex.html');
|
||||
|
||||
await expect(page.getByTestId('preview-run-status')).toContainText('Design ready');
|
||||
await expect(
|
||||
page.getByTestId('preview-run-status').locator('xpath=ancestor::*[contains(@class, "ws-preview-run-status-slot")]'),
|
||||
).toHaveCount(1);
|
||||
});
|
||||
Reference in New Issue
Block a user