193 lines
9.7 KiB
JavaScript
193 lines
9.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import vm from 'node:vm';
|
|
|
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
|
const checks = `
|
|
dashboard = {
|
|
vehicles: [
|
|
{ vin: 'VIN-GD-1', plateNumber: '粤A00001', online: true, dailyMileageKm: 10, locationAvailable: true, longitude: 113.2, latitude: 23.1 },
|
|
{ vin: 'VIN-GD-2', plateNumber: '粤B00002', online: false, dailyMileageKm: 5, locationAvailable: false },
|
|
{ vin: 'VIN-ZJ-1', plateNumber: '浙F00003', online: true, dailyMileageKm: 8, locationAvailable: true, longitude: 120.7, latitude: 30.7 },
|
|
{ vin: 'VIN-X-1', plateNumber: '观光车001', online: false, dailyMileageKm: 0, locationAvailable: false }
|
|
]
|
|
};
|
|
filterState.query = '粤a';
|
|
assert.deepEqual(filteredVehicles().map(vehicle => vehicle.vin), ['VIN-GD-1']);
|
|
filterState.query = '';
|
|
vehicleFilterGroups.provinces = [{ adcode: '440000', vehicles: [dashboard.vehicles[0]] }];
|
|
filterState.province = '440000';
|
|
assert.deepEqual(filteredVehicles().map(vehicle => vehicle.vin), ['VIN-GD-1']);
|
|
filterState.province = '';
|
|
const areaNode = {
|
|
groupByPosition(points) {
|
|
return [
|
|
{ subFeatureIndex: 0, subFeature: { properties: { adcode: '440000', name: '广东省', center: [113.2, 23.1] } }, points: points.filter(point => point.vin === 'VIN-GD-1') },
|
|
{ subFeatureIndex: 1, subFeature: { properties: { adcode: '330000', name: '浙江省', center: [120.2, 30.3] } }, points: points.filter(point => point.vin === 'VIN-ZJ-1') }
|
|
];
|
|
}
|
|
};
|
|
const partition = partitionVehiclesByArea(areaNode, locatedVehicles(), 'province');
|
|
assert.equal(partition.groups.length, 2);
|
|
assert.equal(partition.unmatched.length, 0);
|
|
const provinceNodes = regionNodesFromGroups(partition.groups, 'province');
|
|
assert.equal(provinceNodes.length, 2);
|
|
const guangdong = provinceNodes.find(node => node.nameZh === '广东');
|
|
assert.equal(guangdong.count, 1);
|
|
assert.equal(guangdong.online, 1);
|
|
assert.equal(guangdong.dist, 10);
|
|
|
|
assert.equal(hierarchyLevelForZoom(4.8), 'province');
|
|
assert.equal(hierarchyLevelForZoom(7), 'city');
|
|
assert.equal(hierarchyLevelForZoom(9.5), 'district');
|
|
assert.equal(hierarchyLevelForZoom(12), 'vehicle');
|
|
|
|
vehicleRegionSummary = { level: 'province', nodes: provinceNodes, unassigned: 2, loading: false };
|
|
map = { getZoom: () => 4.8 };
|
|
assert.equal(vehicleNodes().length, 2);
|
|
assert.ok(vehicleNodes().every(node => node.kind === 'vehicleProvince'));
|
|
|
|
const cityPartition = partitionVehiclesByArea({
|
|
groupByPosition(points) {
|
|
return [{ subFeatureIndex: 0, subFeature: { properties: { adcode: '440100', name: '广州市', center: [113.3, 23.1] } }, points }];
|
|
}
|
|
}, [dashboard.vehicles[0]], 'city');
|
|
const cityNodes = regionNodesFromGroups(cityPartition.groups, 'city');
|
|
assert.equal(cityNodes[0].kind, 'vehicleCity');
|
|
assert.equal(cityNodes[0].nameZh, '广州');
|
|
assert.equal(cityNodes[0].nameEn, 'Guangzhou City');
|
|
|
|
assert.equal(administrativeEnglishName('广东省', 'province'), 'Guangdong');
|
|
assert.equal(administrativeEnglishName('广州市', 'city'), 'Guangzhou City');
|
|
assert.equal(administrativeEnglishName('黄埔区', 'district'), 'Huangpu District');
|
|
assert.equal(administrativeEnglishName('两江新区', 'district'), 'Liangjiang New Area');
|
|
assert.equal(stationAdministrativePath({ province: '广东省', city: '广州市', district: '黄埔区' }, 'en'), 'Guangdong · Guangzhou City · Huangpu District');
|
|
assert.deepEqual(modeKpiLabels(i18n.zh, 'station'), { total: '站点总数', active: '合作站点' });
|
|
assert.deepEqual(modeKpiLabels(i18n.en, 'station'), { total: 'Total Stations', active: 'Partner Stations' });
|
|
assert.equal(markerLabel({ kind: 'stationCluster', count: 65 }, i18n.en), '65 stations');
|
|
assert.equal(markerLabel({ kind: 'stationCluster', count: 1 }, i18n.en), '1 station');
|
|
assert.equal(markerLabel({ kind: 'station', cooperative: true }, i18n.en), 'H₂ · Partner');
|
|
|
|
map = {
|
|
getZoom: () => 12,
|
|
getBounds: () => ({
|
|
getSouthWest: () => ({ getLng: () => 100, getLat: () => 20 }),
|
|
getNorthEast: () => ({ getLng: () => 125, getLat: () => 35 })
|
|
})
|
|
};
|
|
const points = vehiclePointNodes();
|
|
assert.equal(points.length, 2);
|
|
assert.ok(points.every(node => node.kind === 'vehiclePoint'));
|
|
assert.equal(points.find(node => node.nameZh === '粤A00001').online, 1);
|
|
|
|
// A selected district is a semantic filter, not a viewport filter. Its full
|
|
// fleet must survive a camera fit even when a valid vehicle sits outside the
|
|
// initial district-centroid viewport.
|
|
currentMode = 'vehicle';
|
|
dashboard = {
|
|
vehicles: [
|
|
{ vin: 'VIN-BY-1', plateNumber: '粤A10001', activeToday: true, locationAvailable: true, longitude: 113.22, latitude: 23.18 },
|
|
{ vin: 'VIN-BY-2', plateNumber: '粤A10002', activeToday: true, locationAvailable: true, longitude: 113.41, latitude: 23.38 }
|
|
]
|
|
};
|
|
vehicleFilterGroups.districts = [{ adcode: '440111', vehicles: dashboard.vehicles }];
|
|
filterState.district = '440111';
|
|
map = {
|
|
getZoom: () => 11.6,
|
|
getBounds: () => ({
|
|
getSouthWest: () => ({ getLng: () => 113.1, getLat: () => 23.1 }),
|
|
getNorthEast: () => ({ getLng: () => 113.3, getLat: () => 23.3 })
|
|
})
|
|
};
|
|
assert.equal(hierarchyLevelForZoom(), 'vehicle');
|
|
assert.equal(vehiclePointNodes().length, 2);
|
|
filterState.district = '';
|
|
|
|
dashboard = {
|
|
stations: [
|
|
{ id: 'GD-1', name: '广州合作站', province: '广东省', city: '广州市', address: '广东省广州市黄埔区开源大道1号', longitude: 113.2, latitude: 23.1, cooperative: true, totalHydrogenKg: 20 },
|
|
{ id: 'GD-2', name: '佛山外部站', province: '广东省', city: '佛山市', longitude: 113.1, latitude: 23.0, cooperative: false, totalHydrogenKg: 10 },
|
|
{ id: 'ZJ-1', name: '嘉兴合作站', province: '浙江省', city: '嘉兴市', longitude: 120.7, latitude: 30.7, cooperative: true, totalHydrogenKg: 30 }
|
|
]
|
|
};
|
|
currentMode = 'station';
|
|
assert.equal(stationDistrictName(dashboard.stations[0]), '黄埔区');
|
|
assert.equal(fuzzySearchScore('广州', 'gz'), 3);
|
|
assert.equal(fuzzySearchScore('广州市', '广州'), 1);
|
|
assert.ok(stationLocationOptions().some(option => option.level === 'district' && option.district === '黄埔区'));
|
|
filterState.query = '合作';
|
|
assert.deepEqual(filteredStations().map(station => station.id).sort(), ['GD-1', 'ZJ-1']);
|
|
filterState.query = '';
|
|
filterState.province = '广东省'; filterState.city = '广州市'; filterState.district = '黄埔区';
|
|
assert.deepEqual(filteredStations().map(station => station.id), ['GD-1']);
|
|
filterState.province = ''; filterState.city = ''; filterState.district = '';
|
|
assert.ok(Math.abs(distanceKm([113, 23], [114, 23]) - 102.4) < 1);
|
|
assert.equal(locationCoordinates({ coords: { longitude: NaN, latitude: 23 } }), null);
|
|
map = {
|
|
getZoom: () => 12,
|
|
getBounds: () => ({
|
|
getSouthWest: () => ({ getLng: () => 112, getLat: () => 22 }),
|
|
getNorthEast: () => ({ getLng: () => 114, getLat: () => 24 })
|
|
})
|
|
};
|
|
assert.equal(stationNodes(false).length, 3);
|
|
assert.equal(stationNodes(true).length, 2);
|
|
assert.deepEqual(stationNodes(true).map(node => node.name).sort(), ['佛山外部站', '广州合作站']);
|
|
const stationNavigationUrl = navigationUrlForEntity('station', dashboard.stations[0]);
|
|
assert.ok(stationNavigationUrl.startsWith('https://uri.amap.com/navigation?to=113.200000,23.100000,'));
|
|
assert.match(stationNavigationUrl, /%E5%B9%BF%E5%B7%9E/);
|
|
assert.match(stationNavigationUrl, /coordinate=gaode&callnative=1$/);
|
|
assert.equal(navigationUrlForEntity('vehicle', { plateNumber: '无位置车辆' }), null);
|
|
const launchClassSet = new Set();
|
|
const launchOverlay = { hidden: true, classList: { add(name) { launchClassSet.add(name); }, remove(name) { launchClassSet.delete(name); } } };
|
|
const launchButton = { disabled: false, dataset: { navigationUrl: stationNavigationUrl }, classList: { add(name) { launchClassSet.add('button:' + name); }, remove(name) { launchClassSet.delete('button:' + name); }, contains(name) { return launchClassSet.has('button:' + name); } } };
|
|
const launchTimers = [];
|
|
document.getElementById = id => id === 'navigationLaunchOverlay' ? launchOverlay : id === 'detailNavigateBtn' ? launchButton : null;
|
|
window.requestAnimationFrame = callback => callback();
|
|
window.setTimeout = (callback, delay) => { launchTimers.push({ callback, delay }); return launchTimers.length; };
|
|
window.clearTimeout = () => {};
|
|
window.location = { assign() {} };
|
|
selectedEntity = { mode: 'station', entity: dashboard.stations[0] };
|
|
navigateToSelectedEntity();
|
|
assert.equal(launchOverlay.hidden, false);
|
|
assert.ok(launchClassSet.has('is-visible'));
|
|
assert.ok(launchClassSet.has('button:is-launching'));
|
|
assert.equal(launchButton.disabled, true);
|
|
assert.deepEqual(launchTimers.map(timer => timer.delay), [140, 4000]);
|
|
const stationView = stationViewportSummary();
|
|
assert.equal(stationView.level, 'station');
|
|
assert.equal(stationView.visibleNodes.length, 2);
|
|
assert.equal(stationView.allNodes.length, 3);
|
|
|
|
map = {
|
|
getZoom: () => 4.8,
|
|
getBounds: () => ({
|
|
getSouthWest: () => ({ getLng: () => 112, getLat: () => 22 }),
|
|
getNorthEast: () => ({ getLng: () => 114, getLat: () => 24 })
|
|
})
|
|
};
|
|
const provinceView = stationViewportSummary();
|
|
assert.equal(provinceView.allNodes.length, 2);
|
|
assert.equal(provinceView.visibleNodes.length, 1);
|
|
assert.equal(provinceView.visibleNodes[0].name, '广东');
|
|
assert.equal(provinceView.visibleNodes[0].count, 2);
|
|
`;
|
|
|
|
const sandbox = {
|
|
assert,
|
|
console,
|
|
pinyinPro: {
|
|
pinyin(value) {
|
|
return ({ 广州: 'guang zhou', 黄埔: 'huang pu', 两江: 'liang jiang' })[value] || value;
|
|
}
|
|
},
|
|
document: { addEventListener() {}, querySelector() {}, createElement() { return {}; }, head: { appendChild() {} } },
|
|
window: {},
|
|
setInterval() {},
|
|
clearInterval() {}
|
|
};
|
|
assert.match(source, /navigationLaunchOverlay/);
|
|
assert.match(source, /正在打开高德地图/);
|
|
vm.runInNewContext(`${source}\n${checks}`, sandbox, { filename: 'app.js' });
|
|
console.log('vehicle hierarchy aggregation tests: ok');
|