Skip to content
Open
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
55 changes: 55 additions & 0 deletions apps/desktop/e2e/session-workbar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,61 @@ test('titlebar workbar action restores an existing tool instead of the picker',
expect(Math.abs(restoredToggleBox!.x - safeAreaToggleBox!.x)).toBeLessThanOrEqual(1);
});

test('registered Workbar descriptors render singleton and dynamic panels', async ({
window: page,
}) => {
await createSession(page, 'create descriptor registry session');
await page.getByRole('button', { name: '展开任务工作栏' }).click();

const launcher = page.getByRole('list', { name: '打开工具' });
const openLauncher = async () => {
await page.getByRole('button', { name: '打开工作栏标签' }).first().click();
await expect(launcher).toBeVisible();
};
const registeredActions = [
/侧边对话.*在不打断主任务的情况下追问和只读探索/,
/变更.*查看当前 Git 工作区变化/,
/终端.*查看当前任务的终端运行和实时输出/,
/浏览器.*打开内置浏览器并保留当前页面/,
/生成文件.*浏览当前任务生成的文件/,
/待办.*查看和维护这个任务的待办台账/,
/工作看板.*记录和管理暂缓事项/,
/追踪.*检查任务调用、工具与耗时记录/,
] as const;
await expect(launcher).toBeVisible();
await expect(launcher.getByRole('button')).toHaveCount(registeredActions.length);
for (const name of registeredActions) {
await expect(launcher.getByRole('button', { name })).toBeVisible();
}

await launcher.getByRole('button', { name: registeredActions[5] }).click();
await expect(page.getByRole('region', { name: '任务待办' })).toBeVisible();
await openLauncher();
await launcher.getByRole('button', { name: registeredActions[5] }).click();
await expect(page.getByRole('tab', { name: '待办' })).toHaveCount(1);

await openLauncher();
await launcher.getByRole('button', { name: registeredActions[2] }).click();
const firstTerminal = page.getByRole('region', { name: '任务终端' });
await expect(firstTerminal).toBeVisible();
const firstTerminalRef = await firstTerminal.getAttribute('data-terminal-ref');
expect(firstTerminalRef).toBeTruthy();
await openLauncher();
await launcher.getByRole('button', { name: registeredActions[2] }).click();
const secondTerminal = page.locator('.maka-session-terminal-panel:visible');
await expect(secondTerminal).toBeVisible();
await expect(page.getByRole('tab', { name: /^终端(?: \d+)?$/ })).toHaveCount(2);
expect(await secondTerminal.getAttribute('data-terminal-ref')).not.toBe(firstTerminalRef);

await openLauncher();
await launcher.getByRole('button', { name: registeredActions[0] }).click();
await expect(page.locator('.maka-quote-companion')).toBeVisible();
await openLauncher();
await launcher.getByRole('button', { name: registeredActions[0] }).click();
await expect(page.getByRole('tab', { name: /^侧边对话(?: \d+)?$/ })).toHaveCount(2);
await expect(page.locator('.maka-quote-companion')).toHaveCount(2);
});

test('Git changes re-read the workspace after the app regains focus', async ({
gitReviewWindow,
}) => {
Expand Down
32 changes: 32 additions & 0 deletions apps/desktop/src/main/__tests__/workbar-boundary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { readdirSync, readFileSync } from 'node:fs';
import { join, relative, resolve } from 'node:path';
import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { WORKBAR_TOOL_DEFINITIONS } from '../../renderer/features/workbar/testing.js';

const desktopRoot = resolve(
fileURLToPath(new URL('../../../', import.meta.url)),
Expand All @@ -43,6 +44,37 @@ function sourceFiles(root: string): string[] {
}

describe('Workbar feature boundary', () => {
it('registers one exhaustive surface descriptor for every Workbar tab kind', () => {
const surface = readFileSync(
join(featureRoot, 'ui', 'workbar-surface.tsx'),
'utf8',
);
const registeredKinds = [...surface.matchAll(
/\n (?:'[^']+'|[a-z]+): defineWorkbarSurfaceDescriptor\(\s*'([^']+)',\s*\{/g,
)].map((match) => match[1]);

assert.deepEqual(
registeredKinds,
WORKBAR_TOOL_DEFINITIONS.map((definition) => definition.kind),
);
assert.equal(
surface.includes(
'const WORKBAR_SURFACE_DESCRIPTOR_BY_KIND: WorkbarSurfaceDescriptorsByKind',
),
true,
);
assert.equal(
surface.includes(
'WORKBAR_TOOL_DEFINITIONS.map(\n (definition) => WORKBAR_SURFACE_DESCRIPTOR_BY_KIND[definition.kind]',
),
true,
);
assert.equal(
surface.includes('const descriptor = workbarSurfaceDescriptor(tab.kind);'),
true,
);
});

it('contains no Desktop global bridge or shell/process imports', () => {
const violations: string[] = [];
for (const path of sourceFiles(featureRoot)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface WorkbarToolDefinition {
readonly labelKey: SessionWorkbarTabKind;
readonly icon:
| 'activity'
| 'clipboard'
| 'folder'
| 'git-branch'
| 'globe'
Expand Down Expand Up @@ -102,7 +103,7 @@ const WORKBAR_TOOL_DEFINITION_BY_KIND = {
'work-board': {
kind: 'work-board',
labelKey: 'work-board',
icon: 'list-todo',
icon: 'clipboard',
persisted: true,
singleton: true,
defaultPlacement: 'right',
Expand Down
Loading