import { readFileSync } from 'node:fs' import { describe, expect, it } from 'vitest' const source = readFileSync(new URL('./App.vue', import.meta.url), 'utf8') const mainCss = readFileSync( new URL('./assets/main.css', import.meta.url), 'utf8', ) describe('browser development preview contract', () => { it('keeps setup light until the appearance choice has been confirmed', () => { expect(source).toContain('const displayedDarkMode = computed(') expect(source).toContain('phone.preferences.settings.setupStep > 4') expect(source).toContain('setupAppearanceSelected.value') expect(source).toContain( '@appearance-selected="setupAppearanceSelected = $event"', ) expect(source).toContain(':dark="displayedDarkMode"') expect(source).toContain('dark: displayedDarkMode') }) it('starts unlocked while preserving an explicit lock screen preview', () => { expect(source).toContain("developmentParameters.has('lockScreenPreview')") expect(source).toContain(': !isDevelopment || developmentLockScreenPreview') expect(source).toContain("developmentParameters.has('setupPreview')") }) it('loads authenticated app data without replacing direct app routes', () => { expect(source).toContain( "if (isLocked.value || setupRequired.value) void router.replace('/')", ) expect(source).toContain('else loadUnlockedPhoneData()') }) it('requires the passcode again after a full device lock', () => { expect(source).toContain('const passcodeRequired = ref(false)') expect(source).toContain( 'if (phone.security.enabled && passcodeRequired.value)', ) expect(source).toContain( 'passcodeRequired.value = isLocked.value && phone.security.enabled', ) expect(source).toMatch( /function lockPhone\(\): void \{[\s\S]*?passcodeRequired\.value = phone\.security\.enabled[\s\S]*?isLocked\.value = true/, ) }) it('keeps hairlines at one rendered device pixel through phone zoom', () => { expect(source).toContain( '...getHairlinePixelStyle(phoneZoom.value, browserDevicePixelRatio.value)', ) expect(source).toContain(':device-pixel-ratio="browserDevicePixelRatio"') }) it('maps the visible device side controls to phone actions', () => { expect(source).toContain('@click="toggleHardwareAlertMute"') expect(source).toContain('@click="changeHardwareAlertVolume(10)"') expect(source).toContain('@click="changeHardwareAlertVolume(-10)"') expect(source).toContain('@click="toggleHardwareLock"') expect(source).toMatch( /function toggleHardwareLock\(\): void \{[\s\S]*?unlockPhone\(\)[\s\S]*?lockPhone\(\)/, ) expect(source).toMatch( /function changeHardwareAlertVolume\(delta: number\): void \{[\s\S]*?phone\.setAlertVolumes\(volume\)/, ) expect(mainCss).toMatch( /\.phone-hardware-button\s*\{[\s\S]*?position:\s*absolute;[\s\S]*?z-index:\s*101;/, ) expect(mainCss).toContain('.phone-hardware-button--power') expect(source).toContain('class="phone-volume-hud"') expect(source).toContain('showHardwareVolumeHud()') expect(mainCss).toContain('.phone-volume-hud__level') }) it('uses layout zoom so the fixed-resolution phone stays sharply rasterized', () => { expect(source).toContain('phone-resolution-canvas--primary') expect(source).toContain("'--phone-rendered-height'") expect(source).toContain("'--phone-rendered-width'") expect(mainCss).toMatch( /\.phone-resolution-canvas\s*\{[^}]*zoom:\s*var\(--phone-zoom, 1\);/s, ) expect(mainCss).not.toMatch( /\.phone-resolution-canvas\s*\{[^}]*transform:\s*scale\(/s, ) }) it('matches the production phone size and bottom-right stage in development', () => { expect(source).toContain('const PHONE_BASE_SCALE = 0.69 * 1.2') expect(source).toContain('() => viewportScale.value * PHONE_BASE_SCALE') expect(source).not.toContain('DEVELOPMENT_PHONE_SCALE') expect(source).not.toContain("'phone-stage--dev': isDevelopment") expect(mainCss).toMatch( /\.phone-stage\s*\{[^}]*place-items:\s*end;[^}]*padding:\s*0 var\(--phone-edge-gap, 24px\) var\(--phone-edge-gap, 24px\) 0;/s, ) expect(mainCss).not.toContain('.phone-stage--dev') }) it('fills the dedicated browser embed without clipping the hardware controls', () => { expect(source).toContain("developmentParameters.has('browserPreview')") expect(source).toContain('import.meta.env.DEV ||') expect(source).toContain("'phone-stage--browser-preview': isBrowserPreview") expect(source).toContain('return (availableScale * 0.94) / PHONE_BASE_SCALE') expect(mainCss).toMatch( /\.phone-stage--browser-preview\s*\{[^}]*place-items:\s*center;[^}]*padding:\s*0;/s, ) expect(mainCss).toMatch( /\.phone-stage--browser-preview \.phone-device\s*\{[^}]*filter:\s*none;/s, ) }) })