test(config_flow,media_player,remote): digit-delay/send-enter options (store + window/error validation), IME-first text entry with ADB fallback tier, icon column parse/prefill/store (P5-2)
This commit is contained in:
1 parent
3d026ae60e
commit
a3b397920e
7 files changed
+320
-18
No files matched your search
@@ -724,6 +724,99 @@ class TestAdbCommandService:
|
||||
assert not player.hass.services.has_service("nvidia_shield", "adb_command")
|
||||
|
||||
|
||||
class TestTextEntry:
|
||||
"""P5-2: text entry — protocol IME first, ADB input-text fallback."""
|
||||
|
||||
def _player_with_texts(self, modules):
|
||||
player, _s, _i = make_player(modules)
|
||||
protocol = player._manager._protocol
|
||||
protocol.text_calls = []
|
||||
protocol.send_text = lambda t: protocol.text_calls.append(t) or True
|
||||
shell_texts = []
|
||||
|
||||
async def _shell(cmd):
|
||||
shell_texts.append(cmd)
|
||||
return "ok"
|
||||
|
||||
player._manager._poller.shell = _shell
|
||||
return player, protocol, shell_texts
|
||||
|
||||
async def test_protocol_ime_sends_first(self, modules):
|
||||
player, protocol, shell_texts = self._player_with_texts(modules)
|
||||
|
||||
await player.async_play_media("text", "hello world")
|
||||
|
||||
assert protocol.text_calls == ["hello world"]
|
||||
assert shell_texts == [] # ADB fallback not used
|
||||
|
||||
async def test_adb_fallback_when_the_protocol_send_fails(self, modules):
|
||||
player, protocol, shell_texts = self._player_with_texts(modules)
|
||||
|
||||
def _fail(text):
|
||||
protocol.text_calls.append(text)
|
||||
raise RuntimeError("ime closed")
|
||||
|
||||
protocol.send_text = _fail
|
||||
await player.async_play_media("text", "hello 'world'")
|
||||
|
||||
import shlex
|
||||
|
||||
sample = "hello " + chr(39) + "world" + chr(39)
|
||||
assert shell_texts == [f"input text {shlex.quote(sample)}"]
|
||||
|
||||
async def test_empty_text_is_ignored(self, modules):
|
||||
player, protocol, shell_texts = self._player_with_texts(modules)
|
||||
|
||||
await player.async_play_media("text", "")
|
||||
|
||||
assert protocol.text_calls == [] and shell_texts == []
|
||||
player._manager._protocol.send_text = lambda t: True
|
||||
await player.async_play_media("text", "")
|
||||
assert protocol.text_calls == []
|
||||
|
||||
|
||||
class TestIconOverride:
|
||||
"""P5-2: optional icon column → CONF_APP_ICON_MAP → entity_picture."""
|
||||
|
||||
def _player_with_icon(self, modules, icon_map):
|
||||
player, _s, _i = make_player(modules)
|
||||
player._entry.options["app_icon_map"] = icon_map
|
||||
return player
|
||||
|
||||
async def test_icon_serves_when_no_jellyfin_picture(self, modules):
|
||||
import asyncio
|
||||
|
||||
player = self._player_with_icon(
|
||||
modules, {"ar.tvplayer.tv": "http://icons.local/tivi.png"}
|
||||
)
|
||||
manager = player._manager
|
||||
manager._on_current_app("ar.tvplayer.tv")
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
assert player.media_image_url == "http://icons.local/tivi.png"
|
||||
|
||||
async def test_no_icon_for_an_unmatched_package(self, modules):
|
||||
import asyncio
|
||||
|
||||
player = self._player_with_icon(
|
||||
modules, {"com.netflix.ninja": "http://icons.local/netflix.png"}
|
||||
)
|
||||
player._manager._on_current_app("ar.tvplayer.tv")
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
assert player.media_image_url is None
|
||||
|
||||
async def test_jellyfin_picture_wins_over_the_icon_map(self, modules):
|
||||
|
||||
player = self._player_with_icon(
|
||||
modules, {"ar.tvplayer.tv": "http://icons.local/tivi.png"}
|
||||
)
|
||||
player._jf_active = lambda: True
|
||||
player._jf_attr = lambda attr: "http://jf.local/pic.jpg"
|
||||
|
||||
assert player.media_image_url == "http://jf.local/pic.jpg"
|
||||
|
||||
|
||||
class TestServiceRegistryKey:
|
||||
"""F4: the participant key is computed once — add/remove always match."""
|
||||
|
||||
|
||||
Reference in new issue
Block a user