mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
test: remove copy inventories and redundant formatter replays (#35890)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,24 +1,11 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { i18n, translate } from '@/i18n/i18n'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import type * as I18nModule from '@/i18n/i18n'
|
||||
import en from '@/i18n/locales/en.json'
|
||||
import zh from '@/i18n/locales/zh.json'
|
||||
import {
|
||||
AGENT_SESSION_ATTACHMENT_PROBLEM_REASONS,
|
||||
AGENT_SESSION_FAILURE_KINDS,
|
||||
readWholeAgentSessionFailureFact,
|
||||
type AgentSessionFailureFact
|
||||
} from '../../../../shared/agent-session-failure'
|
||||
import {
|
||||
AGENT_SESSION_FAILURE_COPY,
|
||||
sayAgentSessionFailureEnglish,
|
||||
type AgentSessionFailureCopyId
|
||||
} from '../../../../shared/agent-session-failure-copy'
|
||||
import {
|
||||
agentSessionFailureSentence,
|
||||
type AgentSessionFailureWordsContext
|
||||
} from '../../../../shared/agent-session-failure-words'
|
||||
import { AGENT_SESSION_WRITE_NOTICE_COPY } from '../../../../shared/agent-session-write-notice-copy'
|
||||
import { agentSessionFailureSentence } from '../../../../shared/agent-session-failure-words'
|
||||
import { agentSessionWriteNoticeParts } from '../../../../shared/agent-session-refusal-notice'
|
||||
import { agentSessionRefusalFailure } from '../../../../shared/agent-session-write-failure'
|
||||
import { structuredAgentSessionRejectionParts } from '../../../../shared/structured-agent-session-rejection-words'
|
||||
@@ -30,93 +17,11 @@ vi.mock('@/i18n/i18n', async (importOriginal) => {
|
||||
return { ...actual, translate: vi.fn(actual.translate) }
|
||||
})
|
||||
|
||||
const IDS = Object.keys(AGENT_SESSION_FAILURE_COPY).filter(
|
||||
(id): id is AgentSessionFailureCopyId => id in AGENT_SESSION_FAILURE_COPY
|
||||
)
|
||||
// Said by a refusal notice too, so they keep the notice's keys.
|
||||
const NOTICE_PIECES: readonly AgentSessionFailureCopyId[] = [
|
||||
'terminalAgentHoldsChat',
|
||||
'quitTerminalAgent',
|
||||
'startNewChat',
|
||||
'backgroundTasksRunning',
|
||||
'waitForBackgroundTasks',
|
||||
'agentStarting',
|
||||
'waitForStart',
|
||||
'agentStillWorking'
|
||||
]
|
||||
// Kana, and kanji whose simplified Chinese form differs (続 is 续, 読 is 读, ...).
|
||||
const JAPANESE_ONLY = /[\u3040-\u30ff続読変済図気帰戻検択転権単圧応対発処実証覧関専]/u
|
||||
const VALUES = {
|
||||
agent: 'Claude',
|
||||
loginCommand: 'agent login',
|
||||
slashCommand: '/login',
|
||||
command: 'compact',
|
||||
detail: 'Image type .bmp',
|
||||
limit: '20',
|
||||
size: '5',
|
||||
option: '--remote'
|
||||
}
|
||||
|
||||
function factsFor(kind: AgentSessionFailureFact['kind']): AgentSessionFailureFact[] {
|
||||
const facts: AgentSessionFailureFact[] = [
|
||||
{ kind },
|
||||
{ kind, detail: { text: 'Context window exceeded.', audience: 'person' } },
|
||||
{ kind, detail: { text: 'exit 1', audience: 'log' } },
|
||||
// Person words with nothing left once trimmed: the sentence says the lead alone.
|
||||
{ kind, detail: { text: ' ... ', audience: 'person' } },
|
||||
{ kind, refusal: { code: 'agent_session_conflict', details: { reason: 'claimConflicted' } } },
|
||||
{ kind, refusal: { code: 'structured_agent_session_unsupported' } },
|
||||
{ kind, retry: { error: 'rate_limit', status: 429 } }
|
||||
]
|
||||
for (const reason of AGENT_SESSION_ATTACHMENT_PROBLEM_REASONS) {
|
||||
facts.push({ kind, attachment: { reason } }, { kind, attachment: { reason, limit: 20 } })
|
||||
}
|
||||
return facts
|
||||
}
|
||||
|
||||
const CONTEXTS: AgentSessionFailureWordsContext[] = [
|
||||
{},
|
||||
{ agentName: 'Codex' },
|
||||
{ agentName: 'Claude', command: 'clear' },
|
||||
{ retryControl: true },
|
||||
{ agentName: 'Claude', command: 'clear', retryControl: true },
|
||||
{ agentName: 'Codex', command: 'compact' },
|
||||
{ command: 'compact', retryControl: true }
|
||||
]
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage('en')
|
||||
})
|
||||
|
||||
describe('desktop words for a failure fact', () => {
|
||||
it.each(['Claude', 'Codex'])(
|
||||
'names %s in terminal refusals on every failure surface',
|
||||
(agentName) => {
|
||||
for (const surface of ['row', 'rejection', 'answer'] as const) {
|
||||
expect(
|
||||
agentSessionFailureSentence(
|
||||
{ kind: 'conversationOpenInTerminal' },
|
||||
surface,
|
||||
{ agentName },
|
||||
sayAgentSessionFailureTranslated
|
||||
)
|
||||
).toBe(
|
||||
`${agentName} is already running this conversation in another terminal. Quit ${agentName} there or close that terminal to continue here.`
|
||||
)
|
||||
}
|
||||
expect(
|
||||
agentSessionFailureSentence(
|
||||
{ kind: 'conversationOpenElsewhere' },
|
||||
'row',
|
||||
{ agentName: 'Codex' },
|
||||
sayAgentSessionFailureTranslated
|
||||
)
|
||||
).toBe(
|
||||
'Codex is still holding this conversation open. Try again in a moment, or close other Codex terminals.'
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
it.each([
|
||||
['en', 'Then send your message again.'],
|
||||
['es', 'Después, envía tu mensaje de nuevo.'],
|
||||
@@ -138,54 +43,6 @@ describe('desktop words for a failure fact', () => {
|
||||
).toBe(`${row}. ${retry}`)
|
||||
})
|
||||
|
||||
it('has a key for every piece, whose English default is the shared sentence', () => {
|
||||
for (const id of IDS) {
|
||||
expect([id, sayAgentSessionFailureTranslated(id, VALUES)]).toEqual([
|
||||
id,
|
||||
sayAgentSessionFailureEnglish(id, VALUES)
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
// The catalog answers first in English, so read the default each key is given directly.
|
||||
it('gives each key the shared sentence as its English default', () => {
|
||||
for (const id of IDS) {
|
||||
vi.mocked(translate).mockClear()
|
||||
sayAgentSessionFailureTranslated(id, VALUES)
|
||||
const section = NOTICE_PIECES.includes(id) ? 'writeNotice' : 'failureWords'
|
||||
expect(vi.mocked(translate).mock.calls.map(([key, fallback]) => [key, fallback])).toEqual([
|
||||
[`components.native-chat.${section}.${id}`, AGENT_SESSION_FAILURE_COPY[id]]
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps the English catalog in step with the shared copy', () => {
|
||||
const own = Object.fromEntries(
|
||||
IDS.filter((id) => !NOTICE_PIECES.includes(id)).map((id) => [
|
||||
id,
|
||||
AGENT_SESSION_FAILURE_COPY[id]
|
||||
])
|
||||
)
|
||||
expect(en.components['native-chat'].failureWords).toEqual(own)
|
||||
for (const id of NOTICE_PIECES) {
|
||||
expect(AGENT_SESSION_WRITE_NOTICE_COPY).toHaveProperty(id, AGENT_SESSION_FAILURE_COPY[id])
|
||||
}
|
||||
})
|
||||
|
||||
it('says every sentence exactly as the host writes it, in English', () => {
|
||||
for (const kind of AGENT_SESSION_FAILURE_KINDS) {
|
||||
for (const fact of factsFor(kind)) {
|
||||
for (const surface of ['row', 'rejection'] as const) {
|
||||
for (const context of CONTEXTS) {
|
||||
expect(
|
||||
agentSessionFailureSentence(fact, surface, context, sayAgentSessionFailureTranslated)
|
||||
).toBe(agentSessionFailureSentence(fact, surface, context))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("words a refused start and a rejected message in the reader's language", async () => {
|
||||
await i18n.changeLanguage('fr')
|
||||
const refused = agentSessionRefusalFailure({
|
||||
@@ -308,20 +165,4 @@ describe('desktop words for a failure fact', () => {
|
||||
)
|
||||
).toBe(sentence)
|
||||
})
|
||||
|
||||
it('shows a host sentence with no fact beside it as written', async () => {
|
||||
await i18n.changeLanguage('fr')
|
||||
expect(
|
||||
agentSessionWriteNoticeText(
|
||||
structuredAgentSessionRejectionParts('Claude does not support .bmp images', 'send')
|
||||
)
|
||||
).toBe('Claude does not support .bmp images')
|
||||
})
|
||||
|
||||
it('keeps Japanese-only characters out of the Chinese words', () => {
|
||||
const chat = zh.components['native-chat']
|
||||
for (const words of Object.values(chat.failureWords)) {
|
||||
expect(words).not.toMatch(JAPANESE_ONLY)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { PetImportError } from '../../../../shared/pet-import-failure-copy'
|
||||
import { customPetLabel, petImportFailureText } from './pet-import-failure-text'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('pet import failure text', () => {
|
||||
it('localizes a coded failure while main keeps the English error', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const { failure } = new PetImportError('fileTooLarge', { size: '12.5', max: 10 })
|
||||
expect(failure.error).toBe('File is too large (12.5 MB). Max is 10 MB.')
|
||||
expect(petImportFailureText(failure)).toBe(
|
||||
es.pet.importErrors.fileTooLarge.replace('{{size}}', '12.5').replace('{{max}}', '10')
|
||||
)
|
||||
const parse = new PetImportError('manifestParseFailed', {}, 'Unexpected token }').failure
|
||||
expect(petImportFailureText(parse)).toContain('Unexpected token }')
|
||||
})
|
||||
|
||||
it('shows main English for unknown codes or missing params', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(petImportFailureText({ ok: false, error: 'Newer', errorCode: 'future' })).toBe('Newer')
|
||||
expect(petImportFailureText({ ok: false, error: 'English', errorCode: 'fileTooLarge' })).toBe(
|
||||
'English'
|
||||
)
|
||||
})
|
||||
|
||||
it('translates only the stored default names', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(customPetLabel({ label: 'Custom pet', kind: 'image' })).toBe(es.pet.defaultLabels.image)
|
||||
expect(customPetLabel({ label: 'Custom pet' })).toBe(es.pet.defaultLabels.image)
|
||||
expect(customPetLabel({ label: 'Pet bundle', kind: 'bundle' })).toBe(
|
||||
es.pet.defaultLabels.bundle
|
||||
)
|
||||
expect(customPetLabel({ label: 'Mochi', kind: 'bundle' })).toBe('Mochi')
|
||||
})
|
||||
})
|
||||
+1
-31
@@ -1,12 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
installedPluginPresentation,
|
||||
isPluginEnabled,
|
||||
pluginNeedsReview,
|
||||
pluginStatusKind,
|
||||
pluginStatusWord,
|
||||
sortByAttention
|
||||
} from './plugin-status-presentation'
|
||||
import { installedPluginPresentation, sortByAttention } from './plugin-status-presentation'
|
||||
import type { PluginHostListEntry } from '../../../../../preload/api-types'
|
||||
|
||||
function host(pluginKey: string, extra: Partial<PluginHostListEntry> = {}): PluginHostListEntry {
|
||||
@@ -31,25 +24,8 @@ function host(pluginKey: string, extra: Partial<PluginHostListEntry> = {}): Plug
|
||||
}
|
||||
|
||||
const idle = { updatedTo: null, needsReview: false }
|
||||
const base = { status: 'running' as const, needsReconsent: false, blockedByKillList: undefined }
|
||||
|
||||
describe('plugin status presentation', () => {
|
||||
it('ranks the safety list, then review, over the worker state', () => {
|
||||
expect(pluginStatusKind({ ...base, blockedByKillList: { reason: 'unsafe' } })).toBe('blocked')
|
||||
expect(pluginStatusKind({ ...base, needsReconsent: true })).toBe('review')
|
||||
expect(pluginStatusKind({ ...base, status: 'pending' })).toBe('review')
|
||||
expect(pluginStatusKind({ ...base, status: 'errored' })).toBe('error')
|
||||
expect(pluginStatusKind({ ...base, status: 'idle' })).toBe('enabled')
|
||||
expect(pluginStatusWord(pluginStatusKind(base))).toBe('Running')
|
||||
})
|
||||
|
||||
it('names a crashed plugin Stopped everywhere, never Error', () => {
|
||||
expect(pluginStatusWord('error')).toBe('Stopped')
|
||||
expect(
|
||||
installedPluginPresentation(host('a', { status: 'errored' }), null, false, idle)
|
||||
).toMatchObject({ primary: 'restart', statusText: 'Stopped' })
|
||||
})
|
||||
|
||||
it('offers Reinstall for a plugin that cannot load, and View error without one', () => {
|
||||
const broken = host('a', { status: 'invalid' })
|
||||
expect(installedPluginPresentation(broken, null, false, idle, '1.0.0').primary).toBe(
|
||||
@@ -58,12 +34,6 @@ describe('plugin status presentation', () => {
|
||||
expect(installedPluginPresentation(broken, null, false, idle).primary).toBe('error')
|
||||
})
|
||||
|
||||
it('counts a crashed plugin as switched on, and a pending one as needing review', () => {
|
||||
expect(isPluginEnabled({ status: 'errored' })).toBe(true)
|
||||
expect(isPluginEnabled({ status: 'disabled' })).toBe(false)
|
||||
expect(pluginNeedsReview({ status: 'pending', needsReconsent: false })).toBe(true)
|
||||
})
|
||||
|
||||
it('puts plugins that need the user first, then updates, keeping order within each', () => {
|
||||
const rows = [
|
||||
{ installed: host('a'), update: false },
|
||||
|
||||
@@ -37,36 +37,6 @@ describe('aiVaultScanIssueDisplayText', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('localizes the notice for WSL rows carried over from the last confirmed scan', async () => {
|
||||
await useSpanish()
|
||||
const carried = aiVaultScanIssueText('wslSessionsFromLastScan')
|
||||
expect(carried.message).toBe('WSL could not be checked; its sessions are from the last scan.')
|
||||
expect(aiVaultScanIssueDisplayText(carried)).toBe(es.aiVault.scanIssues.wslSessionsFromLastScan)
|
||||
})
|
||||
|
||||
it('shows the English message for uncoded, unknown, or incomplete issues', async () => {
|
||||
await useSpanish()
|
||||
expect(aiVaultScanIssueDisplayText({ message: 'Old host text' })).toBe('Old host text')
|
||||
expect(
|
||||
aiVaultScanIssueDisplayText({
|
||||
message: 'Newer',
|
||||
messageCode: 'futureCode'
|
||||
})
|
||||
).toBe('Newer')
|
||||
expect(
|
||||
aiVaultScanIssueDisplayText({
|
||||
message: 'English',
|
||||
messageCode: 'toString'
|
||||
})
|
||||
).toBe('English')
|
||||
expect(
|
||||
aiVaultScanIssueDisplayText({
|
||||
message: 'English',
|
||||
messageCode: 'wslDistroNotRunning'
|
||||
})
|
||||
).toBe('English')
|
||||
})
|
||||
|
||||
it('localizes classified scanner faults and passes other errors through', async () => {
|
||||
await useSpanish()
|
||||
expect(aiVaultScanErrorText('AI Vault service timed out after 130000ms.')).toBe(
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { localizedCreateFailure } from './hosted-review-create-failure-copy'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('localizedCreateFailure for missing create fields', () => {
|
||||
it('localizes the reason and keeps the English error for an older host', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const english = 'Create PR failed: base branch, head branch, and title are required.'
|
||||
expect(
|
||||
localizedCreateFailure(
|
||||
{ ok: false, code: 'validation', error: english, reason: 'base_head_title_required' },
|
||||
'bitbucket'
|
||||
)
|
||||
).toContain(es.sourceControl.createReviewFailure.baseHeadTitleRequired)
|
||||
expect(localizedCreateFailure({ ok: false, code: 'validation', error: english }, 'gitea')).toBe(
|
||||
english
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('localizedCreateFailure for stacked pull requests', () => {
|
||||
it('localizes a stacked reason and falls back to English without its branch', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const english = 'Orca found multiple open pull requests for the current branch feature/x.'
|
||||
expect(
|
||||
localizedCreateFailure(
|
||||
{
|
||||
ok: false,
|
||||
code: 'validation',
|
||||
error: english,
|
||||
reason: 'stack_head_ambiguous',
|
||||
head: 'feature/x'
|
||||
},
|
||||
'github'
|
||||
)
|
||||
).toBe(
|
||||
es.sourceControl.createReviewFailure.stacked.stackHeadAmbiguous.replace(
|
||||
'{{head}}',
|
||||
'feature/x'
|
||||
)
|
||||
)
|
||||
expect(
|
||||
localizedCreateFailure(
|
||||
{ ok: false, code: 'validation', error: english, reason: 'stack_head_ambiguous' },
|
||||
'github'
|
||||
)
|
||||
).toBe(english)
|
||||
})
|
||||
})
|
||||
@@ -1,83 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import {
|
||||
wslHostAddRefusalText,
|
||||
wslHostServerStopReason,
|
||||
wslHostStatusDetailText
|
||||
} from '../../../../shared/wsl-host-failure-copy'
|
||||
import {
|
||||
wslHostAddRefusalMessage,
|
||||
wslHostStatusDetailMessage,
|
||||
wslHostStopReasonMessage
|
||||
} from './wsl-host-failure-text'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
async function useSpanish(): Promise<void> {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
}
|
||||
|
||||
describe('WSL host failure text', () => {
|
||||
it('keeps the English wire text and translates it by code, commands verbatim', async () => {
|
||||
await useSpanish()
|
||||
const wsl1 = wslHostAddRefusalText('wsl1', { distro: 'Ubuntu' })
|
||||
expect(wsl1.text).toBe(
|
||||
'Ubuntu runs WSL 1, which Orca no longer supports. Convert it to WSL 2 in a Windows terminal with: wsl --set-version Ubuntu 2'
|
||||
)
|
||||
const translated = wslHostAddRefusalMessage({
|
||||
message: wsl1.text,
|
||||
errorCode: wsl1.code,
|
||||
errorParams: wsl1.params
|
||||
})
|
||||
expect(translated).toBe(
|
||||
es.sidebar.wslHosts.addErrors.wsl1
|
||||
.replace('{{distro}}', 'Ubuntu')
|
||||
.replace('{{command}}', 'wsl --set-version Ubuntu 2')
|
||||
)
|
||||
expect(translated).not.toBe(wsl1.text)
|
||||
})
|
||||
|
||||
it('translates status details, including plural restarts and verbatim stop errors', async () => {
|
||||
await useSpanish()
|
||||
const gaveUp = wslHostStatusDetailText('restartGaveUp', { distro: 'Ubuntu', count: 5 })
|
||||
expect(gaveUp.text).toBe('Could not restart the Orca server in Ubuntu after 5 attempts')
|
||||
expect(
|
||||
wslHostStatusDetailMessage({
|
||||
detail: gaveUp.text,
|
||||
detailCode: gaveUp.code,
|
||||
detailParams: gaveUp.params
|
||||
})
|
||||
).toBe(
|
||||
es.sidebar.wslHosts.statusDetails.restartGaveUp_other
|
||||
.replace('{{distro}}', 'Ubuntu')
|
||||
.replace('{{count}}', '5')
|
||||
)
|
||||
const unrecorded = wslHostServerStopReason('removalUnrecorded', { detail: 'EACCES' })
|
||||
expect(wslHostStopReasonMessage(unrecorded)).toBe(
|
||||
es.sidebar.wslHosts.statusDetails.removalUnrecorded.replace('{{detail}}', 'EACCES')
|
||||
)
|
||||
})
|
||||
|
||||
it('shows the English text for uncoded, unknown, or incomplete codes', async () => {
|
||||
await useSpanish()
|
||||
expect(wslHostAddRefusalMessage({ message: 'bridge refused' })).toBe('bridge refused')
|
||||
expect(wslHostAddRefusalMessage({ message: 'Newer', errorCode: 'futureCode' })).toBe('Newer')
|
||||
expect(wslHostAddRefusalMessage({ message: 'English', errorCode: 'alreadyAdded' })).toBe(
|
||||
'English'
|
||||
)
|
||||
expect(wslHostAddRefusalMessage({ message: 'English', errorCode: 'toString' })).toBe('English')
|
||||
expect(
|
||||
wslHostStatusDetailMessage({
|
||||
detail: 'English',
|
||||
detailCode: 'restartGaveUp',
|
||||
detailParams: { distro: 'Ubuntu', count: '5' }
|
||||
})
|
||||
).toBe('English')
|
||||
})
|
||||
})
|
||||
@@ -1,34 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { bitbucketConnectFailure } from '../../../shared/bitbucket-credentials'
|
||||
import { bitbucketConnectErrorText } from './bitbucket-connect-error-copy'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('bitbucket connect error copy', () => {
|
||||
it('translates a coded failure while its wire error stays English', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const failure = bitbucketConnectFailure('rejected')
|
||||
expect(failure.error).toBe(
|
||||
'Bitbucket rejected these credentials. Check the email and token, then try again.'
|
||||
)
|
||||
expect(bitbucketConnectErrorText(failure)).toBe(es.codeHost.bitbucketConnect.rejected)
|
||||
})
|
||||
|
||||
it('shows the English error for uncoded or unknown failures', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(bitbucketConnectErrorText({ error: 'Enter an access token.' })).toBe(
|
||||
'Enter an access token.'
|
||||
)
|
||||
expect(bitbucketConnectErrorText({ error: 'New text', errorCode: 'fromNewerHost' })).toBe(
|
||||
'New text'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -11,23 +11,6 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
describe('browserCookieImportFailureText', () => {
|
||||
it('localizes coded failures while the wire reason stays the legacy English', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const copyFailed = browserCookieImportFailure('databaseCopyFailed', { browser: 'Arc' })
|
||||
expect(copyFailed.reason).toBe('Could not copy Arc cookies database. Try closing Arc first.')
|
||||
expect(browserCookieImportFailureText(copyFailed)).toBe(
|
||||
es.browser.cookieImport.errors.databaseCopyFailed.replaceAll('{{browser}}', 'Arc')
|
||||
)
|
||||
const skipped = browserCookieImportFailure('fileNoValidCookies', { count: 1 })
|
||||
expect(skipped.reason).toBe(
|
||||
'No valid cookies found. 1 entries were skipped due to missing or invalid fields.'
|
||||
)
|
||||
expect(browserCookieImportFailureText(skipped)).toBe(
|
||||
es.browser.cookieImport.errors.fileNoValidCookies_one.replace('{{count}}', '1')
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps diagnostic detail verbatim', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
@@ -41,19 +24,4 @@ describe('browserCookieImportFailureText', () => {
|
||||
)
|
||||
expect(browserCookieImportFailureText(failed)).toContain('database is locked {{browser}}')
|
||||
})
|
||||
|
||||
it('shows the host English for uncoded, unknown, or incomplete failures', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(browserCookieImportFailureText({ reason: 'Old host text' })).toBe('Old host text')
|
||||
expect(browserCookieImportFailureText({ reason: 'Newer', reasonCode: 'futureCode' })).toBe(
|
||||
'Newer'
|
||||
)
|
||||
expect(browserCookieImportFailureText({ reason: 'English', reasonCode: 'noCookies' })).toBe(
|
||||
'English'
|
||||
)
|
||||
expect(browserCookieImportFailureText({ reason: 'English', reasonCode: 'toString' })).toBe(
|
||||
'English'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { remoteServerPairingFailureMessage } from './remote-server-pairing-error-copy'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('remote server pairing error copy', () => {
|
||||
it('translates a coded failure and keeps the server name verbatim', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const text = remoteServerPairingFailureMessage({
|
||||
message: 'A server named "Studio" already exists.',
|
||||
errorCode: 'nameTaken',
|
||||
errorParams: { name: 'Studio' }
|
||||
})
|
||||
expect(text).toBe(es.runtime.remoteServerPairingErrors.nameTaken.replace('{{name}}', 'Studio'))
|
||||
expect(text).not.toBe('A server named "Studio" already exists.')
|
||||
})
|
||||
|
||||
it('shows the English message for uncoded, unknown, or incomplete failures', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const message = 'A server named "Studio" already exists.'
|
||||
expect(remoteServerPairingFailureMessage({ message })).toBe(message)
|
||||
expect(remoteServerPairingFailureMessage({ message, errorCode: 'fromNewerHost' })).toBe(message)
|
||||
expect(remoteServerPairingFailureMessage({ message, errorCode: 'nameTaken' })).toBe(message)
|
||||
})
|
||||
})
|
||||
@@ -1,39 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { updaterCodedMessage } from '../../../shared/updater-failure-copy'
|
||||
import { updaterMessageText } from './updater-message-copy'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('updater message copy', () => {
|
||||
it('translates coded updater messages and keeps channel names verbatim', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(updaterMessageText(updaterCodedMessage('updateServerUnreachable'))).toBe(
|
||||
es.updates.failure.updateServerUnreachable
|
||||
)
|
||||
expect(
|
||||
updaterMessageText(updaterCodedMessage('unsignedDevChannel', { channel: 'Hourly' }))
|
||||
).toBe(es.updates.failure.unsignedDevChannel.replace('{{channel}}', 'Hourly'))
|
||||
})
|
||||
|
||||
it('keeps the English message for older hosts, unknown codes and missing params', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
expect(updaterMessageText({ message: 'Older host text' })).toBe('Older host text')
|
||||
expect(updaterMessageText({ message: 'Newer host text', messageCode: 'futureCode' })).toBe(
|
||||
'Newer host text'
|
||||
)
|
||||
expect(
|
||||
updaterMessageText({
|
||||
message: 'Hourly builds are unsigned…',
|
||||
messageCode: 'unsignedDevChannel'
|
||||
})
|
||||
).toBe('Hourly builds are unsigned…')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user