import { readFileSync } from 'node:fs'
import { describe, expect, it } from 'vitest'
const source = readFileSync(new URL('./MusicApp.vue', import.meta.url), 'utf8')
const navigationSource = source.slice(
source.indexOf('') + ''.length,
)
describe('MusicApp Sky pill navigation contract', () => {
it('uses the centered surface back action owned by SkyNavbar for playlists', () => {
const navbarTag = source.match(/]*>/is)?.[0]
expect(navbarTag).toBeDefined()
expect(navbarTag).toContain(':show-back="Boolean(activePlaylist)"')
expect(navbarTag).toContain('back-appearance="surface"')
expect(navbarTag).toContain('Apps.music.tabs.playlists')
expect(navbarTag).toContain('@back="closePlaylist"')
expect(source).not.toContain('SkyNavbarBackLink,')
expect(source).not.toContain(' {
const appRuleStart = source.indexOf('.music-app {')
const appRule = source.slice(
appRuleStart,
source.indexOf('}', appRuleStart) + 1,
)
expect(appRule).not.toContain('--sky-safe-area-top:')
expect(appRule).not.toContain('--sky-safe-area-bottom:')
})
it('uses the full-width sliding Glass navigation outside playlists', () => {
expect(source).not.toContain('kTabbar')
expect(source).not.toContain('kTabbarLink')
expect(source).not.toContain('kToolbarPane')
expect(source).not.toContain(' {
expect(source).toContain("{ id: 'library', icon: Library }")
expect(source).toContain("{ id: 'playlists', icon: ListMusic }")
expect(source).toContain("{ id: 'search', icon: Search }")
expect(navigationSource).toContain(':active="activeTab === item.id"')
expect(navigationSource).toContain(
':aria-label="phone.t(`Apps.music.tabs.${item.id}`)"',
)
expect(navigationSource).toContain('@click="selectTab(item.id)"')
expect(navigationSource).toContain(':size="20"')
expect(source).toMatch(
/function selectTab\(tab: MusicTab\): void \{\s+activeTab\.value = tab\s+activePlaylist\.value = null\s+scrollToTop\(\)\s+\}/,
)
})
it('reserves the absolute navigation and mini-player without changing playlist space', () => {
expect(source).toMatch(
/\.music-scroll\s*\{[^}]*padding:\s*8px 0\s*calc\(\s*var\(--sky-safe-area-bottom\) \+ var\(--sky-tabbar-height\) \+\s*var\(--sky-space-3\)\s*\)/s,
)
expect(source).toMatch(
/\.music-app--playing \.music-scroll\s*\{[^}]*padding-bottom:\s*calc\(\s*var\(--music-mini-player-bottom\) \+ var\(--music-mini-player-height\) \+\s*var\(--sky-space-3\)\s*\)/s,
)
expect(source).toContain(
'var(--sky-safe-area-bottom) + var(--sky-tabbar-height) + var(--sky-space-2)',
)
expect(source).toContain('--music-mini-player-height: 58px')
const miniPlayerRuleStart = source.indexOf('.music-mini-player {')
const miniPlayerRule = source.slice(
miniPlayerRuleStart,
source.indexOf('}', miniPlayerRuleStart) + 1,
)
expect(miniPlayerRule).toContain(
'right: calc(var(--sky-safe-area-right) + var(--sky-space-4))',
)
expect(miniPlayerRule).toContain(
'left: calc(var(--sky-safe-area-left) + var(--sky-space-4))',
)
expect(source).toMatch(
/\.music-app--playlist \.music-scroll\s*\{[^}]*padding-bottom:\s*42px/s,
)
expect(source).toMatch(
/\.music-app--playlist\.music-app--playing \.music-scroll\s*\{[^}]*padding-bottom:\s*100px/s,
)
})
it('bridges the Music accent and light-dark theme into Sky UI', () => {
expect(source).toContain('class="music-app sky-ui-provider"')
expect(source).toContain("'sky-ui-provider--dark': phone.isDarkMode")
expect(source).toContain('--sky-app-accent: var(--music-accent)')
expect(source).toContain('class="music-navigation__item"')
expect(source).toMatch(
/\.music-navigation__item > span:last-child\s*\{[^}]*text-overflow:\s*ellipsis[^}]*white-space:\s*nowrap/s,
)
expect(source).not.toContain('.music-tabbar')
})
})