import assert from 'assert'; import {AbsDevice} from 'scriptable-abstract'; import {DeviceMockState, DeviceModel, DeviceScreen} from '../../types'; /** * Type guard for validating screen dimensions. */ function isValidScreenDimension(value: unknown): value is number { return typeof value === 'number' && value > 0; } /** * Type guard for validating percentage values. */ function isValidPercentage(value: unknown): value is number { return typeof value === 'number' && value >= 0 && value <= 1; } /** * Type guard for validating device models. */ function isValidDeviceModel(value: unknown): value is DeviceModel { return typeof value === 'string' && ['iPhone', 'iPad'].includes(value); } /** * Default state for a new device. */ const DEFAULT_STATE: DeviceMockState = { name: 'iPhone Mock', systemName: 'iOS', systemVersion: '16.0', model: 'iPhone', screenSize: { width: 390, height: 844, }, screenResolution: { width: 1170, height: 2532, }, screenScale: 3, screenBrightness: 1, orientation: { isInPortrait: true, isInPortraitUpsideDown: false, isInLandscapeLeft: false, isInLandscapeRight: false, isFaceUp: false, isFaceDown: false, }, battery: { level: 1, isDischarging: false, isCharging: false, isFullyCharged: true, }, locale: { preferredLanguages: ['en'], locale: 'en_US', language: 'en', }, appearance: { isUsingDarkAppearance: false, }, volume: 0.5, }; /** * Mock implementation of Scriptable's Device. * Provides access to device-specific information and functionality. */ export class MockDevice extends AbsDevice { static get instance(): MockDevice { return super.instance as MockDevice; } constructor() { super(DEFAULT_STATE); } // Device information methods name(): string { return this.state.name; } systemName(): string { return this.state.systemName; } systemVersion(): string { return this.state.systemVersion; } model(): string { return this.state.model; } isPhone(): boolean { return this.state.model.toLowerCase().includes('phone'); } isPad(): boolean { return this.state.model.toLowerCase().includes('pad'); } // Screen methods screenSize(): DeviceScreen { return {...this.state.screenSize}; } screenResolution(): DeviceScreen { return {...this.state.screenResolution}; } screenScale(): number { return this.state.screenScale; } screenBrightness(): number { return this.state.screenBrightness; } setScreenBrightness(value: number): void { assert(isValidPercentage(value), 'Brightness must be between 0 and 1'); this.setState({screenBrightness: value}); } // Orientation methods isInPortrait(): boolean { return this.state.orientation.isInPortrait; } isInPortraitUpsideDown(): boolean { return this.state.orientation.isInPortraitUpsideDown; } isInLandscapeLeft(): boolean { return this.state.orientation.isInLandscapeLeft; } isInLandscapeRight(): boolean { return this.state.orientation.isInLandscapeRight; } isFaceUp(): boolean { return this.state.orientation.isFaceUp; } isFaceDown(): boolean { return this.state.orientation.isFaceDown; } // Battery methods batteryLevel(): number { return this.state.battery.level; } isDischarging(): boolean { return this.state.battery.isDischarging; } isCharging(): boolean { return this.state.battery.isCharging; } isFullyCharged(): boolean { return this.state.battery.isFullyCharged; } // Locale methods preferredLanguages(): string[] { return [...this.state.locale.preferredLanguages]; } locale(): string { return this.state.locale.locale; } language(): string { return this.state.locale.language; } // Appearance methods isUsingDarkAppearance(): boolean { return this.state.appearance.isUsingDarkAppearance; } // Volume methods volume(): number { return this.state.volume; } setVolume(value: number): void { assert(isValidPercentage(value), 'Volume must be between 0 and 1'); this.setState({volume: value}); } protected updateState(state: Partial): void { // Screen validations if (state.screenBrightness !== undefined) { assert(isValidPercentage(state.screenBrightness), 'Brightness must be between 0 and 1'); } if (state.screenScale !== undefined) { assert(isValidScreenDimension(state.screenScale), 'Screen scale must be greater than 0'); } if (state.screenResolution) { const {width, height} = state.screenResolution; if (width !== undefined) { assert(isValidScreenDimension(width), 'Screen resolution width must be greater than 0'); } if (height !== undefined) { assert(isValidScreenDimension(height), 'Screen resolution height must be greater than 0'); } } if (state.screenSize) { const {width, height} = state.screenSize; if (width !== undefined) { assert(isValidScreenDimension(width), 'Screen size width must be greater than 0'); } if (height !== undefined) { assert(isValidScreenDimension(height), 'Screen size height must be greater than 0'); } } // Model validation if (state.model !== undefined) { assert(isValidDeviceModel(state.model), 'Model must be either iPhone or iPad'); } // Volume validation if (state.volume !== undefined) { assert(isValidPercentage(state.volume), 'Volume must be between 0 and 1'); } super.updateState(state); } }