Merge origin/feature/N7Web into feature/N7Web
Integrate the remote firmware workbench, OTA delivery, and timed mode schedules while preserving the local N7 scene and runtime changes.
This commit is contained in:
@@ -5,6 +5,8 @@ import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from cognitive_agent.application.mode_profile import ModeProfileRegistry
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
@@ -44,8 +46,18 @@ def test_runtime_mode_catalog_exposes_safe_executable_action_details() -> None:
|
||||
if mode["id"] == "office"
|
||||
)
|
||||
|
||||
assert len(office["operations"]) == office["operation_count"] == 10
|
||||
assert len(office["operations"]) == office["operation_count"] == 12
|
||||
assert office["operations"][0] == {
|
||||
"domain": "seat",
|
||||
"capability": "seat.set_driver_ventilation_native",
|
||||
"target_scope": "driver",
|
||||
"operation": "set",
|
||||
"parameters": {"state": "on", "level": 2},
|
||||
}
|
||||
assert next(
|
||||
operation for operation in office["operations"]
|
||||
if operation["capability"] == "seat.set_longitudinal_position"
|
||||
) == {
|
||||
"domain": "seat",
|
||||
"capability": "seat.set_longitudinal_position",
|
||||
"target_scope": "passenger",
|
||||
@@ -241,7 +253,7 @@ def test_top_mode_action_formatter_uses_device_target_action_and_parameter_value
|
||||
actions = json.loads(completed.stdout)
|
||||
|
||||
assert len(actions) == office["operation_count"]
|
||||
assert actions[0] == {
|
||||
assert next(action for action in actions if action["action"] == "调节座椅前后位置") == {
|
||||
"device": "座椅",
|
||||
"target": "副驾",
|
||||
"action": "调节座椅前后位置",
|
||||
@@ -266,7 +278,11 @@ def test_mode_chip_scroller_keeps_clicks_and_only_captures_real_drags() -> None:
|
||||
capture = snippet.index("group.setPointerCapture(pointerId);")
|
||||
assert capture > threshold
|
||||
assert "let pointerCaptured = false;" in snippet
|
||||
assert "if (pointerCaptured) group.releasePointerCapture?.(pointerId);" in snippet
|
||||
assert "const finishedPointerId = pointerId;" in snippet
|
||||
assert "pointerId = null;" in snippet
|
||||
assert "group.hasPointerCapture?.(finishedPointerId)" in snippet
|
||||
assert "group.releasePointerCapture(finishedPointerId);" in snippet
|
||||
assert 'group.addEventListener("lostpointercapture", finishPointerDrag);' in snippet
|
||||
assert 'event.stopImmediatePropagation();' in snippet
|
||||
|
||||
|
||||
@@ -309,6 +325,7 @@ const context = {
|
||||
modeProfiles() { return [savedMode]; },
|
||||
setModeRailSelection(mode) { calls.push(["select", mode?.id]); },
|
||||
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
|
||||
hideModeActionPopover() {},
|
||||
showToast(message) { calls.push(["toast", message]); },
|
||||
hasExecutableModeCommandFlow(mode) {
|
||||
const flow = mode?.command_flow;
|
||||
@@ -418,6 +435,7 @@ const context = {
|
||||
setModeRailSelection(mode) { calls.push(["select", mode?.id]); },
|
||||
setModeExecutionState(modeId, status) { calls.push(["state", modeId, status]); },
|
||||
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
|
||||
hideModeActionPopover() {},
|
||||
showToast(message) { calls.push(["toast", message]); },
|
||||
hasExecutableModeCommandFlow(mode) {
|
||||
const flow = mode?.command_flow;
|
||||
@@ -484,6 +502,44 @@ console.log(JSON.stringify({ result: context.result, calls }));
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("phase", "input_mode"), (
|
||||
("capturing", "manual"),
|
||||
("capturing", "wake"),
|
||||
("finalizing", "manual"),
|
||||
("finalizing", "wake"),
|
||||
("arming", "manual"),
|
||||
))
|
||||
def test_mode_click_during_dictation_does_not_create_stuck_running_state(
|
||||
phase: str, input_mode: str,
|
||||
) -> None:
|
||||
app = (WEB / "app.js").read_text(encoding="utf-8")
|
||||
start = app.index("function applyModeCommand")
|
||||
end = app.index("\n\nfunction applyRuntimeTargetPolicy", start)
|
||||
script = "const snippet = " + json.dumps(app[start:end]) + ";\n"
|
||||
script += "const speech = " + json.dumps({"phase": phase, "inputMode": input_mode}) + ";\n"
|
||||
script += r"""
|
||||
const vm = require("node:vm");
|
||||
const calls = [];
|
||||
const context = {
|
||||
window: {
|
||||
CognitiveAgentAssistant: { isPending() { return false; } },
|
||||
CognitiveAgentSpeech: speech,
|
||||
},
|
||||
showToast(message) { calls.push(message); },
|
||||
};
|
||||
vm.createContext(context);
|
||||
vm.runInContext(snippet + '\nthis.result = applyModeCommand("进入办公模式", "office");', context);
|
||||
console.log(JSON.stringify({ result: context.result, calls }));
|
||||
"""
|
||||
completed = subprocess.run(
|
||||
[node_runtime(), "-e", script], check=True, capture_output=True, encoding="utf-8",
|
||||
)
|
||||
assert json.loads(completed.stdout) == {
|
||||
"result": False,
|
||||
"calls": ["请等待语音整理完成后再发送"],
|
||||
}
|
||||
|
||||
|
||||
def test_top_and_management_mode_cards_share_the_assistant_submission_entrypoint() -> None:
|
||||
app = (WEB / "app.js").read_text(encoding="utf-8")
|
||||
install_start = app.index('document.addEventListener("DOMContentLoaded"')
|
||||
|
||||
Reference in New Issue
Block a user