diff --git a/frontend/src/views/apps/RadioApp.contract.test.ts b/frontend/src/views/apps/RadioApp.contract.test.ts new file mode 100644 index 0000000..7cba1fa --- /dev/null +++ b/frontend/src/views/apps/RadioApp.contract.test.ts @@ -0,0 +1,91 @@ +import { readFileSync } from 'node:fs' + +import { describe, expect, it } from 'vitest' + +const source = readFileSync(new URL('./RadioApp.vue', import.meta.url), 'utf8') +const navigationSource = source.slice( + source.indexOf('') + ''.length, +) + +describe('RadioApp Sky UI contract', () => { + it('uses only the first-party Sky UI surface', () => { + expect(source).not.toContain("from 'konsta/vue'") + expect(source).not.toMatch(/<\/?k-[a-z]/) + expect(source).toContain(' { + expect(navigationSource).toContain('layout="full"') + expect(navigationSource).toContain('', + ) + expect(source.indexOf('')).toBeLessThan( + source.indexOf('') + expect(source).not.toContain('radio-tab-switcher') + expect(source).toMatch( + /\.radio-navigation__item > span:last-child\s*\{[^}]*text-overflow:\s*ellipsis[^}]*white-space:\s*nowrap/s, + ) + }) + + it('keeps the Radio actions, hydration and trusted updates wired', () => { + expect(source).toContain('await radio.load()') + expect(source).toContain('await radio.connect(primary, secondary)') + expect(source).toContain('await radio.disconnect()') + expect(source).toContain('void radio.setVolume(volumeInput.value)') + expect(source).toContain("radio.saveSetting('autoRejoin', $event)") + expect(source).toContain("radio.saveSetting('notifications', $event)") + expect(source).toContain('await radio.saveDisplayName(displayName)') + expect(source).toContain('await radio.saveBadge(badge)') + expect(source).toContain('isTrustedRootMessageSource(event.source, window)') + expect(source).toContain("event.data?.type === 'radio:updated'") + expect(source).toContain('radio.updateMembers(event.data.data.members)') + expect(source).toContain("window.addEventListener('message', onMessage)") + expect(source).toContain("window.removeEventListener('message', onMessage)") + expect(source).toContain("value.replace(',', '.')") + expect(source).toContain('connectHistory(entry)') + }) + + it('preserves theme, labels, loading, empty, error and feedback states', () => { + expect(source).toContain(':dark="phone.isDarkMode"') + expect(source).toContain(':label="phone.t(\'Apps.radio.name\')"') + expect(source).toContain('radio.isLoading && !radio.data.provider') + expect(source).toContain('