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

157 lines
6.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { isPluginThemeRegistration, parsePluginAppThemeArtifact } from './plugin-theme-artifact'
import {
isPluginTerminalThemeRegistration,
parsePluginTerminalThemeArtifact
} from './plugin-terminal-theme-artifact'
const parseApp = (value: unknown) => () =>
parsePluginAppThemeArtifact(JSON.stringify(value), 'strict')
describe('plugin app theme artifacts', () => {
it.each([
['a private token', { base: 'dark', tokens: { '--internal-gap': '#000' } }],
['a URL', { base: 'dark', tokens: { '--background': 'url(x)' } }],
['a var() chain', { base: 'dark', tokens: { '--background': 'var(--a)' } }],
['declaration syntax', { base: 'dark', tokens: { '--background': '#000; color: red' } }],
['no tokens', { base: 'dark', tokens: {} }],
[
'transparent',
{ base: 'dark', tokens: { '--background': 'transparent', '--foreground': '#fff' } }
],
['an unknown key', { base: 'dark', tokens: { '--border': '#000' }, extra: 1 }]
])('rejects %s', (_label, value) => {
expect(parseApp(value)).toThrow()
})
it('skips unknown tokens and keys at runtime so newer themes still load', () => {
const theme = parsePluginAppThemeArtifact(
JSON.stringify({
schemaVersion: 1,
base: 'dark',
tokens: { '--background': '#000', '--foreground': '#fff', '--future': '#123' },
future: true
})
)
expect(theme.tokens).not.toHaveProperty('--future')
expect(parseApp({ schemaVersion: 2, base: 'dark', tokens: {} })).toThrow()
})
it('reads a background image, strict for authors and never fatal at runtime', () => {
const base = { base: 'dark', tokens: { '--background': '#000', '--foreground': '#fff' } }
const background = { path: 'images/dusk.webp', opacity: 0.35, size: 'tile' }
const runtime = (value: unknown) =>
parsePluginAppThemeArtifact(JSON.stringify({ ...base, background: value })).background
expect(parseApp({ ...base, background })().background).toEqual(background)
expect(parseApp({ ...base, background: { path: 'a.png', blur: 4 } })).toThrow()
expect(runtime({ path: 'a.png', blur: 4 })).toEqual({ path: 'a.png' })
// A newer or out-of-range style value falls back to its default; only a bad path drops the image.
expect(parseApp({ ...base, background: { path: 'a.png', size: 'stretch' } })).toThrow()
expect(runtime({ path: 'a.png', size: 'stretch', opacity: 2 })).toMatchObject({ path: 'a.png' })
expect(parseApp({ ...base, background: { path: '../escape.png' } })).toThrow()
expect(runtime({ path: '../escape.png' })).toBeUndefined()
})
it('reads VS Code-shaped tokenColors, strict for authors and tolerant at runtime', () => {
const base = { base: 'dark', tokens: { '--background': '#000', '--foreground': '#fff' } }
const keyword = { name: 'Keywords', scope: ['keyword'], settings: { foreground: '#ff79c6' } }
expect(parseApp({ ...base, tokenColors: [keyword] })().tokenColors).toEqual([keyword])
expect(
parseApp({ ...base, tokenColors: [{ scope: 'x', settings: { foreground: 'red' } }] })
).toThrow()
expect(
parseApp({ ...base, tokenColors: [{ scope: 'x', settings: { fontStyle: 'wavy' } }] })
).toThrow()
expect(
parsePluginAppThemeArtifact(
JSON.stringify({
...base,
tokenColors: [
{ ...keyword, settings: { ...keyword.settings, background: '#000' } },
{ scope: 'x', settings: { foreground: 'red' } },
'junk'
]
})
).tokenColors
).toEqual([{ scope: ['keyword'], settings: { foreground: '#ff79c6' } }])
})
it('validates registrations crossing IPC', () => {
const registration = {
id: 'plugin:samples.midnight/midnight',
revision: 'r1',
pluginKey: 'samples.midnight',
pluginName: 'Midnight',
label: 'Midnight',
base: 'dark',
tokens: { '--background': '#000000', '--foreground': '#ffffff' }
}
expect(isPluginThemeRegistration(registration)).toBe(true)
expect(isPluginThemeRegistration({ ...registration, id: 'midnight' })).toBe(false)
expect(isPluginThemeRegistration({ ...registration, tokens: { '--x': '#000' } })).toBe(false)
})
})
describe('plugin terminal theme artifacts', () => {
const terminal = { background: '#000000', foreground: '#ffffff', green: '#00ff00' }
it('skips unknown slots at runtime', () => {
expect(
parsePluginTerminalThemeArtifact(
JSON.stringify({ schemaVersion: 1, terminal: { ...terminal, glow: '#000000' } })
)
).toEqual({ mode: 'unknown', terminal })
})
it.each([
['an unknown slot', { ...terminal, glow: '#000000' }],
['an invalid color', { ...terminal, red: 'red; x' }],
['no ANSI color', { background: '#000000', foreground: '#ffffff' }]
])('rejects %s', (_label, colors) => {
expect(() =>
parsePluginTerminalThemeArtifact(
JSON.stringify({ schemaVersion: 1, terminal: colors }),
'strict'
)
).toThrow()
})
it('validates registrations crossing IPC', () => {
const registration = {
id: 'plugin:samples.midnight/midnight',
pluginKey: 'samples.midnight',
pluginName: 'Midnight',
label: 'Midnight',
mode: 'dark',
terminal
}
expect(isPluginTerminalThemeRegistration(registration)).toBe(true)
expect(isPluginTerminalThemeRegistration({ ...registration, terminal: {} })).toBe(false)
})
})
describe('plugin app theme completion', () => {
it('requires a background and foreground', () => {
expect(parseApp({ base: 'dark', tokens: { '--background': '#000' } })).toThrow('--foreground')
})
it.each(['transparent', 'inherit', 'currentColor', '100px', 'calc(1px + 2px)', 'env(x)'])(
'rejects the non-color value %s',
(value) => {
expect(
parseApp({ base: 'dark', tokens: { '--background': value, '--foreground': '#fff' } })
).toThrow()
}
)
it('keeps a sidebar the theme sets', () => {
const { tokens } = parsePluginAppThemeArtifact(
JSON.stringify({
base: 'dark',
tokens: { '--background': '#1d1533', '--foreground': '#e8e0ff', '--sidebar': '#271d45' }
})
)
expect(tokens['--worktree-sidebar']).toBe('#271d45')
})
})