You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by "Astro-Han (via GitHub)" <gi...@apache.org> on 2026/09/04 17:51:00 UTC
[PR] test(desktop): move P1 journeys off Electron [maka]
Astro-Han opened a new pull request, #4803:
URL: https://github.com/apache/maka/pull/4803
## Summary
- move the remaining 20 P1 assertions from 10 Electron specs to unit, component, and Storybook coverage
- add exact lower-tier contracts for Goal budgets, revision-draft retirement, IME Enter handling, About-page content, and Session-rail render/timing budgets
- rely on the existing authoritative lower-tier coverage for WorkHub reconstruction, new-task draft handoff, Module Hub routing, parent-session deletion, and accessibility surfaces
- reduce the current `origin/main` Electron tier from 75 tests / 35 spec files to 55 tests / 25 spec files
Tracks #4761.
## Validation
- `npm --workspace @maka/desktop run build-storybook`
- `npm --workspace @maka/desktop run smoke:storybook` (292 stories, 318 theme renders)
- focused migrated unit/component suite (141 tests)
- Goal and revision-draft tests (7 tests)
- Desktop main, renderer, and Storybook TypeScript checks
- `npm --workspace @maka/ui run typecheck`
- `npm --workspace @maka/desktop run check:architecture`
- architecture ratchet against `origin/main`
- `npm run lint`
- `npm run format:check`
- `node scripts/asf-license-headers.mjs check`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): reduce P1 Electron coverage at owning boundaries [maka]
Posted by "Astro-Han (via GitHub)" <gi...@apache.org>.
Astro-Han commented on code in PR #4803:
URL: https://github.com/apache/maka/pull/4803#discussion_r3941490810
##########
packages/ui/stories/session-list-panel.stories.tsx:
##########
@@ -314,6 +436,8 @@ export const ActiveTaskActionsOpen: Story = {
const page = within(canvasElement.ownerDocument.body);
await waitFor(() => expect(page.getByRole('menu')).toBeVisible());
expect(page.getByRole('menuitem', { name: '重命名' })).toBeVisible();
+ expect(page.getByRole('menuitem', { name: /^归档$/ })).toBeVisible();
+ expect(page.queryByRole('menuitem', { name: /删除/ })).toBeNull();
Review Comment:
I accept the correction to the scope of this story, but I disagree with the P1 classification and am declining the requested expansion in this PR.
The story checks the rendered component with its existing action contract. My earlier mutation added an unconditional Delete item; it did not exercise your different mutation that expands SessionRowActions and wires an optional callback through the production provider. I should not have described the former as complete coverage of the latter. I am correcting the PR description accordingly.
On the clean head, SessionRowActions has flag, archive, unarchive and rename, with no delete capability. That type boundary is a review-visible restriction, not an immutable safety guarantee. Your experiment establishes a missing guard against a future contract-and-provider change; it does not establish an irreversible action exposed by this patch. I therefore classify the residual coverage gap as P2, not a current P1 product defect.
Under the test-selection principle used here, a characterization test must protect a worthwhile behavior at a trustworthy boundary, and its cost includes provider fixtures, environment startup and CI work. A surviving mutation is useful evidence of scope, but does not alone justify another integration harness. I am neither adding a source/field-name assertion that mirrors the interface nor restoring a native journey for this one check. The current component story retains its narrower rendered-menu obligation; the production-provider mutation is an explicitly accepted coverage gap.
Resolving as an acknowledged coverage limitation with the requested test expansion and P1 grading declined, not as a claim that the mutation is now caught.
##########
apps/desktop/e2e/accessibility-coverage.spec.ts:
##########
@@ -1,354 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/test-only/fake-backend';
-import type { CDPSession, Locator, Page } from '@playwright/test';
-import { awaitSendReady, ensureSidebarExpanded, expect, test, COMPOSER_INPUT } from './fixtures';
-import { auditAxTree } from '../../../scripts/ax-tree-audit.mjs';
-import { groupedNav } from '../src/renderer/settings/settings-nav';
-
-function exactNameWithOptionalBadge(label: string): RegExp {
- const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- return new RegExp(`^${escaped}(?: Beta)?$`);
-}
-
-async function assertAxHealth(cdp: CDPSession, surface: string): Promise<void> {
- const result = await cdp.send('Accessibility.getFullAXTree');
- const audit = auditAxTree(result.nodes);
- expect(audit.problems, `${surface} exposes an unhealthy AX tree`).toEqual([]);
-}
-
-async function openSettings(page: Page): Promise<void> {
- const sidebarToggle = page.getByRole('button', { name: '展开侧边栏' });
- if (await sidebarToggle.isVisible()) await sidebarToggle.click();
- await page.getByRole('button', { name: '设置', exact: true }).click();
- await expect(page.getByRole('main', { name: '设置内容' })).toBeVisible();
-}
-
-async function tabTo(page: Page, target: Locator, label: string, limit = 30): Promise<void> {
- for (let index = 0; index < limit; index += 1) {
- if (await target.evaluate((element) => element === document.activeElement)) return;
- await page.keyboard.press('Tab');
- }
- expect(
- await target.evaluate((element) => element === document.activeElement),
- `${label} is not reachable within ${limit} Tab presses`,
- ).toBe(true);
-}
-
-async function enterMainFromSkipLink(page: Page): Promise<void> {
- await page.evaluate(() => {
- document.body.tabIndex = -1;
- document.body.focus();
- });
- const skipLink = page.getByRole('link', { name: '跳到主要内容' });
- await tabTo(page, skipLink, 'skip link', 10);
- await page.keyboard.press('Enter');
- await expect(page.getByRole('main')).toBeFocused();
- await page.evaluate(() => document.body.removeAttribute('tabindex'));
-}
-
-test('every settings page exposes named actionable controls', async ({ window: page }) => {
- await openSettings(page);
- const cdp = await page.context().newCDPSession(page);
- const navigation = page.getByRole('navigation', { name: '设置分组' });
- await expect(page.getByRole('main')).toHaveCount(1);
- const sectionLabels = (await navigation.getByRole('button').allTextContents())
- .map((label) => label.trim().replace(/\s*Beta$/, ''))
- .filter((label) => label.length > 0 && label !== '返回应用');
- const expectedSectionLabels = groupedNav('zh-CN')
- .flatMap(({ items }) => items)
- .filter(({ enabled }) => enabled)
- .map(({ label }) => label);
- expect(sectionLabels, 'settings navigation must expose every enabled page in source order').toEqual(
- expectedSectionLabels,
- );
-
- for (const section of sectionLabels) {
- const sectionButton = navigation.getByRole('button', {
- name: exactNameWithOptionalBadge(section),
- });
- await sectionButton.click();
- await expect(sectionButton).toHaveAttribute('aria-current', 'page');
- await expect(page.getByRole('heading', { name: section, exact: true })).toBeVisible();
- await assertAxHealth(cdp, `settings/${section}`);
- }
-});
-
-test('module pages and global overlays expose named actionable controls', async ({
- window: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('link', { name: '跳到主要内容' })).toHaveCount(1);
- await expect(page.getByRole('link', { name: 'Skip to content' })).toHaveCount(0);
- const sidebarToggle = page.getByRole('button', { name: '展开侧边栏' });
- if (await sidebarToggle.isVisible()) await sidebarToggle.click();
- const navigation = page.getByRole('navigation', { name: '任务列表' });
-
- await navigation.getByRole('button', { name: '扩展', exact: true }).click();
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: '扩展', exact: true })).toBeVisible();
- const extensionsNavigation = page.getByRole('navigation', { name: /扩展内容/ });
- await expect(
- extensionsNavigation.getByRole('button', { name: '技能', exact: true }),
- ).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'extensions/skills');
- const mcpButton = extensionsNavigation.getByRole('button', { name: 'MCP', exact: true });
- await mcpButton.click();
- await expect(mcpButton).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'extensions/mcp');
-
- await navigation.getByRole('button', { name: /定时任务/ }).click();
- const automationsNavigation = page.getByRole('navigation', { name: /定时任务内容/ });
- await expect(
- automationsNavigation.getByRole('button', { name: '定时任务', exact: true }),
- ).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'automations/scheduled-tasks');
- const dailyReviewButton = automationsNavigation.getByRole('button', {
- name: '每日回顾',
- exact: true,
- });
- await dailyReviewButton.click();
- await expect(dailyReviewButton).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'automations/daily-review');
-
- await page.keyboard.press('Shift+Slash');
- const keyboardHelpDialog = page.getByRole('dialog', { name: '键盘快捷键' });
- await expect(keyboardHelpDialog).toBeVisible();
- await assertAxHealth(cdp, 'overlay/keyboard-help');
- await page.keyboard.press('Escape');
- await expect(keyboardHelpDialog).toBeHidden();
-
- await page.keyboard.press(process.platform === 'darwin' ? 'Meta+k' : 'Control+k');
- const commandPaletteDialog = page.getByRole('dialog', { name: '命令面板' });
- await expect(commandPaletteDialog).toBeVisible();
- await assertAxHealth(cdp, 'overlay/command-palette');
- await page.keyboard.press('Escape');
-});
-
-test('data-backed conversation exposes an open workbar face and keyboard access to tools, models, and Graph', async ({
- accessibilityNarrativeWindow: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('region', { name: /对话:/ })).toBeVisible();
- // The scenario opens a face beside the transcript so the AX sweep below sees
- // the workbar's own tree, not an empty column. Which face is incidental — it
- // was the Task face until that face was retired — so this asserts the strip
- // reports one, rather than reaching into the face's contents.
- const workbar = page.getByRole('complementary', { name: '任务工作栏' });
- await expect(workbar).toBeVisible();
- await expect(workbar.getByRole('tab', { name: '变更' })).toHaveAttribute(
- 'aria-selected',
- 'true',
- );
- await assertAxHealth(cdp, 'conversation/data-backed');
-
- await expect(page.getByRole('main')).toHaveCount(1);
- await enterMainFromSkipLink(page);
-
- const toolCall = page.getByRole('button', { name: /^检查测试状态/ });
- await tabTo(page, toolCall, 'tool result');
- await page.keyboard.press('Enter');
- await expect(toolCall).toHaveAttribute('aria-expanded', 'true');
- await expect(page.locator('[data-slot="tool-output"]')).toContainText('core 41 passing');
- await assertAxHealth(cdp, 'conversation/tool-result-expanded');
- await page.keyboard.press('Enter');
- await expect(toolCall).toHaveAttribute('aria-expanded', 'false');
-
- const modelSwitcher = page.getByRole('button', { name: '切换当前任务模型' });
- await tabTo(page, modelSwitcher, 'model picker');
- await page.keyboard.press('Enter');
- await expect(page.getByRole('menuitemradio', { name: /glm-5\.1/ })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/model-picker');
- const availableModel = page.getByRole('menuitemradio', { name: 'glm-4.5', exact: true });
- await expect(availableModel).toBeFocused();
- await page.keyboard.press('Enter');
- await expect(modelSwitcher).toContainText('glm-4.5');
-
- const composer = page.locator(COMPOSER_INPUT);
- await tabTo(page, composer, 'composer', 60);
- await page.keyboard.insertText('/graph on');
- const send = page.getByRole('button', { name: '发送' });
- await tabTo(page, send, 'Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
- await expect(page.getByText('Graph Mode 已开启', { exact: true })).toBeVisible();
- await assertAxHealth(cdp, 'overlay/graph-mode-toast');
-
- const graphPanel = page.getByRole('region', { name: 'Agent Graph' });
- await expect(graphPanel).toBeVisible();
- await expect(graphPanel).toContainText('等待主 Agent 创建 operator…');
- const collapseGraph = graphPanel.getByRole('button', { name: '收起 Agent Graph' });
- await tabTo(page, collapseGraph, 'Graph collapse', 60);
- await page.keyboard.press('Enter');
- await expect(
- graphPanel.getByRole('button', { name: '展开 Agent Graph' }),
- ).toHaveAttribute('aria-expanded', 'false');
- await assertAxHealth(cdp, 'conversation/agent-graph-empty');
-});
-
-test('toast and error states expose healthy live regions', async ({ window: page }) => {
- const cdp = await page.context().newCDPSession(page);
- const composer = page.locator(COMPOSER_INPUT);
- await composer.fill('/graph history');
- await awaitSendReady(page);
- await composer.press('Enter');
- await expect(page.getByText('Graph 历史', { exact: true })).toBeVisible();
- await assertAxHealth(cdp, 'overlay/graph-history-toast');
-
- await page.evaluate(async () => {
- await window.maka.connections.setDefaultModel(null);
- await window.maka.settings.updateClient({ workHub: { enabled: true } });
- });
- const failure = page.getByRole('alert');
- await expect(failure).toContainText('WorkHub 暂时无法启动');
- await expect(failure).toContainText('请检查当前 Runtime Host 的默认模型配置');
- await assertAxHealth(cdp, 'workhub/startup-error');
-});
-
-test('a streaming answer exposes a healthy live conversation state', async ({ window: page }) => {
- const cdp = await page.context().newCDPSession(page);
- await enterMainFromSkipLink(page);
- const composer = page.locator(COMPOSER_INPUT);
- await tabTo(page, composer, 'streaming composer', 60);
- await page.keyboard.insertText(FAKE_HOLD_OPEN_PROMPT);
- const send = page.getByRole('button', { name: '发送' });
- // After the Tab walk, not before it: a tooltip-carrying Astryx Button is
- // disabled via `aria-disabled`, so it stays focusable and `tabTo` would
- // reach it either way.
- await tabTo(page, send, 'streaming Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
-
- await expect(page.locator('.maka-bubble-streaming')).toContainText(
- 'Fake backend waiting',
- { timeout: 20_000 },
- );
- await expect(page.getByRole('button', { name: '停止' })).toBeEnabled();
- await assertAxHealth(cdp, 'conversation/streaming');
-
- const stop = page.getByRole('button', { name: '停止' });
- await tabTo(page, stop, 'streaming Stop button', 20);
- await page.keyboard.press('Enter');
- await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
- timeout: 20_000,
- });
- await assertAxHealth(cdp, 'conversation/stopped');
-});
-
-test('composer and workbar entry points expose named actionable controls', async ({
- window: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: '新任务对话' })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/new-task');
-
- await enterMainFromSkipLink(page);
- const composer = page.locator(COMPOSER_INPUT);
- const prompt = 'create a session for accessibility coverage';
- await tabTo(page, composer, 'new-task composer', 60);
- await page.keyboard.insertText(prompt);
- const send = page.getByRole('button', { name: '发送' });
- await tabTo(page, send, 'new-task Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
- await expect(page.getByText(`Fake backend received: ${prompt}`)).toBeVisible({
- timeout: 30_000,
- });
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: /对话:/ })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/session');
-
- await page.getByRole('button', { name: '展开任务工作栏' }).click();
- await expect(page.getByRole('list', { name: '打开工具' })).toBeVisible();
- await assertAxHealth(cdp, 'workbar/launcher');
-
- // Registry order, minus the Task face this shell retired. The launcher is
- // the empty state, so it opens the first face and then goes away; every face
- // after it is opened from the [+] menu, which is the only entry point once a
- // face is on screen. The faces stay open behind one another — the strip is a
- // `tablist` now, so each one has to keep reporting its own tab.
- const workbarPanels = ['侧边对话', '变更', '终端', '浏览器', '生成文件', '追踪'] as const;
Review Comment:
I agree that generic AX auditing and direct Browser stories do not verify the production route inventory. That limitation is already an explicit coverage subtraction in this PR, not an equivalence claim.
I disagree with classifying it as P1 or treating inventory coverage as automatically mandatory. Removing Browser from the production registry demonstrates what the retired sweep could detect. It does not demonstrate a broken keyboard/screen-reader route on this clean head, nor establish that recreating the sweep is the lowest-cost valuable test. The runtime consequence of that hypothetical future change should not be attributed to this patch as an already-present product defect.
The test-selection rule here is to retain representative tests for valuable obligations, not reproduce every old assertion or manufacture RED by changing working production code merely to justify a new test. Fixtures, route permutations and CI startup all count toward cost. An inventory assertion derived from the same registry would also miss removal of an entry; an independent fixed list needs its own product requirement and maintenance justification. I am not introducing either solely to satisfy this review.
I retained the draft-restoration focus journey and the WorkHub/revision integration paths, and strengthened the picker callback contract. The broad AX and Module Hub route inventories remain deliberately retired. This is a disclosed P2 coverage trade, not a claim that accessibility or Browser access is unimportant.
Resolving with the requested coverage expansion and P1 grading declined. A reproduced defect on the supported clean-head behavior would warrant a focused regression test and its own consequence-based severity.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): reduce P1 Electron coverage at owning boundaries [maka]
Posted by "Astro-Han (via GitHub)" <gi...@apache.org>.
Astro-Han merged PR #4803:
URL: https://github.com/apache/maka/pull/4803
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): reduce P1 Electron coverage at owning boundaries [maka]
Posted by "Astro-Han (via GitHub)" <gi...@apache.org>.
Astro-Han commented on code in PR #4803:
URL: https://github.com/apache/maka/pull/4803#discussion_r3941073523
##########
apps/desktop/src/renderer/app-shell.tsx:
##########
@@ -2112,13 +2112,15 @@ function AppShellContent({
}
if (ok !== false && revisionSend) {
if (expectedRevisionDraft) {
- completeTurnRevisionCopyAttempt(expectedRevisionDraft);
- composerRef.current?.clearDraft(expectedRevisionDraft.draftSessionId);
- if (expectedRevisionDraft.sourceSessionId !== expectedRevisionDraft.draftSessionId) {
- composerRef.current?.clearDraft(expectedRevisionDraft.sourceSessionId);
- }
+ retireRevisionDraft(
Review Comment:
Addressed in 72287bbf00 by retaining the integration owner: I restored both skill-draft-lifecycle journeys and removed retireRevisionDraft plus its direct-call tests. AppShell and the conversation exports now match the PR base, so this PR no longer substitutes a helper-only test for the successful-send branch. Local native verification remains limited: the shared window fixture timed out before the first retained test body; I have recorded that in the PR description rather than claiming those journeys passed locally.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): reduce P1 Electron coverage at owning boundaries [maka]
Posted by "hqhq1025 (via GitHub)" <gi...@apache.org>.
hqhq1025 commented on code in PR #4803:
URL: https://github.com/apache/maka/pull/4803#discussion_r3941092726
##########
packages/ui/stories/session-list-panel.stories.tsx:
##########
@@ -314,6 +436,8 @@ export const ActiveTaskActionsOpen: Story = {
const page = within(canvasElement.ownerDocument.body);
await waitFor(() => expect(page.getByRole('menu')).toBeVisible());
expect(page.getByRole('menuitem', { name: '重命名' })).toBeVisible();
+ expect(page.getByRole('menuitem', { name: /^归档$/ })).toBeVisible();
+ expect(page.queryByRole('menuitem', { name: /删除/ })).toBeNull();
Review Comment:
**[P1] Exercise the production rail boundary, not a fixture without delete**
This assertion only checks the story's fixed `rowActions` fixture, which omits any delete action. Production constructs these actions in `SessionNavigationProvider`, where `deleteSession` is available. On this exact head, I added an optional Delete action to the UI component and wired it through that provider; the build and the full Storybook smoke suite still passed all 292 stories / 318 renders, including this story, while irreversible deletion was exposed in the rail again. Please mount the production provider/composition here or retain a focused Electron route test so the guard actually covers the safety boundary.
##########
apps/desktop/e2e/accessibility-coverage.spec.ts:
##########
@@ -1,354 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { FAKE_HOLD_OPEN_PROMPT } from '@maka/runtime/test-only/fake-backend';
-import type { CDPSession, Locator, Page } from '@playwright/test';
-import { awaitSendReady, ensureSidebarExpanded, expect, test, COMPOSER_INPUT } from './fixtures';
-import { auditAxTree } from '../../../scripts/ax-tree-audit.mjs';
-import { groupedNav } from '../src/renderer/settings/settings-nav';
-
-function exactNameWithOptionalBadge(label: string): RegExp {
- const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- return new RegExp(`^${escaped}(?: Beta)?$`);
-}
-
-async function assertAxHealth(cdp: CDPSession, surface: string): Promise<void> {
- const result = await cdp.send('Accessibility.getFullAXTree');
- const audit = auditAxTree(result.nodes);
- expect(audit.problems, `${surface} exposes an unhealthy AX tree`).toEqual([]);
-}
-
-async function openSettings(page: Page): Promise<void> {
- const sidebarToggle = page.getByRole('button', { name: '展开侧边栏' });
- if (await sidebarToggle.isVisible()) await sidebarToggle.click();
- await page.getByRole('button', { name: '设置', exact: true }).click();
- await expect(page.getByRole('main', { name: '设置内容' })).toBeVisible();
-}
-
-async function tabTo(page: Page, target: Locator, label: string, limit = 30): Promise<void> {
- for (let index = 0; index < limit; index += 1) {
- if (await target.evaluate((element) => element === document.activeElement)) return;
- await page.keyboard.press('Tab');
- }
- expect(
- await target.evaluate((element) => element === document.activeElement),
- `${label} is not reachable within ${limit} Tab presses`,
- ).toBe(true);
-}
-
-async function enterMainFromSkipLink(page: Page): Promise<void> {
- await page.evaluate(() => {
- document.body.tabIndex = -1;
- document.body.focus();
- });
- const skipLink = page.getByRole('link', { name: '跳到主要内容' });
- await tabTo(page, skipLink, 'skip link', 10);
- await page.keyboard.press('Enter');
- await expect(page.getByRole('main')).toBeFocused();
- await page.evaluate(() => document.body.removeAttribute('tabindex'));
-}
-
-test('every settings page exposes named actionable controls', async ({ window: page }) => {
- await openSettings(page);
- const cdp = await page.context().newCDPSession(page);
- const navigation = page.getByRole('navigation', { name: '设置分组' });
- await expect(page.getByRole('main')).toHaveCount(1);
- const sectionLabels = (await navigation.getByRole('button').allTextContents())
- .map((label) => label.trim().replace(/\s*Beta$/, ''))
- .filter((label) => label.length > 0 && label !== '返回应用');
- const expectedSectionLabels = groupedNav('zh-CN')
- .flatMap(({ items }) => items)
- .filter(({ enabled }) => enabled)
- .map(({ label }) => label);
- expect(sectionLabels, 'settings navigation must expose every enabled page in source order').toEqual(
- expectedSectionLabels,
- );
-
- for (const section of sectionLabels) {
- const sectionButton = navigation.getByRole('button', {
- name: exactNameWithOptionalBadge(section),
- });
- await sectionButton.click();
- await expect(sectionButton).toHaveAttribute('aria-current', 'page');
- await expect(page.getByRole('heading', { name: section, exact: true })).toBeVisible();
- await assertAxHealth(cdp, `settings/${section}`);
- }
-});
-
-test('module pages and global overlays expose named actionable controls', async ({
- window: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('link', { name: '跳到主要内容' })).toHaveCount(1);
- await expect(page.getByRole('link', { name: 'Skip to content' })).toHaveCount(0);
- const sidebarToggle = page.getByRole('button', { name: '展开侧边栏' });
- if (await sidebarToggle.isVisible()) await sidebarToggle.click();
- const navigation = page.getByRole('navigation', { name: '任务列表' });
-
- await navigation.getByRole('button', { name: '扩展', exact: true }).click();
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: '扩展', exact: true })).toBeVisible();
- const extensionsNavigation = page.getByRole('navigation', { name: /扩展内容/ });
- await expect(
- extensionsNavigation.getByRole('button', { name: '技能', exact: true }),
- ).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'extensions/skills');
- const mcpButton = extensionsNavigation.getByRole('button', { name: 'MCP', exact: true });
- await mcpButton.click();
- await expect(mcpButton).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'extensions/mcp');
-
- await navigation.getByRole('button', { name: /定时任务/ }).click();
- const automationsNavigation = page.getByRole('navigation', { name: /定时任务内容/ });
- await expect(
- automationsNavigation.getByRole('button', { name: '定时任务', exact: true }),
- ).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'automations/scheduled-tasks');
- const dailyReviewButton = automationsNavigation.getByRole('button', {
- name: '每日回顾',
- exact: true,
- });
- await dailyReviewButton.click();
- await expect(dailyReviewButton).toHaveAttribute('aria-current', 'true');
- await assertAxHealth(cdp, 'automations/daily-review');
-
- await page.keyboard.press('Shift+Slash');
- const keyboardHelpDialog = page.getByRole('dialog', { name: '键盘快捷键' });
- await expect(keyboardHelpDialog).toBeVisible();
- await assertAxHealth(cdp, 'overlay/keyboard-help');
- await page.keyboard.press('Escape');
- await expect(keyboardHelpDialog).toBeHidden();
-
- await page.keyboard.press(process.platform === 'darwin' ? 'Meta+k' : 'Control+k');
- const commandPaletteDialog = page.getByRole('dialog', { name: '命令面板' });
- await expect(commandPaletteDialog).toBeVisible();
- await assertAxHealth(cdp, 'overlay/command-palette');
- await page.keyboard.press('Escape');
-});
-
-test('data-backed conversation exposes an open workbar face and keyboard access to tools, models, and Graph', async ({
- accessibilityNarrativeWindow: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('region', { name: /对话:/ })).toBeVisible();
- // The scenario opens a face beside the transcript so the AX sweep below sees
- // the workbar's own tree, not an empty column. Which face is incidental — it
- // was the Task face until that face was retired — so this asserts the strip
- // reports one, rather than reaching into the face's contents.
- const workbar = page.getByRole('complementary', { name: '任务工作栏' });
- await expect(workbar).toBeVisible();
- await expect(workbar.getByRole('tab', { name: '变更' })).toHaveAttribute(
- 'aria-selected',
- 'true',
- );
- await assertAxHealth(cdp, 'conversation/data-backed');
-
- await expect(page.getByRole('main')).toHaveCount(1);
- await enterMainFromSkipLink(page);
-
- const toolCall = page.getByRole('button', { name: /^检查测试状态/ });
- await tabTo(page, toolCall, 'tool result');
- await page.keyboard.press('Enter');
- await expect(toolCall).toHaveAttribute('aria-expanded', 'true');
- await expect(page.locator('[data-slot="tool-output"]')).toContainText('core 41 passing');
- await assertAxHealth(cdp, 'conversation/tool-result-expanded');
- await page.keyboard.press('Enter');
- await expect(toolCall).toHaveAttribute('aria-expanded', 'false');
-
- const modelSwitcher = page.getByRole('button', { name: '切换当前任务模型' });
- await tabTo(page, modelSwitcher, 'model picker');
- await page.keyboard.press('Enter');
- await expect(page.getByRole('menuitemradio', { name: /glm-5\.1/ })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/model-picker');
- const availableModel = page.getByRole('menuitemradio', { name: 'glm-4.5', exact: true });
- await expect(availableModel).toBeFocused();
- await page.keyboard.press('Enter');
- await expect(modelSwitcher).toContainText('glm-4.5');
-
- const composer = page.locator(COMPOSER_INPUT);
- await tabTo(page, composer, 'composer', 60);
- await page.keyboard.insertText('/graph on');
- const send = page.getByRole('button', { name: '发送' });
- await tabTo(page, send, 'Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
- await expect(page.getByText('Graph Mode 已开启', { exact: true })).toBeVisible();
- await assertAxHealth(cdp, 'overlay/graph-mode-toast');
-
- const graphPanel = page.getByRole('region', { name: 'Agent Graph' });
- await expect(graphPanel).toBeVisible();
- await expect(graphPanel).toContainText('等待主 Agent 创建 operator…');
- const collapseGraph = graphPanel.getByRole('button', { name: '收起 Agent Graph' });
- await tabTo(page, collapseGraph, 'Graph collapse', 60);
- await page.keyboard.press('Enter');
- await expect(
- graphPanel.getByRole('button', { name: '展开 Agent Graph' }),
- ).toHaveAttribute('aria-expanded', 'false');
- await assertAxHealth(cdp, 'conversation/agent-graph-empty');
-});
-
-test('toast and error states expose healthy live regions', async ({ window: page }) => {
- const cdp = await page.context().newCDPSession(page);
- const composer = page.locator(COMPOSER_INPUT);
- await composer.fill('/graph history');
- await awaitSendReady(page);
- await composer.press('Enter');
- await expect(page.getByText('Graph 历史', { exact: true })).toBeVisible();
- await assertAxHealth(cdp, 'overlay/graph-history-toast');
-
- await page.evaluate(async () => {
- await window.maka.connections.setDefaultModel(null);
- await window.maka.settings.updateClient({ workHub: { enabled: true } });
- });
- const failure = page.getByRole('alert');
- await expect(failure).toContainText('WorkHub 暂时无法启动');
- await expect(failure).toContainText('请检查当前 Runtime Host 的默认模型配置');
- await assertAxHealth(cdp, 'workhub/startup-error');
-});
-
-test('a streaming answer exposes a healthy live conversation state', async ({ window: page }) => {
- const cdp = await page.context().newCDPSession(page);
- await enterMainFromSkipLink(page);
- const composer = page.locator(COMPOSER_INPUT);
- await tabTo(page, composer, 'streaming composer', 60);
- await page.keyboard.insertText(FAKE_HOLD_OPEN_PROMPT);
- const send = page.getByRole('button', { name: '发送' });
- // After the Tab walk, not before it: a tooltip-carrying Astryx Button is
- // disabled via `aria-disabled`, so it stays focusable and `tabTo` would
- // reach it either way.
- await tabTo(page, send, 'streaming Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
-
- await expect(page.locator('.maka-bubble-streaming')).toContainText(
- 'Fake backend waiting',
- { timeout: 20_000 },
- );
- await expect(page.getByRole('button', { name: '停止' })).toBeEnabled();
- await assertAxHealth(cdp, 'conversation/streaming');
-
- const stop = page.getByRole('button', { name: '停止' });
- await tabTo(page, stop, 'streaming Stop button', 20);
- await page.keyboard.press('Enter');
- await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
- timeout: 20_000,
- });
- await assertAxHealth(cdp, 'conversation/stopped');
-});
-
-test('composer and workbar entry points expose named actionable controls', async ({
- window: page,
-}) => {
- const cdp = await page.context().newCDPSession(page);
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: '新任务对话' })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/new-task');
-
- await enterMainFromSkipLink(page);
- const composer = page.locator(COMPOSER_INPUT);
- const prompt = 'create a session for accessibility coverage';
- await tabTo(page, composer, 'new-task composer', 60);
- await page.keyboard.insertText(prompt);
- const send = page.getByRole('button', { name: '发送' });
- await tabTo(page, send, 'new-task Send button', 20);
- await awaitSendReady(page);
- await page.keyboard.press('Enter');
- await expect(page.getByText(`Fake backend received: ${prompt}`)).toBeVisible({
- timeout: 30_000,
- });
- await expect(page.getByRole('main')).toHaveCount(1);
- await expect(page.getByRole('region', { name: /对话:/ })).toBeVisible();
- await assertAxHealth(cdp, 'conversation/session');
-
- await page.getByRole('button', { name: '展开任务工作栏' }).click();
- await expect(page.getByRole('list', { name: '打开工具' })).toBeVisible();
- await assertAxHealth(cdp, 'workbar/launcher');
-
- // Registry order, minus the Task face this shell retired. The launcher is
- // the empty state, so it opens the first face and then goes away; every face
- // after it is opened from the [+] menu, which is the only entry point once a
- // face is on screen. The faces stay open behind one another — the strip is a
- // `tablist` now, so each one has to keep reporting its own tab.
- const workbarPanels = ['侧边对话', '变更', '终端', '浏览器', '生成文件', '追踪'] as const;
Review Comment:
**[P1] Keep an owner for route-specific accessible shell inventory**
The generic Storybook accessibility sweep does not own the production workbar registry or its keyboard entry routes. On this exact head, I removed Browser from `WORKBAR_TOOL_DEFINITIONS`, rebuilt Storybook, and the full smoke suite still passed all 292 stories / 318 renders even though Browser disappeared from both the launcher and `[+]` menu; the direct Browser stories continued to pass. Please retain a focused per-route inventory/keyboard journey for critical shell entries, or add a lower-tier test that composes the production registry and entry points. Declaring this broad journey retired does not replace that ownership.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): move P1 journeys off Electron [maka]
Posted by "hqhq1025 (via GitHub)" <gi...@apache.org>.
hqhq1025 commented on code in PR #4803:
URL: https://github.com/apache/maka/pull/4803#discussion_r3940385772
##########
apps/desktop/src/renderer/app-shell.tsx:
##########
@@ -2112,13 +2112,15 @@ function AppShellContent({
}
if (ok !== false && revisionSend) {
if (expectedRevisionDraft) {
- completeTurnRevisionCopyAttempt(expectedRevisionDraft);
- composerRef.current?.clearDraft(expectedRevisionDraft.draftSessionId);
- if (expectedRevisionDraft.sourceSessionId !== expectedRevisionDraft.draftSessionId) {
- composerRef.current?.clearDraft(expectedRevisionDraft.sourceSessionId);
- }
+ retireRevisionDraft(
Review Comment:
[P2] Keep this AppShell call site under an integration owner. The new `app-shell-revision-actions.test.ts` calls `retireRevisionDraft` directly, so it proves the helper's internals but never proves that a successful revision send reaches this branch. In a temporary worktree at this exact head, I replaced this call with only `commitRevisionDraft(null)`; Desktop typecheck still passed and the advertised Goal/revision suite remained 7/7 green. That mutation leaves the child/source draft slots uncleared after a successful edit-and-resend, which the deleted `skill-draft-lifecycle.spec.ts` guarded by switching back to the previous version and asserting an empty composer. Please add a test that drives a successful revision send through AppShell and observes both draft owners, or retain the journey.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org
Re: [PR] test(desktop): reduce P1 Electron coverage at owning boundaries [maka]
Posted by "Astro-Han (via GitHub)" <gi...@apache.org>.
Astro-Han commented on PR #4803:
URL: https://github.com/apache/maka/pull/4803#issuecomment-5552807149
I addressed the coverage gaps in `72287bbf00`, but I am not restoring every deleted assertion. The PR now removes 15 Electron tests, rather than claiming 20 equivalent migrations.
- **Destructive rail entry:** the existing rendered row-menu story now checks that Archive is available and Delete is absent. Adding a Delete menu entry makes it fail. I retained the controller/Host/storage deletion coverage rather than restoring the full parent-deletion journey.
- **Project picker:** I extended the existing controller test to invoke both picker callbacks and check the target and draft identity through a round trip. Replacing both callbacks with no-ops now fails that test; the clean controller/draft/staging suite passes 29/29. This complements the existing content-handoff tests without adding two full Electron journeys.
- **WorkHub:** I retained reopening after leaving and exact cross-Session delegation replacement. The redundant creation-announcement journey remains removed.
- **Revision drafts:** I removed the helper extraction and its direct-call tests and restored both existing edit/resend and cancellation journeys. AppShell and the conversation exports are back to the PR base.
- **Accessibility:** I retained the session-row draft-restoration focus journey. I am intentionally retiring the six broad AX journeys and the Module Hub route sweep. Generic AX auditing, leaf rendering and controller tests are not equivalent to those journeys, and the description no longer claims otherwise. Exhaustive page/menu inventories, copy assertions and repeated AX scans are not all independent product requirements. The fact that a removed assertion detects a mutation establishes a coverage difference, but does not by itself justify the maintenance cost of retaining that entire journey.
I accept that the original destination table overstated equivalence. I do not agree with grading these coverage changes as current P1 product defects solely from hypothetical future mutations: no current destructive action or broken accessibility route was demonstrated on the clean head. That grading disagreement does not excuse the missing draft and entry protections, which are addressed above.
Validation: both targeted mutations fail, the clean Storybook smoke passes 292 stories / 318 renders, and build, renderer/Storybook typechecks, lint, format, ASF headers and architecture checks pass. Playwright discovers 63 tests in 28 files. The retained native journeys were attempted locally, but the shared window fixture timed out before the first test body; the other four did not run after the first-failure stop. I am not claiming a passing local native run. The updated PR description records these limits and the deliberate coverage reductions.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org