This commit is contained in:
fuenmao
2026-11-11 16:12:02 +08:00
parent 20467ca479
commit db0c578890
23 changed files with 464 additions and 426 deletions
+13 -16
View File
@@ -17,7 +17,6 @@ const { default: Overview } = await server.ssrLoadModule('/src/views/DeviceContr
const { default: Detail } = await server.ssrLoadModule('/src/views/DeviceDetailView.vue')
const { deviceControlKey } = await server.ssrLoadModule('/src/devices/controlContext.ts')
const { createDeviceParameterState, syncDeviceParameters } = await server.ssrLoadModule('/src/devices/parameterRegistry.ts')
const mattressParams = await server.ssrLoadModule('/src/devices/mattress/parameters.ts')
const { deviceDetailLocation } = await server.ssrLoadModule('/src/devices/deviceKinds.ts')
const { parameterIsAvailable, parameterHasReading } = await server.ssrLoadModule('/src/devices/parameterState.ts')
const { primaryDeviceEntity, reportedPower } = await server.ssrLoadModule('/src/devices/deviceSummary.ts')
@@ -31,7 +30,7 @@ const devices = [
device('lamp', '演示卧室灯', 'lighting', 'Lighting', [reading('light.room', 'light', 'on', { brightness: 128,
supported_color_modes: ['color_temp'], min_color_temp_kelvin: 2700, max_color_temp_kelvin: 6500,
color_temp_kelvin: 3000, supported_features: 4, effect_list: ['阅读', '助眠'], effect: '阅读' })], { primaryControlEntityId: 'light.room' }),
device('mattress', '演示智能床垫', 'mattress', 'Sleep'),
device('environment', '演示环境检测仪', 'environment', 'EnvironmentSensor'),
device('fragrance', '演示香氛机', 'fragrance', 'Fragrance', [reading('switch.fragrance', 'switch', 'off')], { primaryControlEntityId: 'switch.fragrance' }),
device('speaker', '演示音箱', 'audio', 'Media', [reading('media_player.room', 'media_player', 'idle', { volume_level: .7 })], { primaryControlEntityId: 'media_player.room' }),
]
@@ -50,8 +49,7 @@ async function render(location = '/device-control', overrides = {}) {
environment: { temperature: 25.6, humidity: 58.8 }, controlParameters: {} }),
musicTransition: ref({ running: true, hasVolume: true, volumePercent: 46 }), lightTransition: ref(),
sceneRunning: ref(), switchingDeviceId: ref(), settingParam: ref(), actionFeedback: ref(), currentOperationText: ref(''), lastUpdatedAt: ref(''),
localParams: createDeviceParameterState(), mattressComfort: ref(), mattressActiveSide: ref('left'),
...mattressParams,
localParams: createDeviceParameterState(),
isDeviceTurnedOn: value => Boolean(reportedPower(primaryDeviceEntity(value))),
isDeviceControlAvailable: value => primaryDeviceEntity(value)?.domain === 'climate' ? climateCapabilities(value).canToggle
: Boolean(value?.online && reportedPower(primaryDeviceEntity(value)) !== null &&
@@ -60,7 +58,7 @@ async function render(location = '/device-control', overrides = {}) {
hasParameterReading: (parameter, value) => parameterHasReading(context.haStatus.value?.controlParameters, parameter, value),
toggleDevice: unexpectedCommand, controlSpeaker: unexpectedCommand, applyParam: unexpectedCommand,
executeScene: unexpectedCommand, loadStatus: unexpectedCommand, controlLightTransition: unexpectedCommand,
setMattressMode: unexpectedCommand, setMattressSelect: unexpectedCommand, ...overrides,
...overrides,
}
await router.push(location); await router.isReady()
const app = createSSRApp({ render: () => h(RouterView) })
@@ -171,7 +169,7 @@ test('unbound devices in new categories remain generic and cannot borrow bound c
test('overview has compact tiles and room tabs without detailed sliders or mattress controls', async () => {
const { html } = await render()
assert.equal((html.match(/<article class="device-overview-tile/g) ?? []).length, 4)
for (const value of ['一键启动全屋舒睡模式', '一键关闭设备', '全屋', '睡眠空间', '音量 46%', '演示智能床垫']) assert.ok(html.includes(value), value)
for (const value of ['一键启动全屋舒睡模式', '一键关闭设备', '全屋', '睡眠空间', '音量 46%', '演示环境检测仪']) assert.ok(html.includes(value), value)
for (const value of ['左侧加热档位', '按摩力度', '灯光色温调节', 'van-slider']) assert.ok(!html.includes(value), value)
// 快捷开关与进入详情按钮是同级元素,不会发生嵌套按钮或点击开关误进详情。
for (const tile of html.match(/<article class="device-overview-tile[\s\S]*?<\/article>/g) ?? []) {
@@ -288,16 +286,15 @@ test('offline main light disables standard controls even when an auxiliary entit
test('configured scene light composes shared controls with its own fade and wake extensions', async () => {
const { html } = await render('/device-control/lamp')
for (const text of ['灯光亮度', '灯光色温调节', '灯光模式', '开始助眠渐暗', '晨起唤醒', '灯灭停止按摩']) assert.ok(html.includes(text), text)
for (const text of ['灯光亮度', '灯光色温调节', '灯光模式', '开始助眠渐暗', '晨起唤醒']) assert.ok(html.includes(text), text)
assert.equal((html.match(/<section class="device-card"/g) ?? []).length, 1)
assert.ok(!html.includes('不自动加入舒睡或唤醒场景'))
})
test('mattress detail retains full controls and never renders other devices or scene-wide actions', async () => {
test('retired mattress detail link has no controls', async () => {
const { html } = await render('/device-control/mattress')
assert.equal((html.match(/<section class="device-card"/g) ?? []).length, 1)
for (const value of ['演示智能床垫', '全部设备', '左侧加热档位', '右侧加热档位', '左侧承托模式', '右侧承托模式', '左肩软硬度', '右腿软硬度', '左侧按摩部位', '右侧按摩部位', '按摩力度']) assert.ok(html.includes(value), value)
for (const value of ['演示卧室灯', '演示香氛机', '一键启动全屋舒睡模式', 'device-overview-tile']) assert.ok(!html.includes(value), value)
assert.ok(html.includes('未找到这个设备'))
for (const value of ['左侧加热档位', '按摩力度', '左肩软硬度']) assert.ok(!html.includes(value), value)
})
test('fragrance detail retains separate nightlight and unavailable quick-release feedback', async () => {
@@ -330,13 +327,13 @@ test('missing, offline and disconnected devices display honest explanatory state
assert.ok((await render('/device-control/removed')).html.includes('未找到这个设备'))
const offline = { ...devices[1], online: false }
const snapshot = { connected: true, devices: [offline], summary: { totalDevices: 1, onlineDevices: 0 } }
const { html } = await render('/device-control/mattress', { haStatus: ref(snapshot) })
const { html } = await render('/device-control/environment', { haStatus: ref(snapshot) })
assert.ok(html.includes('设备回读显示离线'))
assert.ok(html.includes('右侧按摩部位'))
const disconnected = await render('/device-control/mattress', { haStatus: ref({ ...snapshot, connected: false, message: '连接失败' }) })
assert.ok(html.includes('温度'))
const disconnected = await render('/device-control/environment', { haStatus: ref({ ...snapshot, connected: false, message: '连接失败' }) })
assert.ok(disconnected.html.includes('暂时无法读取设备'))
assert.ok(!disconnected.html.includes('未找到这个设备'))
const loading = await render('/device-control/mattress', { haStatus: ref(), loading: ref(true) })
const loading = await render('/device-control/environment', { haStatus: ref(), loading: ref(true) })
assert.ok(loading.html.includes('正在读取设备详情'))
})
@@ -350,7 +347,7 @@ test('detail route safely round-trips unusual device IDs; unfinished constitutio
})
test('offline parameter details disable controls and do not present default values as current readings', async () => {
for (const item of [devices[0], devices[1], devices[2]]) {
for (const item of [devices[0], devices[2]]) {
const snapshot = { connected: true, devices: [{ ...item, online: false }], controlParameters: {} }
const { html } = await render(deviceDetailLocation(item.deviceId), { haStatus: ref(snapshot) })
const card = html.match(/<section class="device-card"[\s\S]*?<\/section>/)?.[0]