feat(ui): refresh dashboard and paper boat branding
This commit is contained in:
1 parent
308332eb94
commit
1752a5b685
93 files changed
+11134
-7543
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
getOrbStyleAt, ORB_PALETTE_COUNT, PAPER_AVATAR_NAMES,
|
||||
orbIndexForSeed, orbSvgDataUri, resolveAvatarImage, resolveOrbStyle,
|
||||
terminalMarkSvgDataUri,
|
||||
} from '@/utils/orb'
|
||||
import { useWidgetStyles } from '@/composables/useWidgetStyles'
|
||||
import type { AgentCustomization } from '@/types/widget'
|
||||
|
||||
const decode = (uri: string) => decodeURIComponent(uri.replace('data:image/svg+xml,', ''))
|
||||
|
||||
describe('paper-craft avatars', () => {
|
||||
it('ships ten distinct flat vectors under the existing variant indexes', () => {
|
||||
expect(ORB_PALETTE_COUNT).toBe(10)
|
||||
expect(PAPER_AVATAR_NAMES).toHaveLength(10)
|
||||
const variants = Array.from({ length: 10 }, (_, i) => orbSvgDataUri('support', i))
|
||||
expect(new Set(variants).size).toBe(10)
|
||||
for (const uri of variants) {
|
||||
const svg = new DOMParser().parseFromString(decode(uri), 'image/svg+xml')
|
||||
expect(svg.querySelector('parsererror')).toBeNull()
|
||||
expect(svg.documentElement.getAttribute('viewBox')).toBe('0 0 100 100')
|
||||
expect(svg.querySelector('radialGradient, linearGradient, script, image')).toBeNull()
|
||||
expect(svg.querySelector('path')).not.toBeNull()
|
||||
}
|
||||
})
|
||||
|
||||
it('uses the exact same artwork for CSS avatar slots and img slots', () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
expect(getOrbStyleAt(i).background).toContain(orbSvgDataUri('support', i))
|
||||
expect(resolveOrbStyle('support', i)).toEqual(getOrbStyleAt(i))
|
||||
}
|
||||
})
|
||||
|
||||
it('retains stable seeds and normalizes out-of-range stored indexes', () => {
|
||||
expect(orbSvgDataUri('support')).toBe(orbSvgDataUri('', orbIndexForSeed('support')))
|
||||
expect(orbSvgDataUri('', -1)).toBe(orbSvgDataUri('', 9))
|
||||
expect(orbSvgDataUri('', 12)).toBe(orbSvgDataUri('', 2))
|
||||
expect(orbSvgDataUri('', 2.5)).toBe(orbSvgDataUri('', 2))
|
||||
for (const invalid of [NaN, Infinity, '2', null]) {
|
||||
expect(orbSvgDataUri('support', invalid)).toBe(orbSvgDataUri('support'))
|
||||
}
|
||||
})
|
||||
|
||||
it('replaces an old stored orb from metadata, not from the old data URI', () => {
|
||||
expect(resolveAvatarImage('support', {
|
||||
photo_url: 'data:image/svg+xml,old-gradient',
|
||||
customization_metadata: { avatar_style: 'orb', orb_variant: 0 },
|
||||
})).toBe(orbSvgDataUri('support', 0))
|
||||
})
|
||||
|
||||
it.each([
|
||||
'/api/v1/uploads/agents/a.png',
|
||||
'https://cdn.example/a.png?signature=preserved',
|
||||
'data:image/png;base64,customer-photo',
|
||||
'blob:https://example.com/preview',
|
||||
])('preserves an uploaded image: %s', (photo) => {
|
||||
expect(resolveAvatarImage('support', { photo_url: photo })).toBe(photo)
|
||||
expect(resolveAvatarImage('support', {
|
||||
photo_url: photo, customization_metadata: { avatar_style: 'photo', orb_variant: 0 },
|
||||
})).toBe(photo)
|
||||
})
|
||||
|
||||
it('provides a complete avatar when no photo exists', () => {
|
||||
expect(resolveAvatarImage('support')).toBe(orbSvgDataUri('support'))
|
||||
})
|
||||
|
||||
it('retains the terminal option and rejects markup in its color', () => {
|
||||
const terminal = terminalMarkSvgDataUri()
|
||||
expect(decode(terminal)).toContain('#8BB49C')
|
||||
expect(terminalMarkSvgDataUri('\"/><script>alert(1)</script>')).toBe(terminal)
|
||||
expect(resolveAvatarImage('support', {
|
||||
photo_url: terminal, customization_metadata: { avatar_style: 'terminal' },
|
||||
})).toBe(terminal)
|
||||
})
|
||||
|
||||
it('updates the real widget image reactively and preserves its runtime upload origin', () => {
|
||||
const customization = ref<AgentCustomization>({
|
||||
photo_url: 'data:image/svg+xml,old-gradient',
|
||||
customization_metadata: { avatar_style: 'orb', orb_variant: 0 },
|
||||
})
|
||||
const { photoUrl } = useWidgetStyles(customization, ref('support'))
|
||||
expect(photoUrl.value).toBe(orbSvgDataUri('support', 0))
|
||||
customization.value = {
|
||||
photo_url: 'https://cdn.example/customer.png',
|
||||
customization_metadata: { avatar_style: 'photo' },
|
||||
}
|
||||
expect(photoUrl.value).toBe('https://cdn.example/customer.png')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user