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
1 change: 0 additions & 1 deletion apps/desktop/renderer-architecture.json
Original file line number Diff line number Diff line change
Expand Up @@ -4305,7 +4305,6 @@
"../features/connection-settings": 1,
"./oauth-login-flow-guard": 1,
"./runtime-host-settings-target.js": 1,
"@maka/core/redaction": 1,
"@maka/core/ui-locale": 1,
"@maka/ui": 1,
"react": 1
Expand Down
8 changes: 4 additions & 4 deletions apps/desktop/src/main/__tests__/computer-use-host.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,27 @@ describe('Computer Use host health', () => {
it('does not report a binary-only executor as healthy before first use', () => {
assert.deepEqual(computerUseServiceHealth('maka-cu', snapshot('idle')), {
state: 'not_run',
reason: 'maka-cu 已可用,将在首次调用时启动。',
reason: 'cu_executor_lazy_start',
});
});

it('reports ready, recovery, and unavailable states', () => {
assert.equal(computerUseServiceHealth('maka-cu', snapshot('ready')).state, 'healthy');
assert.equal(
computerUseServiceHealth('maka-cu', snapshot('backing_off')).reason,
'maka-cu executor 正在启动或恢复。',
'cu_executor_recovering',
);
assert.equal(
computerUseServiceHealth('maka-cu', snapshot('starting')).state,
'degraded',
);
assert.deepEqual(computerUseServiceHealth('maka-cu', snapshot('unavailable')), {
state: 'not_available',
reason: 'maka-cu executor 启动失败或已退出。',
reason: 'cu_executor_start_failed',
});
assert.deepEqual(computerUseServiceHealth('maka-cu', snapshot('disposed')), {
state: 'not_available',
reason: 'maka-cu executor 已停止。',
reason: 'cu_executor_stopped',
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('importGitHubCopilotLocalCredential', () => {
assert.equal(imported.result.ok, false);
if (!imported.result.ok) {
assert.equal(imported.result.reason, 'token_exchange_failed');
assert.match(imported.result.message, /不支持 classic PAT/);
assert.equal(imported.result.code, 'copilot_classic_pat_unsupported');
assert.equal(imported.result.message.includes('ghp_classic_pat'), false);
}
assert.equal(imported.secret, undefined);
Expand All @@ -83,7 +83,7 @@ describe('importGitHubCopilotLocalCredential', () => {
});

assert.equal(imported.result.ok, false);
if (!imported.result.ok) assert.match(imported.result.message, /凭据类型不受支持/);
if (!imported.result.ok) assert.equal(imported.result.code, 'copilot_credential_type_unsupported');
assert.equal(imported.secret, undefined);
});

Expand All @@ -95,7 +95,7 @@ describe('importGitHubCopilotLocalCredential', () => {
});

assert.equal(imported.result.ok, false);
if (!imported.result.ok) assert.match(imported.result.message, /未找到可导入/);
if (!imported.result.ok) assert.equal(imported.result.code, 'copilot_local_credential_missing');
assert.equal(imported.secret, undefined);
});
});
60 changes: 60 additions & 0 deletions apps/desktop/src/main/__tests__/oauth-result-copy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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 assert from "node:assert/strict";
import test from "node:test";
// Lives under main/__tests__ on purpose: desktop main tests run from dist via
// `node --test "dist/main/**/*.test.js"`, and settings-provider-copy is a pure
// copy module (no react), so it is safe to exercise from node. Same precedent
// as permission-center-copy.test.ts.
import {
connectionTestFailureMessage,
subscriptionResultMessage,
} from "../../renderer/features/connection-settings/index.js";

test("renders a coded Copilot import failure per locale, ignoring its machine message", () => {
const result = { code: "copilot_subscription_unavailable", message: "copilot_subscription_unavailable" };
assert.equal(subscriptionResultMessage(result, "fallback", "zh"), "当前 GitHub 账号没有可用的 Copilot 订阅权限。");
assert.equal(subscriptionResultMessage(result, "fallback", "en"), "This GitHub account has no usable Copilot subscription.");
});

test("renders the typed experimental_disabled reason per locale", () => {
const result = { reason: "experimental_disabled", message: "enrollment is disabled for this provider" };
assert.equal(subscriptionResultMessage(result, "fallback", "zh"), "本机未启用该账号登录方式;可改用导入兼容凭据,或由管理员启用后重试。");
assert.equal(subscriptionResultMessage(result, "fallback", "en"), "This sign-in is not enabled on this install. Import a compatible credential instead, or ask an operator to enable it.");
});

test("falls back to catalog copy for an unknown code instead of the raw message", () => {
const result = { code: "not_a_known_code", message: "内部错误" };
assert.equal(subscriptionResultMessage(result, "fallback", "en"), "fallback");
assert.equal(subscriptionResultMessage(result, "fallback", "zh"), "fallback");
});

test("renders provider rate limits consistently from the stable status code", () => {
const result = { ok: false, statusCode: 429, errorClass: "provider_unavailable" } as const;
const troubleshooting = { auth: "auth", recheck: "recheck" };
assert.equal(
connectionTestFailureMessage(result, troubleshooting, "zh"),
"当前账号或模型服务触发速率限制,请稍后重试。",
);
assert.equal(
connectionTestFailureMessage(result, troubleshooting, "en"),
"This account or model service is rate-limited. Try again later.",
);
});
26 changes: 26 additions & 0 deletions apps/desktop/src/main/__tests__/permission-center-copy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,29 @@ test('presents a granted OS permission as a verified success', () => {
assert.equal(getPermissionCenterCopy('zh').osStates.granted.tone, 'success');
assert.equal(getPermissionCenterCopy('en').osStates.granted.tone, 'success');
});

test('renders capability reason codes per locale', () => {
const zh = getPermissionCenterCopy('zh');
const en = getPermissionCenterCopy('en');
assert.equal(zh.reasons['missing platform credentials'], '未配置平台凭据');
assert.equal(en.reasons['missing platform credentials'], 'Platform credentials are not configured');
assert.equal(zh.reasons.cu_executor_recovering, 'maka-cu executor 正在启动或恢复。');
assert.equal(en.reasons.cu_executor_recovering, 'The maka-cu executor is starting or recovering.');
});

test('composes the computer-use backend status from snapshot facts per locale', () => {
const zh = getPermissionCenterCopy('zh');
const en = getPermissionCenterCopy('en');
assert.equal(
zh.cuBackendStatus(['辅助功能', '屏幕录制'], 'healthy'),
'maka-cu artifact 已通过本地完整性检查。等待辅助功能、屏幕录制权限。操作与截图 service 已就绪;按目标与动作类别授权后可操作本机应用。',
);
assert.equal(
zh.cuBackendStatus([], 'not_run'),
'maka-cu artifact 已通过本地完整性检查。service 将在首次调用时启动;按目标与动作类别授权后可操作本机应用。',
);
assert.equal(
en.cuBackendStatus(['Accessibility'], 'degraded'),
'The maka-cu artifact passed the local integrity check. Waiting for Accessibility permission. The maka-cu service is starting or recovering.',
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function attachmentReadHandler(
): Handler {
const handlers = new Map<string, Handler>();
registerRuntimeHostArtifactsIpc({
uiLocale: () => 'zh' as const,
ipcMain: {
handle: (channel, handler) => handlers.set(channel, handler as Handler),
},
Expand Down Expand Up @@ -121,6 +122,7 @@ test("Runtime Host Artifact IPC preserves previews and streams complete exports"

try {
registerRuntimeHostArtifactsIpc({
uiLocale: () => 'zh' as const,
ipcMain: {
handle: (channel, handler) => handlers.set(channel, handler as Handler),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ test('does not project or open remote Runtime Host file paths', async () => {
assert.deepEqual(projected.backups.map(({ path }) => path), ['']);
assert.deepEqual(opened, {
ok: false,
code: 'remote_host_owned',
message: 'Memory files are owned by the remote Runtime Host',
});
});
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/main/browser-message-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

import type { UiCatalog } from '@maka/core/ui-locale';
import { randomUUID } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
Expand Down Expand Up @@ -348,7 +349,7 @@ export function buildBrowserMessageBoxHtml(

function renderBrowserMessageBoxHtml(input: BrowserMessageBoxPresentation): string {
const nonce = randomUUID().replaceAll('-', '');
const closeLabel = input.locale === 'zh' ? '关闭' : 'Close';
const closeLabel = CLOSE_LABEL[input.locale];
const closeButton = `<button class="window-close" type="button" data-response="${input.cancelId}" aria-label="${closeLabel}">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12" /></svg>
</button>`;
Expand Down Expand Up @@ -638,3 +639,5 @@ function escapeHtml(value: string): string {
return entities[character] ?? character;
});
}

const CLOSE_LABEL = { zh: '关闭', en: 'Close' } satisfies UiCatalog<string>;
Loading