Multiple workspaces can share one checkout directory. Agent and terminal status, sidebar tabs, and ownership now attribute strictly by workspace ID, so a running or blocked agent in one workspace no longer shows up on its same-directory siblings. A one-time startup migration backfills workspace IDs onto legacy agents, and every agent and terminal is created with one thereafter. Directory lookups remain only for genuinely folder-derived git facts (branch, diff) and the archive-by-path adapter.
627 lines
33 KiB
TypeScript
627 lines
33 KiB
TypeScript
import { readdirSync, readFileSync } from "node:fs";
|
|
import { join, relative } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { ar } from "./resources/ar";
|
|
import { en } from "./resources/en";
|
|
import { es } from "./resources/es";
|
|
import { fr } from "./resources/fr";
|
|
import { ru } from "./resources/ru";
|
|
import { zhCN } from "./resources/zh-CN";
|
|
|
|
function flattenKeys(value: unknown, prefix = ""): string[] {
|
|
if (typeof value !== "object" || value === null) {
|
|
return [prefix];
|
|
}
|
|
|
|
const entries = Object.entries(value);
|
|
return entries.flatMap(([key, child]) => flattenKeys(child, prefix ? `${prefix}.${key}` : key));
|
|
}
|
|
|
|
function flattenStrings(value: unknown, prefix = ""): Record<string, string> {
|
|
if (typeof value === "string") {
|
|
return { [prefix]: value };
|
|
}
|
|
if (typeof value !== "object" || value === null) {
|
|
return {};
|
|
}
|
|
|
|
return Object.fromEntries(
|
|
Object.entries(value).flatMap(([key, child]) =>
|
|
Object.entries(flattenStrings(child, prefix ? `${prefix}.${key}` : key)),
|
|
),
|
|
);
|
|
}
|
|
|
|
function countMatchingEnglishStrings(resource: unknown): number {
|
|
const englishStrings = flattenStrings(en);
|
|
const localeStrings = flattenStrings(resource);
|
|
return Object.entries(englishStrings).filter(([key, value]) => localeStrings[key] === value)
|
|
.length;
|
|
}
|
|
|
|
function findInterpolationMismatches(resource: unknown): string[] {
|
|
const interpolationPattern = /\{\{[^}]+\}\}/g;
|
|
const englishStrings = flattenStrings(en);
|
|
const localeStrings = flattenStrings(resource);
|
|
return Object.entries(englishStrings).flatMap(([key, value]) => {
|
|
const expected = [...value.matchAll(interpolationPattern)].map((match) => match[0]).sort();
|
|
const actual = [...(localeStrings[key] ?? "").matchAll(interpolationPattern)]
|
|
.map((match) => match[0])
|
|
.sort();
|
|
return expected.join("|") === actual.join("|")
|
|
? []
|
|
: [`${key}: ${expected.join(", ")} -> ${actual.join(", ")}`];
|
|
});
|
|
}
|
|
|
|
const appSourceRoot = join(__dirname, "..");
|
|
const untranslatedConnectionErrors = [
|
|
"Daemon unavailable",
|
|
"Daemon client unavailable",
|
|
"Daemon client not available",
|
|
"Daemon client is disconnected",
|
|
"Host is not connected",
|
|
] as const;
|
|
const untranslatedLocalFallbacks = [
|
|
"No file found for ",
|
|
"Unable to load pull request status",
|
|
"Unable to load pull request activity",
|
|
"An unexpected error occurred while handling dictation.",
|
|
"Unable to load desktop settings.",
|
|
"Unable to save desktop settings.",
|
|
] as const;
|
|
|
|
function collectSourceFiles(directory: string): string[] {
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
const path = join(directory, entry.name);
|
|
if (entry.isDirectory()) {
|
|
if (entry.name === "i18n") {
|
|
return [];
|
|
}
|
|
return collectSourceFiles(path);
|
|
}
|
|
if (!/\.(ts|tsx)$/.test(entry.name) || /\.test\./.test(entry.name)) {
|
|
return [];
|
|
}
|
|
return [path];
|
|
});
|
|
}
|
|
|
|
function findUntranslatedConnectionErrors(): string[] {
|
|
return collectSourceFiles(appSourceRoot).flatMap((path) => {
|
|
const contents = readFileSync(path, "utf8");
|
|
const matches = [...untranslatedConnectionErrors, ...untranslatedLocalFallbacks].filter(
|
|
(text) => contents.includes(`"${text}"`) || contents.includes(`\`${text}`),
|
|
);
|
|
if (matches.length === 0) {
|
|
return [];
|
|
}
|
|
return [`${relative(appSourceRoot, path)}: ${matches.join(", ")}`];
|
|
});
|
|
}
|
|
|
|
describe("translation resources", () => {
|
|
it("keeps UN official language keys in sync with English", () => {
|
|
const englishKeys = flattenKeys(en).sort();
|
|
expect(flattenKeys(ar).sort()).toEqual(englishKeys);
|
|
expect(flattenKeys(es).sort()).toEqual(englishKeys);
|
|
expect(flattenKeys(fr).sort()).toEqual(englishKeys);
|
|
expect(flattenKeys(ru).sort()).toEqual(englishKeys);
|
|
expect(flattenKeys(zhCN).sort()).toEqual(englishKeys);
|
|
});
|
|
|
|
it("keeps non-English UN official languages translated beyond fallback labels", () => {
|
|
const totalStrings = Object.keys(flattenStrings(en)).length;
|
|
const maxFallbackStrings = Math.floor(totalStrings * 0.25);
|
|
expect(countMatchingEnglishStrings(ar)).toBeLessThan(maxFallbackStrings);
|
|
expect(countMatchingEnglishStrings(es)).toBeLessThan(maxFallbackStrings);
|
|
expect(countMatchingEnglishStrings(fr)).toBeLessThan(maxFallbackStrings);
|
|
expect(countMatchingEnglishStrings(ru)).toBeLessThan(maxFallbackStrings);
|
|
});
|
|
|
|
it("preserves interpolation placeholders in every language", () => {
|
|
expect(findInterpolationMismatches(ar)).toEqual([]);
|
|
expect(findInterpolationMismatches(es)).toEqual([]);
|
|
expect(findInterpolationMismatches(fr)).toEqual([]);
|
|
expect(findInterpolationMismatches(ru)).toEqual([]);
|
|
expect(findInterpolationMismatches(zhCN)).toEqual([]);
|
|
});
|
|
|
|
it("keeps reported Spanish settings and scripts labels clean", () => {
|
|
expect(es.workspace.scripts.title).toBe("Scripts");
|
|
expect(es.settings.general.terminalScrollback.label).toBe("Historial de terminal");
|
|
expect(es.settings.project.scripts.title).toBe("Scripts");
|
|
});
|
|
|
|
it("keeps model count labels spaced around the count", () => {
|
|
expect(ar.modelSelector.modelCountPlural).toBe("{{count}} نماذج");
|
|
expect(es.modelSelector.modelCountPlural).toBe("{{count}} modelos");
|
|
expect(fr.modelSelector.modelCountPlural).toBe("{{count}} modèles");
|
|
expect(ru.modelSelector.modelCountPlural).toBe("{{count}} моделей");
|
|
expect(zhCN.modelSelector.modelCountPlural).toBe("{{count}} 个模型");
|
|
expect(ar.settings.providers.models.many).toBe("{{count}} نماذج");
|
|
expect(es.settings.providers.models.many).toBe("{{count}} modelos");
|
|
expect(fr.settings.providers.models.many).toBe("{{count}} modèles");
|
|
expect(ru.settings.providers.models.many).toBe("{{count}} моделей");
|
|
expect(zhCN.settings.providers.models.many).toBe("{{count}} 个 Model");
|
|
});
|
|
|
|
it("keeps local connection fallback errors translated", () => {
|
|
expect(findUntranslatedConnectionErrors()).toEqual([]);
|
|
});
|
|
|
|
it("includes shared shell keys for the Batch 1 migration", () => {
|
|
expect(en.common.actions.back).toBe("Back");
|
|
expect(en.common.actions.cancel).toBe("Cancel");
|
|
expect(en.common.actions.close).toBe("Close");
|
|
expect(en.common.actions.dismiss).toBe("Dismiss");
|
|
expect(en.common.actions.retry).toBe("Retry");
|
|
expect(en.common.actions.search).toBe("Search");
|
|
expect(en.common.states.starting).toBe("Starting...");
|
|
expect(en.common.states.downloadComplete).toBe("Download complete");
|
|
expect(en.common.states.downloadFailed).toBe("Download failed");
|
|
expect(en.shell.menu.toggleSidebar).toBe("Toggle sidebar");
|
|
expect(en.shell.menu.open).toBe("Open menu");
|
|
expect(en.shell.menu.close).toBe("Close menu");
|
|
expect(en.shell.commandCenter.placeholder).toBe("Type a command or search agents...");
|
|
expect(en.shell.commandCenter.noMatches).toBe("No matches");
|
|
expect(en.shell.commandCenter.actions).toBe("Actions");
|
|
expect(en.shell.commandCenter.agents).toBe("Agents");
|
|
expect(en.shell.commandCenter.newAgent).toBe("New agent");
|
|
expect(en.shell.commandCenter.openProject).toBe("Open project");
|
|
expect(en.shell.commandCenter.home).toBe("Home");
|
|
});
|
|
|
|
it("includes composer and agent workflow keys for the Batch 2 migration", () => {
|
|
expect(en.composer.placeholders.desktop).toBe(
|
|
"Message the agent, tag @files, or use /commands and /skills",
|
|
);
|
|
expect(en.composer.input.addAttachment).toBe("Add attachment");
|
|
expect(en.composer.input.sendMessage).toBe("Send message");
|
|
expect(en.composer.voice.startDictation).toBe("Start dictation");
|
|
expect(en.composer.attachments.addIssueOrPr).toBe("Add issue or PR");
|
|
expect(en.composer.github.title).toBe("Attach issue or PR");
|
|
expect(en.agentControls.provider.fallback).toBe("Provider");
|
|
expect(en.agentControls.hints.model).toBe("Change model");
|
|
expect(en.agentControls.features.title).toBe("Features");
|
|
expect(en.agentControls.mode.title).toBe("Mode");
|
|
expect(en.agentStream.permission.required).toBe("Permission Required");
|
|
expect(en.agentStream.permission.proposedPlan).toBe("Proposed plan");
|
|
expect(en.agentPanel.unavailable.selectedHost).toBe("Selected host");
|
|
expect(en.agentPanel.states.notFound).toBe("Agent not found");
|
|
expect(en.panels.draft.newAgent).toBe("New Agent");
|
|
});
|
|
|
|
it("includes Settings expansion keys for the Batch 3A migration", () => {
|
|
expect(en.settings.diagnostics.title).toBe("Diagnostics");
|
|
expect(en.settings.about.title).toBe("About");
|
|
expect(en.settings.about.releaseChannel.label).toBe("Release channel");
|
|
expect(en.settings.appearance.theme.title).toBe("Theme");
|
|
expect(en.settings.appearance.fonts.interfaceFont).toBe("Interface font");
|
|
expect(en.settings.shortcuts.actions.rebind).toBe("Rebind");
|
|
expect(en.settings.integrations.commandLine.title).toBe("Command line");
|
|
expect(en.settings.integrations.skills.updateAvailable).toBe("Update available");
|
|
expect(en.settings.permissions.notifications).toBe("Notifications");
|
|
expect(en.settings.permissions.actions.request).toBe("Request");
|
|
});
|
|
|
|
it("includes Settings expansion keys for the Batch 3B migration", () => {
|
|
expect(en.settings.host.notFound).toBe("Host not found");
|
|
expect(en.settings.host.connections.title).toBe("Connections");
|
|
expect(en.settings.host.daemon.restart.title).toBe("Restart daemon");
|
|
expect(en.settings.host.orchestration.enableTools.title).toBe("Enable Paseo tools");
|
|
expect(en.settings.providers.title).toBe("Providers");
|
|
expect(en.settings.providers.models.addModel).toBe("Add model");
|
|
expect(en.settings.providers.diagnostic.title).toBe("Diagnostic");
|
|
expect(en.settings.project.worktree.title).toBe("Worktree lifecycle hooks");
|
|
expect(en.settings.project.scripts.actions.add).toBe("Add script");
|
|
expect(en.settings.project.metadata.title).toBe("Metadata generation");
|
|
expect(en.settings.project.actions.save).toBe("Save");
|
|
});
|
|
|
|
it("includes workspace and panel keys for the Batch 4A migration", () => {
|
|
expect(en.importSession.title).toBe("Import session");
|
|
expect(en.importSession.status.connectHost).toBe("Connect to a host to import sessions");
|
|
expect(en.importSession.actions.refresh).toBe("Refresh sessions");
|
|
expect(en.workspace.fileExplorer.sort.name).toBe("Name");
|
|
expect(en.workspace.fileExplorer.actions.hideHiddenFiles).toBe("Hide hidden files");
|
|
expect(en.workspace.fileExplorer.actions.showHiddenFiles).toBe("Show hidden files");
|
|
expect(en.workspace.fileExplorer.empty.noFiles).toBe("No files");
|
|
expect(en.workspace.fileExplorer.empty.noVisibleFiles).toBe("No visible files");
|
|
expect(en.workspace.setup.status.running).toBe("Running");
|
|
expect(en.workspace.setup.empty.noCommands).toBe("No setup commands ran for this workspace.");
|
|
expect(en.workspace.browser.unavailable.title).toBe("Browser is desktop-only");
|
|
expect(en.workspace.browser.controls.enterUrl).toBe("Enter URL");
|
|
expect(en.workspace.terminal.hostDisconnected).toBe("Host is not connected");
|
|
expect(en.panels.file.directoryMissing).toBe("Workspace directory not found.");
|
|
});
|
|
|
|
it("includes workspace Git and review keys for the Batch 4B migration", () => {
|
|
expect(en.workspace.tabs.actions.newAgent).toBe("New agent");
|
|
expect(en.workspace.header.actions.copyPath).toBe("Copy workspace path");
|
|
expect(en.workspace.scripts.actions.run).toBe("Run");
|
|
expect(en.workspace.git.actions.commit.label).toBe("Commit");
|
|
expect(en.workspace.git.diff.binaryFile).toBe("Binary file");
|
|
expect(en.workspace.git.pr.sections.checks).toBe("Checks");
|
|
expect(en.review.comment.placeholder).toBe("Leave a comment");
|
|
});
|
|
|
|
it("includes sidebar and workspace creation keys for the Batch 4C migration", () => {
|
|
expect(en.sidebar.workspace.actions.copyPath).toBe("Copy path");
|
|
expect(en.sidebar.project.confirmations.removeTitle).toBe("Remove project?");
|
|
expect(en.newWorkspace.title).toBe("New workspace");
|
|
expect(en.newWorkspace.refPicker.searchPlaceholder).toBe("Search branches and PRs");
|
|
expect(en.openProject.tiles.addProject.title).toBe("Add a project");
|
|
});
|
|
|
|
it("resolves the worktree delete prompt keys used by the sidebar prompt", () => {
|
|
const strings = flattenStrings(en);
|
|
|
|
expect(strings["sidebar.workspace.confirmations.deleteWorktreePrompt.title"]).toBe(
|
|
"Archive workspace",
|
|
);
|
|
expect(strings["sidebar.workspace.confirmations.deleteWorktreePrompt.message"]).toBe(
|
|
"Also remove the worktree from disk?",
|
|
);
|
|
expect(strings["sidebar.workspace.confirmations.deleteWorktreePrompt.keep"]).toBe(
|
|
"Keep on disk",
|
|
);
|
|
expect(strings["sidebar.workspace.confirmations.deleteWorktreePrompt.delete"]).toBe("Delete");
|
|
});
|
|
|
|
it("includes provider selector and pairing keys for the Batch 4D migration", () => {
|
|
expect(en.modelSelector.title).toBe("Select provider");
|
|
expect(en.modelSelector.favorites).toBe("Favorites");
|
|
expect(en.providerCatalog.title).toBe("Add provider");
|
|
expect(en.providerCatalog.actions.installInstructions).toBe("Install instructions");
|
|
expect(en.pairing.link.title).toBe("Paste pairing link");
|
|
expect(en.pairing.connectionMethods.direct.title).toBe("Direct connection");
|
|
});
|
|
|
|
it("includes onboarding and direct connection keys for the Batch 4E migration", () => {
|
|
expect(en.onboarding.title).toBe("Welcome to Paseo");
|
|
expect(en.onboarding.actions.settings).toBe("Settings");
|
|
expect(en.pairing.direct.title).toBe("Direct connection");
|
|
expect(en.pairing.direct.fields.host).toBe("Host");
|
|
expect(en.pairing.scan.title).toBe("Scan QR");
|
|
expect(en.pairing.device.copy).toBe("Copy");
|
|
});
|
|
|
|
it("includes shared utility chrome keys for the Batch 4F migration", () => {
|
|
expect(en.realtimeVoice.actions.mute).toBe("Mute realtime voice");
|
|
expect(en.rewind.actions.conversation).toBe("Rewind conversation");
|
|
expect(en.rewind.warning).toBe("This action cannot be undone");
|
|
expect(en.diffViewer.empty).toBe("No changes to display");
|
|
expect(en.serviceUrl.title).toBe("Open service URL");
|
|
});
|
|
|
|
it("includes keyboard shortcut help keys for the Batch 4G migration", () => {
|
|
expect(en.settings.shortcuts.dialogTitle).toBe("Shortcuts");
|
|
expect(en.settings.shortcuts.sections.tabsPanes).toBe("Tabs & Panes");
|
|
expect(en.settings.shortcuts.help.toggleCommandCenter).toBe("Toggle command center");
|
|
expect(en.settings.shortcuts.helpNotes.showKeyboardShortcuts).toBe(
|
|
"Available when focus is not in a text field or terminal.",
|
|
);
|
|
});
|
|
|
|
it("includes sessions and agent list keys for the Batch 4H migration", () => {
|
|
expect(en.sessions.title).toBe("Agent history");
|
|
expect(en.sessions.empty).toBe("No sessions yet");
|
|
expect(en.sessions.actions.loadMore).toBe("Load more");
|
|
expect(en.agentList.fallbackTitle).toBe("New session");
|
|
expect(en.agentList.dateSections.today).toBe("Today");
|
|
expect(en.agentList.dateSections.older).toBe("Older");
|
|
expect(en.agentList.status.initializing).toBe("Starting");
|
|
expect(en.agentList.status.running).toBe("Running");
|
|
expect(en.agentList.badges.archived).toBe("Archived");
|
|
expect(en.agentList.badges.pending).toBe("{{count}} pending");
|
|
expect(en.agentList.badges.attention).toBe("Attention");
|
|
expect(en.agentList.archiveSheet.hostOffline).toBe("Host offline");
|
|
expect(en.agentList.archiveSheet.runningAgent).toBe(
|
|
"This agent is still running. Archiving it will stop the agent.",
|
|
);
|
|
expect(en.agentList.archiveSheet.archive).toBe("Archive");
|
|
});
|
|
|
|
it("includes message utility keys for the Batch 4I migration", () => {
|
|
expect(en.message.actions.copyCode).toBe("Copy code");
|
|
expect(en.message.actions.copyTurn).toBe("Copy turn");
|
|
expect(en.message.actions.copyMessage).toBe("Copy message");
|
|
expect(en.message.actions.copied).toBe("Copied");
|
|
expect(en.message.attachments.dismissImage).toBe("Dismiss image");
|
|
expect(en.message.attachments.closeImage).toBe("Close image");
|
|
expect(en.message.attachments.imageLoadFailed).toBe("Couldn't load image");
|
|
expect(en.message.attachments.imageUnavailable).toBe("Image unavailable");
|
|
expect(en.message.dictation.start).toBe("Start voice dictation");
|
|
expect(en.message.dictation.cancel).toBe("Cancel dictation");
|
|
expect(en.message.dictation.retry).toBe("Retry dictation");
|
|
expect(en.message.dictation.insert).toBe("Insert transcription");
|
|
expect(en.message.dictation.insertAndSend).toBe("Insert transcription and send");
|
|
expect(en.message.dictation.failed).toBe("Dictation failed: {{error}}");
|
|
expect(en.message.dictation.failedRetry).toBe("Dictation failed. Tap retry.");
|
|
expect(en.message.question.submit).toBe("Submit");
|
|
expect(en.message.question.answerPlaceholder).toBe("Type your answer...");
|
|
expect(en.message.question.otherPlaceholder).toBe("Other...");
|
|
expect(en.message.todo.title).toBe("Tasks");
|
|
expect(en.message.todo.empty).toBe("No tasks yet.");
|
|
});
|
|
|
|
it("includes workspace tab toast keys for the Batch 4J migration", () => {
|
|
expect(en.workspace.tabs.emptyPane).toBe("No tabs in this pane.");
|
|
expect(en.workspace.tabs.toasts.copyFailed).toBe("Copy failed");
|
|
expect(en.workspace.tabs.toasts.agentIdCopiedLabel).toBe("Agent ID");
|
|
expect(en.workspace.tabs.toasts.resumeCommandCopiedLabel).toBe("resume command");
|
|
expect(en.workspace.tabs.toasts.resumeIdUnavailable).toBe("Resume ID not available");
|
|
expect(en.workspace.tabs.toasts.resumeCommandUnavailable).toBe("Resume command not available");
|
|
expect(en.workspace.tabs.toasts.reloadingAgent).toBe("Reloading agent...");
|
|
expect(en.workspace.tabs.toasts.reloadedAgent).toBe("Reloaded agent");
|
|
expect(en.workspace.tabs.toasts.failedToReloadAgent).toBe("Failed to reload agent");
|
|
expect(en.workspace.header.toasts.workspacePathCopiedLabel).toBe("Workspace path");
|
|
expect(en.workspace.header.toasts.branchNameCopiedLabel).toBe("Branch name");
|
|
});
|
|
|
|
it("includes sidebar project list keys for the Batch 4K migration", () => {
|
|
expect(en.sidebar.host.noHost).toBe("No host");
|
|
expect(en.sidebar.host.switchTitle).toBe("Switch host");
|
|
expect(en.sidebar.host.searchPlaceholder).toBe("Search hosts...");
|
|
expect(en.sidebar.actions.addProject).toBe("Add project");
|
|
expect(en.sidebar.actions.home).toBe("Home");
|
|
expect(en.sidebar.actions.settings).toBe("Settings");
|
|
expect(en.sidebar.actions.closeSidebar).toBe("Close sidebar");
|
|
expect(en.sidebar.sections.sessions).toBe("History");
|
|
expect(en.sidebar.workspace.actions.newWorkspace).toBe("New workspace");
|
|
expect(en.sidebar.workspace.actions.createWorkspaceFor).toBe(
|
|
"Create a new workspace for {{projectName}}",
|
|
);
|
|
expect(en.sidebar.project.empty.title).toBe("No projects yet");
|
|
expect(en.sidebar.project.empty.description).toBe("Add a project to get started");
|
|
expect(en.settings.projectList.hostLoadFailed).toBe(
|
|
"Couldn't load projects from host {{hostName}}: {{message}}",
|
|
);
|
|
});
|
|
|
|
it("includes picker, file pane, and tool detail keys for the Batch 4L migration", () => {
|
|
expect(en.projectPicker.placeholder).toBe("Type a directory path...");
|
|
expect(en.projectPicker.opening).toBe("Opening project...");
|
|
expect(en.projectPicker.empty).toBe("Start typing a path");
|
|
expect(en.branchSwitcher.currentBranch).toBe(
|
|
"Current branch: {{branchName}}. Press to switch branch.",
|
|
);
|
|
expect(en.branchSwitcher.placeholder).toBe("Switch branch...");
|
|
expect(en.branchSwitcher.searchPlaceholder).toBe("Filter branches...");
|
|
expect(en.branchSwitcher.empty).toBe("No branches found.");
|
|
expect(en.branchSwitcher.title).toBe("Switch branch");
|
|
expect(en.panels.file.loading).toBe("Loading file...");
|
|
expect(en.panels.file.noPreview).toBe("No preview available");
|
|
expect(en.panels.file.binaryPreviewUnavailable).toBe("Binary preview unavailable");
|
|
expect(en.panels.file.failedToLoad).toBe("Failed to load file");
|
|
expect(en.toolCallDetails.error).toBe("Error");
|
|
expect(en.toolCallDetails.empty).toBe("No additional details available");
|
|
expect(en.message.actions.openFile).toBe("Open file");
|
|
});
|
|
|
|
it("includes hook and modal utility keys for the Batch 4M migration", () => {
|
|
expect(en.imageAttachmentPicker.permissionTitle).toBe("Permission required");
|
|
expect(en.imageAttachmentPicker.permissionMessage).toBe(
|
|
"Please allow access to your photo library to attach images.",
|
|
);
|
|
expect(en.imageAttachmentPicker.errorTitle).toBe("Error");
|
|
expect(en.imageAttachmentPicker.failedToSelect).toBe("Failed to select image");
|
|
expect(en.imageAttachmentPicker.dialogTitle).toBe("Attach images");
|
|
expect(en.imageAttachmentPicker.dialogFilterName).toBe("Images");
|
|
expect(en.common.states.copied).toBe("Copied");
|
|
expect(en.common.states.copiedLabel).toBe("Copied {{label}}");
|
|
expect(en.common.errors.unableToSave).toBe("Unable to save");
|
|
expect(en.common.errors.nameRequired).toBe("Name is required");
|
|
expect(en.common.errors.daemonUnavailable).toBe("Daemon unavailable");
|
|
expect(en.common.errors.daemonClientUnavailable).toBe("Daemon client unavailable");
|
|
expect(en.common.errors.daemonClientDisconnected).toBe("Daemon client is disconnected");
|
|
expect(en.common.errors.noFileFound).toBe("No file found for {{token}}");
|
|
expect(en.common.errors.unexpectedDictationError).toBe(
|
|
"An unexpected error occurred while handling dictation.",
|
|
);
|
|
expect(en.common.connectionStatus.online).toBe("Online");
|
|
expect(en.common.connectionStatus.connecting).toBe("Connecting");
|
|
expect(en.common.connectionStatus.offline).toBe("Offline");
|
|
expect(en.common.connectionStatus.idle).toBe("Idle");
|
|
expect(en.agentList.dateSections.recent).toBe("Recent");
|
|
expect(en.message.attachments.imagePreviewUnavailable).toBe("Image preview unavailable.");
|
|
expect(en.message.attachments.imagePreviewLoadFailed).toBe("Unable to load image preview.");
|
|
expect(en.workspace.tabs.explorer.changes).toBe("Changes");
|
|
expect(en.workspace.tabs.explorer.files).toBe("Files");
|
|
expect(en.branchSwitcher.uncommittedTitle).toBe("Uncommitted changes");
|
|
expect(en.branchSwitcher.uncommittedMessage).toBe(
|
|
"You have uncommitted changes. Stash them before switching branches?",
|
|
);
|
|
expect(en.branchSwitcher.stashAndSwitch).toBe("Stash & Switch");
|
|
expect(en.branchSwitcher.failedToStash).toBe("Failed to stash changes");
|
|
expect(en.branchSwitcher.failedToSwitch).toBe("Failed to switch branch");
|
|
expect(en.workspaceSetup.errors.failedCreateWorktree).toBe("Failed to create worktree");
|
|
expect(en.workspaceSetup.errors.failedOpenProject).toBe("Failed to open project");
|
|
expect(en.workspaceSetup.errors.selectModel).toBe("Select a model");
|
|
expect(en.workspaceSetup.errors.hostDisconnected).toBe("Host is not connected");
|
|
expect(en.workspaceSetup.errors.pendingRequired).toBe("No workspace setup is pending");
|
|
expect(en.workspaceSetup.errors.composerStateRequired).toBe(
|
|
"Workspace setup composer state is required",
|
|
);
|
|
expect(en.workspaceSetup.title).toBe("Create workspace");
|
|
expect(en.workspace.git.pr.errors.statusLoadFailed).toBe("Unable to load pull request status");
|
|
expect(en.workspace.git.pr.errors.activityLoadFailed).toBe(
|
|
"Unable to load pull request activity",
|
|
);
|
|
expect(en.desktop.settings.loadFailed).toBe("Unable to load desktop settings.");
|
|
expect(en.desktop.settings.saveFailed).toBe("Unable to save desktop settings.");
|
|
expect(en.toolCallDetails.input).toBe("Input");
|
|
expect(en.toolCallDetails.output).toBe("Output");
|
|
expect(en.renameModal.rename).toBe("Rename");
|
|
expect(en.renameModal.saving).toBe("Saving...");
|
|
expect(en.sidebarCallout.dismiss).toBe("Dismiss");
|
|
expect(en.contextWindow.title).toBe("Context window");
|
|
expect(en.contextWindow.used).toBe("{{percentage}}% used");
|
|
});
|
|
|
|
it("includes view-model and policy utility keys for the Batch 4N migration", () => {
|
|
expect(en.importSession.preview.untitledSession).toBe("Untitled session");
|
|
expect(en.importSession.preview.noPrompt).toBe("No prompt preview");
|
|
expect(en.importSession.empty.noRecent).toBe("No recent sessions to import.");
|
|
expect(en.importSession.empty.alreadyImported).toBe(
|
|
"All recent sessions are already imported.",
|
|
);
|
|
expect(en.importSession.empty.noProviderSessions).toBe("No {{provider}} sessions found.");
|
|
expect(en.sidebar.worktreeSetup.title).toBe("Set up worktree scripts");
|
|
expect(en.sidebar.worktreeSetup.description).toBe(
|
|
"Add setup commands so new worktrees can install dependencies and prepare themselves automatically.",
|
|
);
|
|
expect(en.sidebar.worktreeSetup.openProjectSettings).toBe("Open project settings");
|
|
});
|
|
|
|
it("includes remaining small utility chrome keys for the Batch 4O migration", () => {
|
|
expect(en.workspace.route.loading).toBe("Loading workspace");
|
|
expect(en.workspace.route.connecting).toBe("Connecting");
|
|
expect(en.workspace.route.hostOffline).toBe("{{hostName}} is offline");
|
|
expect(en.workspace.route.cannotReachHost).toBe("Cannot reach {{hostName}}");
|
|
expect(en.workspace.route.hostStatus).toBe("Host status: {{status}}");
|
|
expect(en.workspace.route.missing).toBe("Workspace not found");
|
|
expect(en.message.compaction.loading).toBe("Compacting...");
|
|
expect(en.message.compaction.auto).toBe("Context automatically compacted");
|
|
expect(en.message.compaction.manual).toBe("Context manually compacted");
|
|
expect(en.message.compaction.withTokens).toBe("Context compacted ({{tokens}}K tokens)");
|
|
expect(en.message.compaction.completed).toBe("Context compacted");
|
|
expect(en.agentPanel.archived.callout).toBe("This agent is archived");
|
|
expect(en.agentPanel.archived.unarchive).toBe("Unarchive");
|
|
expect(en.desktop.quitting.title).toBe("Quitting Paseo...");
|
|
expect(en.desktop.quitting.detail).toBe("Stopping the local daemon.");
|
|
expect(en.composer.attachments.dropImagesHere).toBe("Drop images here");
|
|
});
|
|
|
|
it("includes provider selection utility keys for the Batch 4P migration", () => {
|
|
expect(en.providerSelection.defaultModel).toBe("Default");
|
|
expect(en.providerSelection.selectModel).toBe("Select model");
|
|
expect(en.providerSelection.loading).toBe("Loading...");
|
|
expect(en.providerSelection.error).toBe("Error");
|
|
expect(en.providerSelection.unavailable).toBe("Unavailable");
|
|
expect(en.providerSelection.unknownError).toBe("Unknown error");
|
|
expect(en.providerSelection.readiness.initialPromptRequired).toBe("Initial prompt is required");
|
|
expect(en.providerSelection.readiness.noProviders).toBe(
|
|
"No available providers on the selected host",
|
|
);
|
|
expect(en.providerSelection.readiness.modelDefaultsLoading).toBe(
|
|
"Model defaults are still loading",
|
|
);
|
|
});
|
|
|
|
it("includes desktop update utility keys for the Batch 4Q migration", () => {
|
|
expect(en.desktop.updates.status.checking).toBe("Checking for app updates...");
|
|
expect(en.desktop.updates.status.installing).toBe("Installing app update...");
|
|
expect(en.desktop.updates.status.upToDate).toBe("App is up to date.");
|
|
expect(en.desktop.updates.status.pending).toBe("We'll let you know when the update is ready.");
|
|
expect(en.desktop.updates.status.availableWithVersion).toBe("Update ready: {{version}}");
|
|
expect(en.desktop.updates.status.available).toBe("An app update is ready to install.");
|
|
expect(en.desktop.updates.status.installed).toBe("App update installed. Restart required.");
|
|
expect(en.desktop.updates.status.failed).toBe("Failed to update app.");
|
|
expect(en.desktop.updates.status.idle).toBe("Update status has not been checked yet.");
|
|
expect(en.desktop.updates.installError).toBe("Unable to install the desktop app update.");
|
|
expect(en.desktop.updates.callout.installingTitle).toBe("Installing update");
|
|
expect(en.desktop.updates.callout.failedTitle).toBe("Update failed");
|
|
expect(en.desktop.updates.callout.availableTitle).toBe("Update available");
|
|
expect(en.desktop.updates.callout.genericError).toBe("Something went wrong.");
|
|
expect(en.desktop.updates.callout.whatsNew).toBe("What's new");
|
|
expect(en.desktop.updates.callout.installAndRestart).toBe("Install & restart");
|
|
expect(en.desktop.updates.callout.installingDescription).toBe("Installing and restarting...");
|
|
expect(en.desktop.updates.callout.versionReady).toBe("{{version}} is ready to install.");
|
|
expect(en.desktop.updates.callout.newVersionReady).toBe("A new version is ready to install.");
|
|
expect(en.desktop.updates.callout.restartWarning).toBe(
|
|
"Upgrading the app will stop running agents and close terminal sessions.",
|
|
);
|
|
expect(en.desktop.rosetta.title).toBe("Download the Apple Silicon build");
|
|
expect(en.desktop.rosetta.runningIntel).toBe(
|
|
"You're running the Intel build of Paseo under Rosetta on Apple Silicon.",
|
|
);
|
|
expect(en.desktop.rosetta.highCpu).toBe(
|
|
"This causes high CPU usage. Download the Apple Silicon build to fix it.",
|
|
);
|
|
expect(en.desktop.rosetta.download).toBe("Download");
|
|
});
|
|
|
|
it("includes desktop permission utility keys for the Batch 4R migration", () => {
|
|
expect(en.desktop.permissions.notifications.allowed).toBe(
|
|
"Notifications are allowed by the OS.",
|
|
);
|
|
expect(en.desktop.permissions.notifications.denied).toBe(
|
|
"Notifications are denied in system settings.",
|
|
);
|
|
expect(en.desktop.permissions.notifications.unexpectedState).toBe(
|
|
"Unexpected notification permission state: {{state}}",
|
|
);
|
|
expect(en.desktop.permissions.microphone.granted).toBe("Microphone access is granted.");
|
|
expect(en.desktop.permissions.microphone.statusApiUnavailable).toBe(
|
|
"Microphone status API is unavailable in this runtime. Use Request to check access.",
|
|
);
|
|
expect(en.desktop.permissions.microphone.requestDenied).toBe(
|
|
"Microphone permission was denied by the user or system.",
|
|
);
|
|
expect(en.desktop.permissions.empty.notifications).toBe(
|
|
"Notification status has not been checked yet.",
|
|
);
|
|
expect(en.desktop.permissions.testNotification.title).toBe("Paseo notification test");
|
|
expect(en.desktop.permissions.testNotification.failed).toBe("Failed to send notification.");
|
|
});
|
|
|
|
it("includes desktop daemon settings keys for the Batch 4S migration", () => {
|
|
expect(en.desktop.daemon.title).toBe("Daemon");
|
|
expect(en.desktop.daemon.status.title).toBe("Status");
|
|
expect(en.desktop.daemon.status.builtInOnly).toBe(
|
|
"Only the built-in desktop daemon is shown here",
|
|
);
|
|
expect(en.desktop.daemon.status.notRunning).toBe("not running");
|
|
expect(en.desktop.daemon.status.pid).toBe("PID {{pid}}");
|
|
expect(en.desktop.daemon.management.pauseTitle).toBe("Pause built-in daemon");
|
|
expect(en.desktop.daemon.management.pauseAndStop).toBe("Pause and stop");
|
|
expect(en.desktop.daemon.logs.modalTitle).toBe("Daemon logs");
|
|
expect(en.desktop.daemon.logs.unavailable).toBe("Log path unavailable");
|
|
expect(en.desktop.daemon.fullStatus.modalTitle).toBe("Daemon status");
|
|
expect(en.desktop.daemon.fullStatus.fetchFailed).toBe(
|
|
"Failed to fetch daemon status: {{message}}",
|
|
);
|
|
expect(en.desktop.daemon.loadFailed).toBe("Unable to load desktop daemon status.");
|
|
expect(en.desktop.integrations.cli.installFailed).toBe("Unable to install the Paseo CLI.");
|
|
expect(en.desktop.integrations.skills.installFailed).toBe(
|
|
"Unable to install orchestration skills.",
|
|
);
|
|
});
|
|
|
|
it("includes remaining utility chrome keys for the Batch 4T migration", () => {
|
|
expect(en.message.attachments.review).toBe("Review");
|
|
expect(en.message.attachments.commentsOne).toBe("1 comment");
|
|
expect(en.message.attachments.commentsMany).toBe("{{count}} comments");
|
|
expect(en.message.attachments.textAttachment).toBe("Text attachment");
|
|
expect(en.composer.attachments.element).toBe("Element");
|
|
expect(en.workspace.hoverCard.scriptsAccessibility).toBe("Workspace scripts");
|
|
expect(en.branchSwitcher.restoreStashTitle).toBe("Restore stashed changes?");
|
|
expect(en.branchSwitcher.stashRestored).toBe("Stashed changes restored");
|
|
expect(en.agentAutocomplete.searchingWorkspace).toBe("Searching workspace...");
|
|
expect(en.agentAutocomplete.noCommands).toBe("No commands found");
|
|
expect(en.agentAutocomplete.failedToLoad).toBe("Failed to load");
|
|
expect(en.loadOlderHistory.failed).toBe("Couldn't load older history");
|
|
expect(en.agentControls.thinking.extraHigh).toBe("Extra high");
|
|
expect(en.agentControls.model.unknown).toBe("Unknown model");
|
|
expect(en.panels.draft.creatingAgent).toBe("Creating agent");
|
|
});
|
|
|
|
it("includes shared default utility keys for the Batch 4U migration", () => {
|
|
expect(en.common.actions.select).toBe("Select");
|
|
expect(en.common.placeholders.search).toBe("Search...");
|
|
expect(en.common.empty.noResults).toBe("No results found");
|
|
expect(en.common.empty.noOptionsMatchSearch).toBe("No options match your search.");
|
|
expect(en.toolCallDetails.subAgentActivity).toBe("Sub-agent activity");
|
|
expect(en.panels.file.failedToLoadPreview).toBe("Failed to load file preview");
|
|
});
|
|
|
|
it("includes remaining local wrapper keys for the Batch 4W migration", () => {
|
|
expect(en.workspace.header.toasts.branchNameUnavailable).toBe("Branch name not available");
|
|
expect(en.startup.logs.loading).toBe("Loading daemon logs...");
|
|
expect(en.startup.logs.unavailable).toBe("No daemon logs available.");
|
|
expect(en.startup.logs.loadFailed).toBe("Unable to load daemon logs: {{message}}");
|
|
});
|
|
});
|