refactor: modularize application domains
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { MonitoringVehicle } from '../types';
|
||||
import { defaultMileageDate, getRangePreset, normalizeRangeLabel } from './date-range';
|
||||
import { formatAbsoluteTime, parseOneOsTime, vehicleStatisticTime } from './oneos-time';
|
||||
import {
|
||||
buildExportMonitoringQuery,
|
||||
buildFullscreenMonitoringQuery,
|
||||
buildInitialMonitoringQuery,
|
||||
buildNextPageMonitoringQuery,
|
||||
type MonitoringQueryModel,
|
||||
} from './query-model';
|
||||
import { vehicleSourceDisplay } from './source-display';
|
||||
|
||||
const baseVehicle: MonitoringVehicle = {
|
||||
plate: '粤A00001',
|
||||
vin: 'TESTVIN',
|
||||
dailyKm: 0,
|
||||
totalKm: 0,
|
||||
source: 'ONEOS',
|
||||
sourceProtocol: null,
|
||||
sourceCategory: null,
|
||||
dataTime: null,
|
||||
calculatedAt: null,
|
||||
updatedAt: null,
|
||||
isOnline: false,
|
||||
isDataSynced: false,
|
||||
customer: null,
|
||||
department: null,
|
||||
manager: null,
|
||||
rentStatus: null,
|
||||
entity: null,
|
||||
project: null,
|
||||
region: null,
|
||||
inventoryLocation: null,
|
||||
brand: null,
|
||||
targetNames: [],
|
||||
};
|
||||
|
||||
const queryModel: MonitoringQueryModel = {
|
||||
sortBy: 'statisticTime',
|
||||
sortOrder: 'asc',
|
||||
searchTerm: '粤A',
|
||||
department: '业务一部',
|
||||
customer: '客户甲',
|
||||
project: '项目甲',
|
||||
entity: '主体甲',
|
||||
rentStatus: '租赁',
|
||||
platePrefix: '粤A',
|
||||
targetNames: ['批次甲'],
|
||||
region: '广州',
|
||||
plates: ['粤A00001', '粤A00002'],
|
||||
mileageRange: { min: '10', max: '500' },
|
||||
rangeStart: '2026-08-01',
|
||||
rangeEnd: '2026-08-12',
|
||||
brands: ['现代'],
|
||||
sourcePriority: ['instrument', 'gps'],
|
||||
};
|
||||
|
||||
test('monitoring date rolls over at 05:00 and presets retain existing ranges', () => {
|
||||
assert.equal(defaultMileageDate(new Date(2026, 7, 13, 4, 59)), '2026-08-12');
|
||||
assert.equal(defaultMileageDate(new Date(2026, 7, 13, 5, 0)), '2026-08-13');
|
||||
|
||||
const now = new Date(2026, 7, 13, 12, 0);
|
||||
assert.deepEqual(getRangePreset('today', now), { start: '2026-08-13', end: '2026-08-13' });
|
||||
assert.deepEqual(getRangePreset('yesterday', now), { start: '2026-08-12', end: '2026-08-12' });
|
||||
assert.deepEqual(getRangePreset('thisWeek', now), { start: '2026-08-10', end: '2026-08-13' });
|
||||
assert.deepEqual(getRangePreset('thisMonth', now), { start: '2026-08-01', end: '2026-08-13' });
|
||||
assert.deepEqual(getRangePreset('last7', now), { start: '2026-08-07', end: '2026-08-13' });
|
||||
assert.deepEqual(getRangePreset('last15', now), { start: '2026-07-30', end: '2026-08-13' });
|
||||
assert.equal(normalizeRangeLabel('', ''), '最新数据');
|
||||
assert.equal(normalizeRangeLabel('2026-08-12', '2026-08-12'), '2026-08-12');
|
||||
assert.equal(normalizeRangeLabel('2026-08-01', '2026-08-12'), '2026-08-01 至 2026-08-12');
|
||||
});
|
||||
|
||||
test('OneOS timestamps keep China Standard Time parsing and relative labels', () => {
|
||||
assert.equal(
|
||||
parseOneOsTime('2026-08-13 09:30:05'),
|
||||
Date.parse('2026-08-13T09:30:05+08:00'),
|
||||
);
|
||||
assert.equal(parseOneOsTime('invalid'), null);
|
||||
assert.equal(formatAbsoluteTime('2026-08-13 09:30:05'), '2026/08/13 09:30:05');
|
||||
|
||||
const display = vehicleStatisticTime(
|
||||
{
|
||||
...baseVehicle,
|
||||
dataTime: '2026-08-13 09:30:05',
|
||||
calculatedAt: '2026-08-13 09:30:06',
|
||||
updatedAt: '2026-08-13 09:30:07',
|
||||
isDataSynced: true,
|
||||
},
|
||||
Date.parse('2026-08-13T09:31:05+08:00'),
|
||||
);
|
||||
assert.deepEqual(display, {
|
||||
label: '1分钟前',
|
||||
title: [
|
||||
'数据时间:2026/08/13 09:30:05',
|
||||
'计算时间:2026/08/13 09:30:06',
|
||||
'更新时间:2026/08/13 09:30:07',
|
||||
].join('\n'),
|
||||
color: 'text-emerald-500',
|
||||
});
|
||||
|
||||
assert.deepEqual(vehicleStatisticTime({ ...baseVehicle, isDataSynced: true }, Date.now()), {
|
||||
label: '统计时间待接口',
|
||||
title: 'OneOS 当前未返回车辆级数据时间',
|
||||
color: 'text-amber-500',
|
||||
});
|
||||
});
|
||||
|
||||
test('vehicle source display retains all source and missing-data states', () => {
|
||||
assert.deepEqual(vehicleSourceDisplay({
|
||||
...baseVehicle,
|
||||
sourceCategory: 'INSTRUMENT',
|
||||
sourceProtocol: 'MQTT',
|
||||
}), {
|
||||
label: '仪表数据',
|
||||
title: 'MQTT',
|
||||
className: 'bg-violet-50 text-violet-600',
|
||||
});
|
||||
assert.equal(vehicleSourceDisplay({ ...baseVehicle, sourceCategory: 'GPS' }).title, 'JT808');
|
||||
assert.equal(vehicleSourceDisplay({ ...baseVehicle, sourceCategory: 'MIXED' }).label, '仪表+GPS');
|
||||
assert.equal(vehicleSourceDisplay({ ...baseVehicle, isDataSynced: true }).label, '来源待接口');
|
||||
assert.equal(vehicleSourceDisplay(baseVehicle).label, '无数据');
|
||||
});
|
||||
|
||||
test('four monitoring query builders preserve pagination and fullscreen differences', () => {
|
||||
const initial = buildInitialMonitoringQuery(queryModel, 50);
|
||||
assert.equal(initial.limit, 50);
|
||||
assert.equal(initial.page, 1);
|
||||
assert.equal(initial.project, '项目甲');
|
||||
assert.equal(initial.entity, '主体甲');
|
||||
assert.equal(initial.mileageMin, '10');
|
||||
assert.equal(initial.mileageMax, '500');
|
||||
assert.equal(initial.plate, '粤A00001,粤A00002');
|
||||
|
||||
const nextPage = buildNextPageMonitoringQuery(queryModel, 50, 3);
|
||||
assert.deepEqual(nextPage, { ...initial, page: 3 });
|
||||
|
||||
const exported = buildExportMonitoringQuery(queryModel);
|
||||
assert.deepEqual(exported, { ...initial, limit: 9999 });
|
||||
|
||||
const fullscreen = buildFullscreenMonitoringQuery(queryModel);
|
||||
assert.equal(fullscreen.limit, 9999);
|
||||
assert.equal(fullscreen.page, 1);
|
||||
assert.equal(fullscreen.customer, '客户甲');
|
||||
assert.equal('project' in fullscreen, false);
|
||||
assert.equal('entity' in fullscreen, false);
|
||||
assert.equal('mileageMin' in fullscreen, false);
|
||||
assert.equal('mileageMax' in fullscreen, false);
|
||||
});
|
||||
|
||||
test('query model converts empty selections exactly as the existing requests do', () => {
|
||||
const query = buildInitialMonitoringQuery({
|
||||
...queryModel,
|
||||
searchTerm: '',
|
||||
department: 'All',
|
||||
customer: 'All',
|
||||
project: 'All',
|
||||
entity: 'All',
|
||||
rentStatus: 'All',
|
||||
platePrefix: 'All',
|
||||
targetNames: [],
|
||||
region: 'All',
|
||||
plates: [],
|
||||
mileageRange: { min: '', max: '' },
|
||||
rangeStart: '',
|
||||
rangeEnd: '',
|
||||
brands: [],
|
||||
}, 50);
|
||||
|
||||
assert.equal(query.search, undefined);
|
||||
assert.equal(query.dept, undefined);
|
||||
assert.equal(query.project, undefined);
|
||||
assert.equal(query.plate, undefined);
|
||||
assert.equal(query.mileageMin, undefined);
|
||||
assert.equal(query.startDate, undefined);
|
||||
assert.equal(query.brands, undefined);
|
||||
assert.deepEqual(query.sourcePriority, ['instrument', 'gps']);
|
||||
});
|
||||
Reference in New Issue
Block a user