Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/desktop/e2e/session-local-recovery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ test('a locally saved message survives renderer and application restart, then ex
await awaitSendReady(page);
await page.locator(COMPOSER_INPUT).press('Enter');
await expect(page.locator(COMPOSER_INPUT)).toHaveText('');
await expect(page.getByText('已本地保存 · 等待发送')).toBeVisible();
await expect(page.getByRole('button', { name: '取消发送', exact: true })).toBeVisible();
const before = await page.evaluate((id) => window.maka.sessionLocal.listMessages(id), sessionId!);
const message = before.find((item) => item.text === pending)!;
expect(message.state).toBe('saved');
Expand All @@ -65,7 +65,7 @@ test('a locally saved message survives renderer and application restart, then ex
await page.reload();
await ensureSidebarExpanded(page);
await page.locator(`[data-session-id=${JSON.stringify(sessionId)}]`).click();
await expect(page.getByText('已本地保存 · 等待发送')).toBeVisible();
await expect(page.getByRole('button', { name: '取消发送', exact: true })).toBeVisible();
expect(
(await page.evaluate((id) => window.maka.sessionLocal.listMessages(id), sessionId!)).find(
(item) => item.text === pending,
Expand Down Expand Up @@ -146,7 +146,7 @@ test('a new task is readable locally before the Host session exists', async ({
await awaitSendReady(page);
await page.locator(COMPOSER_INPUT).press('Enter');
await expect(page.locator(COMPOSER_INPUT)).toHaveText('');
await expect(page.getByText('已本地保存 · 等待发送')).toBeVisible();
await expect(page.getByRole('button', { name: '取消发送', exact: true })).toBeVisible();
await ensureSidebarExpanded(page);
const sessionId = await page
.locator('[data-session-id]:has([aria-current="page"])')
Expand All @@ -157,7 +157,7 @@ test('a new task is readable locally before the Host session exists', async ({
await page.reload();
await ensureSidebarExpanded(page);
await page.locator(`[data-session-id=${JSON.stringify(sessionId)}]`).click();
await expect(page.getByText('已本地保存 · 等待发送')).toBeVisible();
await expect(page.getByRole('button', { name: '取消发送', exact: true })).toBeVisible();
await expect(page.getByText('读取任务失败', { exact: true })).toHaveCount(0);
page = await sessionLocalWindow.restart();
await ensureSidebarExpanded(page);
Expand Down
13 changes: 1 addition & 12 deletions apps/desktop/e2e/workhub-layout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ test('WorkHub keeps the submitted prompt visible while its agent is still runnin
await expect(prompt).toHaveCount(1);
await expect(workhub.locator('.maka-bubble-streaming')).toContainText('Fake backend waiting');
await expect(stop).toBeVisible();
const followups = workhub.locator('[data-queue-placement="next_turn"] .maka-composer-queue-text');
const followups = workhub.locator('.maka-composer-queue .maka-composer-queue-text');
const queuedTexts = ['下一轮整理测试结果', '再下一轮补充使用说明'] as const;
await workhub.locator(COMPOSER_INPUT).fill(queuedTexts[0]);
await workhub.getByRole('button', { name: /^(发送|Send)$/ }).click();
Expand All @@ -457,17 +457,6 @@ test('WorkHub keeps the submitted prompt visible while its agent is still runnin
await awaitSendReady(workhub);
await workhub.locator(COMPOSER_INPUT).press('Enter');
await expect(followups).toHaveText(queuedTexts);
const shortcuts = workhub.getByRole('button', { name: '发送快捷键', exact: true });
await expect(shortcuts).toHaveCount(1);
await shortcuts.hover();
const shortcutHint = workhub.getByRole('tooltip');
const steerModifier = process.platform === 'darwin' ? 'Cmd' : 'Ctrl';
await expect(shortcutHint).toHaveText(`${steerModifier}+Enter:转向(Steering)\nEnter:下一轮(Follow-up)\nShift+Enter:换行`);
await expect.poll(() => shortcutHint.evaluate((element) => {
const bounds = element.getBoundingClientRect();
return bounds.left >= 0 && bounds.right <= innerWidth && bounds.top >= 0 && bounds.bottom <= innerHeight;
})).toBe(true);
await workhub.screenshot({ path: testInfo.outputPath('workhub-queue-shortcuts.png') });
for (const text of queuedTexts) {
await expect(workhub.locator('.maka-user-message').filter({ hasText: text })).toHaveCount(0);
}
Expand Down
10 changes: 3 additions & 7 deletions apps/desktop/renderer-architecture.json
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@
"@maka/ui": 1
},
"importSpecifiers": 7,
"nonTriviaTokens": 2687
"nonTriviaTokens": 2677
},
"src/renderer/app-shell-session-ui-state.ts": {
"importDeclarations": 0,
Expand Down Expand Up @@ -694,11 +694,7 @@
"window.maka.sessions.getPlanState": 1,
"window.maka.sessions.listActiveInteractions": 1,
"window.maka.sessions.listTurnLandmarks": 1,
"window.maka.sessions.promoteQueueEntry": 1,
"window.maka.sessions.reorderQueueEntries": 1,
"window.maka.sessions.retractQueueEntry": 1,
"window.maka.sessions.subscribeActiveInteractions": 1,
"window.maka.sessions.updateQueueEntry": 1,
"window.maka.settings.getClient": 1,
"window.maka.settings.subscribeClientChanged": 1
},
Expand All @@ -725,7 +721,7 @@
"useNewTaskChoice": 1,
"useOnboardingSnapshot": 1,
"usePlanModeState": 1,
"useRef": 14,
"useRef": 13,
"useSessionCollaborationDialog": 1,
"useSessionEventHealthPolling": 1,
"useSessionNavigationReads": 1,
Expand Down Expand Up @@ -831,7 +827,7 @@
"react": 1
},
"importSpecifiers": 98,
"nonTriviaTokens": 12730
"nonTriviaTokens": 12498
},
"src/renderer/use-app-shell-composer-quotes.ts": {
"importDeclarations": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ import { describe, it } from 'node:test';
import { act, createElement } from 'react';
import type { StoredMessage } from '@maka/core/session';
import { cleanupFakeDom, installReactRenderer } from './fake-dom.js';
import { useAppShellSessionUiState } from '../../renderer/features/conversation/index.js';
import {
ConversationServicesProvider,
useAppShellSessionUiState,
type ConversationServices,
} from '../../renderer/features/conversation/index.js';
import { createSessionCatalogController } from '../../renderer/application/contracts/session-catalog/session-catalog-state.js';

import type { LiveTurnProjection } from '@maka/ui';
import { LocaleProvider, ToastProvider, type LiveTurnProjection } from '@maka/ui';
import type { DesktopTranscriptRangeController } from '../../renderer/platform/desktop/desktop-transcript-range-store.js';
import { createAppShellChatActions } from '../../renderer/app-shell-chat-actions.js';
import { prepareTranscriptForSend } from '../../renderer/features/conversation/testing.js';
Expand Down Expand Up @@ -715,6 +719,29 @@ describe('composer first-send cleanup', () => {
const { root } = installReactRenderer();
let publication!: ReturnType<typeof useAppShellSessionUiState>['publication'];
const catalog = createSessionCatalogController();
const services: ConversationServices = {
listMessages: async () => [],
cancelMessage: async () => undefined,
reconcileMessage: async () => undefined,
subscribeChanges: () => () => undefined,
skills: { listInvocable: async () => [] },
sessions: {
readSnapshot: async () => {
throw new Error('Session snapshot is not used in this test');
},
promoteQueueEntry: async () => undefined,
updateQueueEntry: async () => undefined,
retractQueueEntry: async () => undefined,
reorderQueueEntries: async () => undefined,
},
workspace: { searchFiles: async () => ({ ok: false, reason: 'no_project' }) },
newTasks: {
subscribeChanges: () => () => undefined,
listInvocableSkills: async () => [],
searchFiles: async () => ({ ok: false, reason: 'no_project' }),
},
mcp: { subscribeChanges: () => () => undefined },
};
function Probe(): null {
publication = useAppShellSessionUiState(
catalog, undefined, deps.activeIdRef,
Expand All @@ -723,7 +750,16 @@ describe('composer first-send cleanup', () => {
return null;
}
try {
act(() => root.render(createElement(Probe)));
act(() => root.render(
createElement(LocaleProvider, {
locale: 'en',
children: createElement(ToastProvider, {
children: createElement(ConversationServicesProvider, {
services, children: createElement(Probe),
}),
}),
}),
));
const actions = createAppShellChatActions({
...deps, transcriptRangeRef: { current: controller },
isMessagePublished: publication.isMessagePublished,
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop/src/main/__tests__/composer-directories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,13 @@ test('IPC validates directory references without turning them into attachments o
}), /Invalid directory references/);
}
});

test('restoreDirectories stages references under another draft for a later visit', async () => {
const probe = await mount();
await act(() => probe.state().restoreDirectories('draft-b', [reference]));
assert.deepEqual(probe.state().pendingDirectories, [], 'draft-a stays untouched');
await probe.render({ draftKey: 'draft-b' });
assert.deepEqual(probe.state().pendingDirectories, [reference]);
await act(() => probe.state().restoreDirectories('draft-b', [reference, reference]));
assert.deepEqual(probe.state().pendingDirectories, [reference], 'duplicates stay out');
});
4 changes: 4 additions & 0 deletions apps/desktop/src/main/__tests__/composer-mentions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ function installCatalogRenderer(t: TestContext) {
readSnapshot: async () => {
throw new Error('Session snapshot is not used in catalog tests');
},
promoteQueueEntry: async () => undefined,
updateQueueEntry: async () => undefined,
retractQueueEntry: async () => undefined,
reorderQueueEntries: async () => undefined,
},
workspace: { searchFiles: async () => ({ ok: false, reason: 'no_project' }) },
newTasks: {
Expand Down
Loading
Loading