scriptable-testlab
Version:
A lightweight, efficient tool designed to manage and update scripts for Scriptable.
178 lines (154 loc) • 5.07 kB
text/typescript
import {AbsLocation} from 'scriptable-abstract';
export interface LocationMockState {
currentLocation: Location.CurrentLocation;
accuracy: 'best' | 'tenMeters' | 'hundredMeters' | 'kilometer' | 'threeKilometers';
simulatedDelay?: number;
}
const ACCURACY_METERS = {
best: 5,
tenMeters: 10,
hundredMeters: 100,
kilometer: 1000,
threeKilometers: 3000,
};
const DEFAULT_STATE: LocationMockState = {
currentLocation: {
latitude: 0,
longitude: 0,
altitude: 0,
horizontalAccuracy: ACCURACY_METERS.best,
verticalAccuracy: ACCURACY_METERS.best,
},
accuracy: 'best',
simulatedDelay: 100,
};
// 添加多语言支持的模拟数据
const MOCK_LOCATIONS = {
'zh-CN': {
name: '模拟位置',
locality: '模拟城市',
subLocality: '模拟区',
thoroughfare: '模拟路',
subThoroughfare: '123号',
administrativeArea: '模拟省',
subAdministrativeArea: '模拟县',
country: '中国',
isoCountryCode: 'CN',
},
'en-US': {
name: 'Mock Location',
locality: 'Mock City',
subLocality: 'Mock District',
thoroughfare: 'Mock Street',
subThoroughfare: '123',
administrativeArea: 'Mock State',
subAdministrativeArea: 'Mock County',
country: 'United States',
isoCountryCode: 'US',
},
'ja-JP': {
name: 'モック位置',
locality: 'モック市',
subLocality: 'モック区',
thoroughfare: 'モック通り',
subThoroughfare: '123',
administrativeArea: 'モック県',
subAdministrativeArea: 'モック郡',
country: '日本',
isoCountryCode: 'JP',
},
};
const createMockGeocodeSummary = (latitude: number, longitude: number, locale = 'en-US'): Location.GeocodeSummary => {
// 获取指定语言的模拟数据,如果不存在则使用英文
const localeData = MOCK_LOCATIONS[locale] || MOCK_LOCATIONS['en-US'];
return {
name: `${localeData.name} (${latitude.toFixed(6)}, ${longitude.toFixed(6)})`,
locality: localeData.locality,
subLocality: localeData.subLocality,
thoroughfare: localeData.thoroughfare,
subThoroughfare: localeData.subThoroughfare,
administrativeArea: localeData.administrativeArea,
subAdministrativeArea: localeData.subAdministrativeArea,
postalCode: '123456',
country: localeData.country,
isoCountryCode: localeData.isoCountryCode,
timeZone: 'UTC',
ocean: null,
inlandWater: null,
areasOfInterest: [`${localeData.name} POI`],
location: {
latitude,
longitude,
altitude: 0,
},
postalAddress: {
street: `${localeData.thoroughfare} ${localeData.subThoroughfare}`,
city: localeData.locality,
state: localeData.administrativeArea,
country: localeData.country,
postalCode: '123456',
isoCountryCode: localeData.isoCountryCode,
subAdministrativeArea: localeData.subAdministrativeArea,
subLocality: localeData.subLocality,
},
};
};
export class MockLocation extends AbsLocation<LocationMockState> {
protected static _instance: MockLocation | undefined;
static get instance(): MockLocation {
return super.instance as MockLocation;
}
protected constructor() {
super(DEFAULT_STATE);
}
private addLocationNoise(value: number, accuracyInMeters: number): number {
const maxOffset = accuracyInMeters / 111000;
return value + (Math.random() - 0.5) * maxOffset;
}
private async simulateDelay(): Promise<void> {
if (this.state.simulatedDelay) {
await new Promise(resolve => setTimeout(resolve, this.state.simulatedDelay));
}
}
async current(): Promise<Location.CurrentLocation> {
await this.simulateDelay();
const accuracyInMeters = ACCURACY_METERS[this.state.accuracy];
const base = this.state.currentLocation;
return {
latitude: this.addLocationNoise(base.latitude, accuracyInMeters),
longitude: this.addLocationNoise(base.longitude, accuracyInMeters),
altitude: this.addLocationNoise(base.altitude, accuracyInMeters),
horizontalAccuracy: accuracyInMeters,
verticalAccuracy: accuracyInMeters,
};
}
setAccuracyToBest(): void {
this.setState({accuracy: 'best'});
}
setAccuracyToTenMeters(): void {
this.setState({accuracy: 'tenMeters'});
}
setAccuracyToHundredMeters(): void {
this.setState({accuracy: 'hundredMeters'});
}
setAccuracyToKilometer(): void {
this.setState({accuracy: 'kilometer'});
}
setAccuracyToThreeKilometers(): void {
this.setState({accuracy: 'threeKilometers'});
}
async reverseGeocode(latitude: number, longitude: number, locale?: string): Promise<Location.GeocodeSummary[]> {
await this.simulateDelay();
return [createMockGeocodeSummary(latitude, longitude, locale)];
}
// Testing helpers
setCurrentLocation(location: Location.CurrentLocation): void {
this.setState({currentLocation: location});
}
setSimulatedDelay(delay: number): void {
this.setState({simulatedDelay: delay});
}
disableSimulatedDelay(): void {
this.setState({simulatedDelay: 0});
}
}