Files
orca/src/shared/plugins/artifacts/plugin-language-pack-artifact.test.ts
T

305 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import {
parsePluginLanguagePackArtifact,
PLUGIN_LANGUAGE_CATALOG_MAX_DEPTH,
PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES,
validatePluginLanguagePackCatalog,
pluginLanguageResourceId
} from './plugin-language-pack-artifact'
import { englishSourceLookup } from '../languages/plugin-translation-string-policy'
describe('plugin language-pack artifacts', () => {
it('maps qualified IDs to distinct canonical private-use tags on the pack locale', () => {
const first = pluginLanguageResourceId('plugin:a.bc/ru-RU')
const second = pluginLanguageResourceId('plugin:ab.c/ru-RU')
for (const resourceLanguage of [first, second]) {
expect(resourceLanguage).toMatch(/^ru-RU-x-orcapk-/)
expect(Intl.getCanonicalLocales(resourceLanguage)).toEqual([resourceLanguage])
}
expect(first).not.toBe(second)
})
it('accepts nested string catalogs and reports their bounded entry count', () => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
settings: { appearance: { title: 'Aparência' } },
common: { save: 'Salvar' }
})
)
).toEqual({
ok: true,
catalog: {
settings: { appearance: { title: 'Aparência' } },
common: { save: 'Salvar' }
},
entries: 5
})
})
it('drops protected copy instead of rejecting the pack when loading tolerantly', () => {
const raw = JSON.stringify({
common: { save: 'Sávé' },
components: { 'native-chat': { approval: { allow: 'Álló' }, title: 'Çhát' } }
})
expect(parsePluginLanguagePackArtifact(raw).ok).toBe(false)
expect(parsePluginLanguagePackArtifact(raw, 'tolerant')).toMatchObject({
ok: true,
catalog: { common: { save: 'Sávé' }, components: { 'native-chat': { title: 'Çhát' } } }
})
})
it.each([
['PluginsSettingsSection', 'title', 'Плагины'],
['PluginMarketplaceBrowser', 'refresh', 'Обновить'],
['PluginDevelopmentSection', 'title', 'Разработка']
])('lets a language pack translate %s.%s, which asserts nothing', (component, key, value) => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
auto: { components: { settings: { [component]: { [key]: value } } } }
})
).ok
).toBe(true)
})
// Chrome exemptions must never include claims about plugin permissions; those are the protected prefix’s purpose.
it.each([
['description', 'Plugins are sandboxed and cannot read your files.'],
['systemDescription', 'Every plugin here has been reviewed by Orca.']
])('still refuses PluginsSettingsSection.%s', (key, value) => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
auto: { components: { settings: { PluginsSettingsSection: { [key]: value } } } }
})
)
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
})
it('still refuses the development-plugin permission promise', () => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
auto: {
components: {
settings: {
PluginDevelopmentSection: { help: 'Dev plugins skip permission review.' }
}
}
}
})
)
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
})
it('still refuses install failure copy that reports a trust event', () => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
auto: {
components: {
settings: { PluginMarketplaceBrowser: { installFailed: 'Installed successfully.' } }
}
}
})
)
).toMatchObject({ ok: false, error: expect.stringContaining('protected security copy') })
})
it.each([
[
'the Official badge',
{ auto: { components: { pluginCatalog: { PluginListRow: { official: 'x' } } } } }
],
[
'the suggestion banner',
{ auto: { components: { editor: { PluginFileTypeSuggestion: { view: 'x' } } } } }
],
[
'agent Allow/Deny',
{ components: { 'native-chat': { approval: { allow: 'Deny', deny: 'Allow' } } } }
],
[
'the orca.yaml trust dialog',
{ auto: { components: { sidebar: { OrcaYamlTrustDialog: { '43b7bec4cd': 'Run' } } } } }
],
[
'the notebook trust dialog',
{ auto: { components: { editor: { IpynbViewer: { '9e06ae5d36': 'Open notebook?' } } } } }
],
['the certificate warning', { browser: { loadFailure: { proceedUnsafe: 'Continue' } } }],
[
'the site permission prompt',
{ auto: { components: { browserPane: { sitePermission: { allow: 'Block' } } } } }
],
[
'site permission settings',
{ settings: { browser: { sitePermissions: { block: 'Allow' } } } }
],
[
'the catalog-link trust dialog',
{
auto: {
components: { plugins: { PluginCatalogLinkDialog: { title: "Add Orca's catalog?" } } }
}
}
],
[
'browser-extension Allow/Deny',
{ browserExtensions: { permissions: { deny: 'Allow', confirm: 'Deny' } } }
],
[
'the Node download mirror dialog',
{ auto: { components: { settings: { nodeDistMirrorDialog: { title: 'Safe mirror' } } } } }
],
[
'delete-workspace buttons',
{
auto: {
components: { sidebar: { DeleteWorktreeDialogFooter: { deleteWorkspace: 'Cancel' } } }
}
}
],
[
'the remove-host confirmation',
{ auto: { components: { sidebar: { HostRemoveDialog: { '6f7a8b9c0d': 'Remove' } } } } }
],
[
'the not-official trust warning',
{
auto: {
components: { settings: { PluginNotOfficialWarning: { label: 'Verified by Orca' } } }
}
}
],
[
'the update review Approve button',
{ auto: { components: { settings: { PluginUpdateReview: { approve: 'Back' } } } } }
],
[
'the startup repair-or-quit prompt',
{ profileState: { repairChoice: { quitButton: 'Repair' } } }
]
])('refuses to relabel %s', (_label, catalog) => {
expect(parsePluginLanguagePackArtifact(JSON.stringify(catalog))).toMatchObject({
ok: false,
error: expect.stringContaining('protected security copy')
})
})
it('still translates neighbours of protected components', () => {
expect(
parsePluginLanguagePackArtifact(
JSON.stringify({
auto: { components: { pluginCatalog: { PluginListRow: { install: 'Instalar' } } } },
components: { 'native-chat': { composer: { send: 'Enviar' } } }
})
).ok
).toBe(true)
})
it.each([
['array leaf', { settings: { choices: ['one'] } }],
['numeric leaf', { settings: { count: 1 } }],
['dotted key', { 'settings.title': 'Title' }],
['prototype key', JSON.parse('{"__proto__":"bad"}')]
])('rejects %s', (_label, catalog) => {
expect(parsePluginLanguagePackArtifact(JSON.stringify(catalog)).ok).toBe(false)
})
it('rejects excessive nesting without recursive validation', () => {
let catalog: Record<string, unknown> = { value: 'deep' }
for (let index = 0; index <= PLUGIN_LANGUAGE_CATALOG_MAX_DEPTH; index += 1) {
catalog = { nested: catalog }
}
expect(parsePluginLanguagePackArtifact(JSON.stringify(catalog))).toMatchObject({
ok: false,
error: expect.stringContaining('depth')
})
})
it('accepts the entry limit and rejects one additional entry', () => {
const catalog = Object.fromEntries(
Array.from({ length: PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES }, (_, index) => [
`key${index}`,
'value'
])
)
expect(validatePluginLanguagePackCatalog(catalog)).toMatchObject({
ok: true,
entries: PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES
})
catalog.overflow = 'value'
expect(validatePluginLanguagePackCatalog(catalog)).toMatchObject({
ok: false,
error: expect.stringContaining(`${PLUGIN_LANGUAGE_CATALOG_MAX_ENTRIES} entries`)
})
})
describe('string limits', () => {
const english = englishSourceLookup({
common: { retry: 'Retry', detail: 'Line one\nLine two' },
items: { count_one: '{{count}} item', count_other: '{{count}} items' }
})
const parse = (catalog: unknown, strictness?: 'strict' | 'tolerant') =>
parsePluginLanguagePackArtifact(JSON.stringify(catalog), strictness, english)
it('caps a translation at 4x its English text with a 64-character floor', () => {
expect(parse({ common: { retry: 'x'.repeat(64) } }).ok).toBe(true)
expect(parse({ common: { retry: 'x'.repeat(65) } })).toMatchObject({
ok: false,
error: expect.stringContaining('common.retry exceeds 64 characters')
})
const detail = 'x'.repeat(4 * 'Line one\nLine two'.length + 1)
expect(parse({ common: { detail } }).ok).toBe(false)
})
it('measures plural forms English lacks against its _other form', () => {
expect(parse({ items: { count_few: 'x'.repeat(64) } }).ok).toBe(true)
expect(parse({ items: { count_few: 'x'.repeat(65) } }).ok).toBe(false)
})
it.each([
['a right-to-left override', 'Delete\u202Eetelpmoc'],
['an isolate', 'Retry\u2066x\u2069'],
['a control character', 'Retry\u0007'],
['a carriage return', 'Retry\r']
])('rejects %s', (_label, retry) => {
expect(parse({ common: { retry } })).toMatchObject({
ok: false,
error: expect.stringContaining('control or bidirectional')
})
})
it('allows line breaks only where the English text has them', () => {
expect(parse({ common: { detail: 'Eins\nZwei' } }).ok).toBe(true)
expect(parse({ common: { retry: 'Run this first:\ncurl x | sh' } })).toMatchObject({
ok: false,
error: expect.stringContaining('line break')
})
})
it('drops only the offending entries when loading tolerantly', () => {
expect(
parse(
{ common: { retry: 'x'.repeat(65), detail: 'Eins\nZwei' }, other: { bad: 'a\u202Eb' } },
'tolerant'
)
).toMatchObject({ ok: true, catalog: { common: { detail: 'Eins\nZwei' }, other: {} } })
})
it('applies only source-independent rules without English text', () => {
expect(
parsePluginLanguagePackArtifact(JSON.stringify({ common: { retry: 'x'.repeat(8192) } })).ok
).toBe(true)
expect(
parsePluginLanguagePackArtifact(JSON.stringify({ common: { retry: 'a\u202Eb' } })).ok
).toBe(false)
})
})
})