diff --git a/homeassistant/components/collection_image/config_flow.py b/homeassistant/components/collection_image/config_flow.py index bb529de913f560..35f29d58f88107 100644 --- a/homeassistant/components/collection_image/config_flow.py +++ b/homeassistant/components/collection_image/config_flow.py @@ -4,16 +4,21 @@ import voluptuous as vol +from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN from homeassistant.components.media_player import BrowseError, MediaClass -from homeassistant.components.media_source import async_browse_media +from homeassistant.components.media_source import URI_SCHEME, async_browse_media from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.helpers.selector import MediaSelector from .const import CONF_MEDIA, DOMAIN +IMAGE_MEDIA_URI = f"{URI_SCHEME}{IMAGE_DOMAIN}" + STEP_USER_DATA_SCHEMA = vol.Schema( { - vol.Required(CONF_MEDIA): MediaSelector({"accept": ["directory"]}), + vol.Required(CONF_MEDIA): MediaSelector( + {"accept": ["directory"], "multiple": True} + ), } ) @@ -28,24 +33,41 @@ async def async_step_user( """Handle the initial step.""" errors: dict[str, str] = {} placeholders: dict[str, str] = {} + found_pictures = False + title = "Unnamed collection" if user_input is not None: - user_media = user_input[CONF_MEDIA] - try: - browse = await async_browse_media( - self.hass, user_media["media_content_id"] - ) - except BrowseError as err: - errors["media"] = "failed_browse" - placeholders["error"] = str(err) - else: - if browse.children and any( - item.media_class == MediaClass.IMAGE for item in browse.children - ): + user_media_list = user_input[CONF_MEDIA] + for user_media in user_media_list: + if user_media["media_content_id"] == IMAGE_MEDIA_URI: + errors["media"] = "invalid_selection" + placeholders["error"] = IMAGE_MEDIA_URI + break + try: + browse = await async_browse_media( + self.hass, user_media["media_content_id"] + ) + except BrowseError as err: + errors["media"] = "failed_browse" + placeholders["error"] = str(err) + break + else: + if ( + not found_pictures + and browse.children + and any( + item.media_class == MediaClass.IMAGE + for item in browse.children + ) + ): + found_pictures = True + if browse.title: + title = f"{browse.title} collection" + if "media" not in errors: + if found_pictures: return self.async_create_entry( - title=f"{browse.title or 'Unnamed'} collection", + title=title, data=user_input, ) - errors["media"] = "selected_media_no_images" return self.async_show_form( diff --git a/homeassistant/components/collection_image/image.py b/homeassistant/components/collection_image/image.py index b138c9f0d181ed..40df64c9d1e35e 100644 --- a/homeassistant/components/collection_image/image.py +++ b/homeassistant/components/collection_image/image.py @@ -8,6 +8,7 @@ from homeassistant.components.image import ImageEntity from homeassistant.components.media_player import ( BrowseError, + BrowseMedia, MediaClass, async_process_play_media_url, ) @@ -36,11 +37,15 @@ async def async_setup_entry( ) -> None: """Set up the Collection Image image entities.""" media = entry.data[CONF_MEDIA] + if isinstance(media, dict): + content_ids = [media["media_content_id"]] + else: + content_ids = [item["media_content_id"] for item in media] async_add_entities( [ CollectionImageImageEntity( name=entry.title, - media_content_id=media["media_content_id"], + media_content_ids=content_ids, unique_id=entry.entry_id, hass=hass, ) @@ -58,7 +63,7 @@ class CollectionImageImageEntity(ImageEntity): def __init__( self, name: str, - media_content_id: str, + media_content_ids: list[str], unique_id: str, hass: HomeAssistant, ) -> None: @@ -67,11 +72,10 @@ def __init__( self.path = None self._attr_unique_id = unique_id self._attr_name = name - self.media_content_id = media_content_id + self.media_content_ids = media_content_ids async def get_next_image(self) -> None: - """Update the image entity with the next image from the source media.""" - + """Update the image entity with a random image from configured media sources.""" self._cached_image = None def set_unavailable() -> None: @@ -81,59 +85,76 @@ def set_unavailable() -> None: self._attr_image_url = UNDEFINED self.async_write_ha_state() - try: - media = await async_browse_media(self.hass, self.media_content_id) - except BrowseError as err: - if not self._unavailable_logged: - _LOGGER.info("%s: %s", self.entity_id, str(err)) - set_unavailable() - return + images: list[BrowseMedia] = [] - if media.children and ( - filtered := [ - item for item in media.children if item.media_class == MediaClass.IMAGE - ] - ): - child = random.choice(filtered) + for media_content_id in self.media_content_ids: try: - resolved = await async_resolve_media( - self.hass, child.media_content_id, self.entity_id - ) - except Unresolvable as err: + media = await async_browse_media(self.hass, media_content_id) + except BrowseError as err: if not self._unavailable_logged: - _LOGGER.info("%s: %s", self.entity_id, str(err)) - set_unavailable() - return - - if resolved.url: - self.path = None - self._attr_image_url = async_process_play_media_url( - self.hass, resolved.url + _LOGGER.info( + "%s: Unable to browse %s: %s", + self.entity_id, + media_content_id, + err, + ) + continue + + if media.children: + images.extend( + item + for item in media.children + if item.media_class == MediaClass.IMAGE ) - else: - self.path = resolved.path - self._attr_image_url = UNDEFINED - - self._attr_content_type = resolved.mime_type - self._attr_available = True - self._attr_image_last_updated = dt_util.utcnow() - if self._unavailable_logged: + + if not images: + if not self._unavailable_logged: _LOGGER.info( - "%s: Has become available again", + "%s: No valid images in %s", self.entity_id, + self.media_content_ids, ) - self._unavailable_logged = False - self.async_write_ha_state() + set_unavailable() return - if not self._unavailable_logged: - _LOGGER.info( - "%s: No valid images in %s", + child = random.choice(images) + + try: + resolved = await async_resolve_media( + self.hass, + child.media_content_id, self.entity_id, - self.media_content_id, ) - set_unavailable() - return + except Unresolvable as err: + if not self._unavailable_logged: + _LOGGER.info( + "%s: Unable to resolve %s: %s", + self.entity_id, + child.media_content_id, + err, + ) + set_unavailable() + return + + if resolved.url: + self.path = None + self._attr_image_url = async_process_play_media_url( + self.hass, + resolved.url, + ) + else: + self.path = resolved.path + self._attr_image_url = UNDEFINED + + self._attr_content_type = resolved.mime_type + self._attr_available = True + self._attr_image_last_updated = dt_util.utcnow() + + if self._unavailable_logged: + _LOGGER.info("%s: Has become available again", self.entity_id) + + self._unavailable_logged = False + self.async_write_ha_state() @override async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/collection_image/strings.json b/homeassistant/components/collection_image/strings.json index facf104c02ad47..a21bef727cf54a 100644 --- a/homeassistant/components/collection_image/strings.json +++ b/homeassistant/components/collection_image/strings.json @@ -2,6 +2,7 @@ "config": { "error": { "failed_browse": "Failed to browse media: {error}", + "invalid_selection": "Invalid media selected: {error}", "selected_media_no_images": "The selected media has no images. Please select a media directory with images." }, "step": { @@ -10,9 +11,9 @@ "media": "Media" }, "data_description": { - "media": "The media directory where images will be retrieved from." + "media": "The media where images will be retrieved from." }, - "description": "The Collection Image integration creates a single image entity by selecting an image from the selected media folder.", + "description": "The Collection Image integration creates an image entity which renders an image from the selected media.", "submit": "Create" } } diff --git a/tests/components/collection_image/conftest.py b/tests/components/collection_image/conftest.py index 3373b34f7da149..8c9bb0dd531296 100644 --- a/tests/components/collection_image/conftest.py +++ b/tests/components/collection_image/conftest.py @@ -5,10 +5,17 @@ import pytest from homeassistant.components.collection_image.const import DOMAIN -from homeassistant.components.media_player import BrowseMedia, MediaClass +from homeassistant.components.media_player import BrowseError, BrowseMedia, MediaClass from homeassistant.components.media_source import BrowseMediaSource, PlayMedia +from homeassistant.core import HomeAssistant -from .const import TEST_IMAGE +from .const import ( + MOCK_MEDIA_URI_1, + MOCK_MEDIA_URI_2, + MOCK_MEDIA_URI_BROWSE_ERROR, + MOCK_MEDIA_URI_EMPTY, + TEST_IMAGE, +) from tests.common import MockConfigEntry @@ -30,13 +37,13 @@ def config_entry() -> MockConfigEntry: @pytest.fixture def browse_media_result() -> BrowseMediaSource: - """Return a default collection containing one image.""" + """Return the default collection containing one image.""" return BrowseMediaSource( domain=None, identifier=None, media_class="", media_content_type="", - title="", + title="My pictures", can_play=False, can_expand=True, children=[ @@ -61,13 +68,114 @@ def browse_media_result() -> BrowseMediaSource: @pytest.fixture -def mock_media_source(browse_media_result: BrowseMediaSource): +def browse_media_result_2() -> BrowseMediaSource: + """Return a collection containing three images.""" + return BrowseMediaSource( + domain=None, + identifier=None, + media_class="", + media_content_type="", + title="Three images", + can_play=False, + can_expand=True, + children=[ + BrowseMedia( + media_class=MediaClass.IMAGE, + media_content_id=f"media-source://mymedia_2/photo_{number}", + media_content_type="image/png", + title=f"picture {number}", + can_play=True, + can_expand=False, + ) + for number in range(1, 4) + ], + ) + + +@pytest.fixture +def browse_media_result_empty() -> BrowseMediaSource: + """Return a collection containing nothing.""" + return BrowseMediaSource( + domain=None, + identifier=None, + media_class="", + media_content_type="", + title="Empty folder", + can_play=False, + can_expand=True, + children=[], + ) + + +@pytest.fixture +def browse_media_browse_error() -> BrowseMediaSource: + """Throws a BrowseError.""" + return BrowseMediaSource( + domain=None, + identifier=None, + media_class="", + media_content_type="", + title="My pictures", + can_play=False, + can_expand=True, + children=[ + BrowseMedia( + media_class=MediaClass.MUSIC, + media_content_id="media-source://mymedia/music", + media_content_type="audio/mp3", + title="a music track", + can_play=True, + can_expand=False, + ), + BrowseMedia( + media_class=MediaClass.IMAGE, + media_content_id="media-source://mymedia/photo", + media_content_type="image/png", + title="a picture", + can_play=True, + can_expand=False, + ), + ], + ) + + +@pytest.fixture +def mock_media_source( + browse_media_result: BrowseMediaSource, + browse_media_result_2: BrowseMediaSource, + browse_media_result_empty: BrowseMediaSource, +): """Mock browsing and resolving the configured media source.""" + + async def browse_side_effect( + _hass: HomeAssistant, + media_content_id, + *, + content_filter=None, + ): + if media_content_id == MOCK_MEDIA_URI_1: + return browse_media_result + + if media_content_id == MOCK_MEDIA_URI_2: + return browse_media_result_2 + + if media_content_id == MOCK_MEDIA_URI_EMPTY: + return browse_media_result_empty + + if media_content_id == MOCK_MEDIA_URI_BROWSE_ERROR: + raise BrowseError("Mock directory failed to browse") + + raise ValueError(f"Unexpected media content ID: {media_content_id}") + with ( + patch( + "homeassistant.components.collection_image.config_flow.async_browse_media", + new=AsyncMock(side_effect=browse_side_effect), + ) as mock_config_flow_browse, patch( "homeassistant.components.collection_image.image.async_browse_media", - new=AsyncMock(return_value=browse_media_result), - ) as mock_browse, + new=AsyncMock(side_effect=browse_side_effect), + ) as mock_image_browse, patch( "homeassistant.components.collection_image.image.async_resolve_media", new=AsyncMock( @@ -79,4 +187,4 @@ def mock_media_source(browse_media_result: BrowseMediaSource): ), ) as mock_resolve, ): - yield mock_browse, mock_resolve + yield mock_config_flow_browse, mock_image_browse, mock_resolve diff --git a/tests/components/collection_image/const.py b/tests/components/collection_image/const.py index 0c4bb7df0f1b9c..2e4f3dc4cc05fc 100644 --- a/tests/components/collection_image/const.py +++ b/tests/components/collection_image/const.py @@ -4,3 +4,8 @@ TEST_IMAGE = Path(__file__).parent / "test.png" DEFAULT_ENTITY_ID = "image.random_image" + +MOCK_MEDIA_URI_1 = "media-source://mymedia" +MOCK_MEDIA_URI_2 = "media-source://mymedia_2" +MOCK_MEDIA_URI_EMPTY = "media-source://mymedia_empty" +MOCK_MEDIA_URI_BROWSE_ERROR = "media-source://mymedia_error" diff --git a/tests/components/collection_image/test_config_flow.py b/tests/components/collection_image/test_config_flow.py index 402b46475e44ed..7c3eff78bbd5ee 100644 --- a/tests/components/collection_image/test_config_flow.py +++ b/tests/components/collection_image/test_config_flow.py @@ -2,80 +2,73 @@ from unittest.mock import patch +import pytest + from homeassistant import config_entries +from homeassistant.components.collection_image.config_flow import IMAGE_MEDIA_URI from homeassistant.components.collection_image.const import DOMAIN -from homeassistant.components.media_player import BrowseMedia, MediaClass -from homeassistant.components.media_source import BrowseMediaSource from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from .const import ( + MOCK_MEDIA_URI_1, + MOCK_MEDIA_URI_2, + MOCK_MEDIA_URI_BROWSE_ERROR, + MOCK_MEDIA_URI_EMPTY, +) -async def _assert_successful_configure( - hass: HomeAssistant, previous_step: config_entries.ConfigFlowResult -) -> None: - with ( - patch( - "homeassistant.components.collection_image.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - patch( - "homeassistant.components.collection_image.config_flow.async_browse_media", - return_value=BrowseMediaSource( - domain=None, - identifier=None, - media_class="", - media_content_type="", - title="My pictures", - can_play=False, - can_expand=True, - children=[ - BrowseMedia( - media_class=MediaClass.IMAGE, - media_content_id="media-source://mymedia/photo", - media_content_type="image/png", - title="a picture", - can_play=True, - can_expand=False, - ), - ], - ), - ), - ): - result = await hass.config_entries.flow.async_configure( - previous_step["flow_id"], - { - "media": { - "media_content_id": "media-source://mymedia", - "media_content_type": "", - }, - }, - ) +from tests.common import AsyncMock - assert result.get("type") is FlowResultType.CREATE_ENTRY - assert result.get("title") == "My pictures collection" - assert result.get("data") == { - "media": { - "media_content_id": "media-source://mymedia", - "media_content_type": "", - }, - } - assert len(mock_setup_entry.mock_calls) == 1 +def _data_from_uris(uris: list[str]) -> dict: + return { + "media": [ + { + "media_content_id": uri, + "media_content_type": "", + } + for uri in uris + ] + } -async def test_config_flow(hass: HomeAssistant) -> None: - """Test the config flow.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result.get("type") is FlowResultType.FORM - assert result.get("errors") == {} +@pytest.fixture +def mock_setup_entry(): + """Mock collection_image setup successfully.""" - await _assert_successful_configure(hass, result) + with patch( + "homeassistant.components.collection_image.async_setup_entry", + new=AsyncMock(return_value=True), + ) as mock_setup: + yield mock_setup -async def test_config_flow_with_error(hass: HomeAssistant) -> None: - """Test the config flow with an invalid directory.""" +@pytest.mark.parametrize( + ("uris", "expected_title"), + [ + ([MOCK_MEDIA_URI_1], "My pictures collection"), + ( + [MOCK_MEDIA_URI_2, MOCK_MEDIA_URI_1], + "Three images collection", + ), + ( + [ + MOCK_MEDIA_URI_EMPTY, + MOCK_MEDIA_URI_1, + MOCK_MEDIA_URI_2, + ], + "My pictures collection", + ), + ], +) +async def test_config_flow( + hass: HomeAssistant, + mock_media_source, + mock_setup_entry, + uris: list[str], + expected_title: str, +) -> None: + """Test the config flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -83,79 +76,74 @@ async def test_config_flow_with_error(hass: HomeAssistant) -> None: assert result.get("type") is FlowResultType.FORM assert result.get("errors") == {} - with ( - patch( - "homeassistant.components.collection_image.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - patch( - "homeassistant.components.collection_image.config_flow.async_browse_media", - return_value=BrowseMediaSource( - domain=None, - identifier=None, - media_class="", - media_content_type="", - title="", - can_play=False, - can_expand=True, - children=[], - ), - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "media": { - "media_content_id": "media-source://mymedia_empty", - "media_content_type": "", - }, - }, - ) - await hass.async_block_till_done() + data = _data_from_uris(uris) - assert result.get("type") is FlowResultType.FORM - assert result.get("title") is None - assert result.get("data") is None - assert result.get("errors") == {"media": "selected_media_no_images"} - assert len(mock_setup_entry.mock_calls) == 0 + result = await hass.config_entries.flow.async_configure(result["flow_id"], data) - # Try again successfully to ensure we can recover from errors - await _assert_successful_configure(hass, result) + assert result.get("type") is FlowResultType.CREATE_ENTRY + assert result.get("title") == expected_title + assert result.get("data") == data + assert len(mock_setup_entry.mock_calls) == 1 -async def test_config_flow_with_exception(hass: HomeAssistant) -> None: - """Test the config flow with a browse failure.""" +@pytest.mark.parametrize( + ("uris", "error", "placeholders"), + [ + ( + [MOCK_MEDIA_URI_EMPTY], + "selected_media_no_images", + {}, + ), + ( + [MOCK_MEDIA_URI_EMPTY, MOCK_MEDIA_URI_EMPTY], + "selected_media_no_images", + {}, + ), + ( + [MOCK_MEDIA_URI_BROWSE_ERROR], + "failed_browse", + {"error": "Mock directory failed to browse"}, + ), + ( + [MOCK_MEDIA_URI_1, MOCK_MEDIA_URI_BROWSE_ERROR], + "failed_browse", + {"error": "Mock directory failed to browse"}, + ), + ([IMAGE_MEDIA_URI], "invalid_selection", {"error": IMAGE_MEDIA_URI}), + ], +) +async def test_config_flow_error( + hass: HomeAssistant, + mock_media_source, + mock_setup_entry, + uris: list[str], + error: str, + placeholders: dict, +) -> None: + """Test the config flow with an empty directory.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") is FlowResultType.FORM assert result.get("errors") == {} - - with ( - patch( - "homeassistant.components.collection_image.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "media": { - "media_content_id": "media-source://mymedia", - "media_content_type": "", - }, - }, - ) - await hass.async_block_till_done() + data = _data_from_uris(uris) + result = await hass.config_entries.flow.async_configure(result["flow_id"], data) + await hass.async_block_till_done() assert result.get("type") is FlowResultType.FORM assert result.get("title") is None assert result.get("data") is None - assert result.get("errors") == {"media": "failed_browse"} - assert result.get("description_placeholders") == { - "error": "Media Source not loaded" - } + assert result.get("errors") == {"media": error} + assert result.get("description_placeholders") == placeholders assert len(mock_setup_entry.mock_calls) == 0 - await _assert_successful_configure(hass, result) + # Try again successfully to ensure we can recover from errors + data = _data_from_uris([MOCK_MEDIA_URI_1]) + + result = await hass.config_entries.flow.async_configure(result["flow_id"], data) + + assert result.get("type") is FlowResultType.CREATE_ENTRY + assert result.get("title") == "My pictures collection" + assert result.get("data") == data + assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/collection_image/test_image.py b/tests/components/collection_image/test_image.py index 0545553ab91201..48b8d5b69e1f8c 100644 --- a/tests/components/collection_image/test_image.py +++ b/tests/components/collection_image/test_image.py @@ -7,13 +7,21 @@ from freezegun import freeze_time import pytest +from homeassistant.components.collection_image.const import DOMAIN from homeassistant.components.image import Image, async_get_image +from homeassistant.components.media_player import MediaClass from homeassistant.components.media_source import BrowseMediaSource, PlayMedia from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, STATE_UNAVAILABLE from homeassistant.core import CoreState, HomeAssistant from homeassistant.exceptions import HomeAssistantError -from .const import DEFAULT_ENTITY_ID, TEST_IMAGE +from .const import ( + DEFAULT_ENTITY_ID, + MOCK_MEDIA_URI_1, + MOCK_MEDIA_URI_2, + MOCK_MEDIA_URI_EMPTY, + TEST_IMAGE, +) from tests.common import MockConfigEntry from tests.typing import ClientSessionGenerator @@ -21,6 +29,19 @@ TEST_TIME = "2025-11-08T12:00:00+00:00" +def _create_config_entry(uris: str | list[str]) -> MockConfigEntry: + if isinstance(uris, str): + media = {"media_content_id": uris, "media_content_type": ""} + else: + media = [{"media_content_id": uri, "media_content_type": ""} for uri in uris] + + return MockConfigEntry( + data={"media": media}, + domain=DOMAIN, + title="Random Image", + ) + + async def test_image( hass: HomeAssistant, hass_client: ClientSessionGenerator, @@ -49,6 +70,58 @@ async def test_image( assert body == expected_data +async def test_image_multi( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + config_entry: MockConfigEntry, + mock_media_source, + browse_media_result: BrowseMediaSource, + browse_media_result_2: BrowseMediaSource, +) -> None: + """Test multiple media sources.""" + + expected_images = [ + child + for result in (browse_media_result, browse_media_result_2) + for child in result.children + if child.media_class == MediaClass.IMAGE + ] + + with ( + freeze_time(TEST_TIME), + patch( + "homeassistant.components.collection_image.image.random.choice", + return_value=expected_images[-1], + ) as mock_choice, + ): + config_entry = _create_config_entry([MOCK_MEDIA_URI_1, MOCK_MEDIA_URI_2]) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + mock_choice.assert_called_once_with(expected_images) + + assert [image.media_content_id for image in mock_choice.call_args.args[0]] == [ + "media-source://mymedia/photo", + "media-source://mymedia_2/photo_1", + "media-source://mymedia_2/photo_2", + "media-source://mymedia_2/photo_3", + ] + + state = hass.states.get(DEFAULT_ENTITY_ID) + + assert state and state.state == TEST_TIME + + client = await hass_client() + + resp = await client.get(f"/api/image_proxy/{DEFAULT_ENTITY_ID}") + assert resp.status == HTTPStatus.OK + assert resp.content_type == "image/png" + expected_data = await hass.async_add_executor_job(TEST_IMAGE.read_bytes) + body = await resp.read() + assert body == expected_data + + async def test_image_during_startup( hass: HomeAssistant, hass_client: ClientSessionGenerator, @@ -135,26 +208,15 @@ async def test_image_url( async def test_no_images( hass: HomeAssistant, hass_client: ClientSessionGenerator, - config_entry: MockConfigEntry, caplog: pytest.LogCaptureFixture, + mock_media_source, ) -> None: """Test when there are no images in the media folder.""" - with patch( - "homeassistant.components.collection_image.image.async_browse_media", - return_value=BrowseMediaSource( - domain=None, - identifier=None, - media_class="", - media_content_type="", - title="", - can_play=False, - can_expand=True, - children=[], - ), - ): - config_entry.add_to_hass(hass) - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() + + config_entry = _create_config_entry([MOCK_MEDIA_URI_EMPTY]) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() state = hass.states.get(DEFAULT_ENTITY_ID) @@ -163,7 +225,8 @@ async def test_no_images( await hass.async_block_till_done(wait_background_tasks=True) assert ( - "image.random_image: No valid images in media-source://mymedia" in caplog.text + "image.random_image: No valid images in ['media-source://mymedia_empty']" + in caplog.text ) client = await hass_client() @@ -189,7 +252,10 @@ async def test_media_error( await hass.async_block_till_done(wait_background_tasks=True) - assert "image.random_image: Media Source not loaded" in caplog.text + assert ( + "image.random_image: Unable to browse media-source://mymedia: Media Source not loaded" + in caplog.text + ) client = await hass_client() resp = await client.get(f"/api/image_proxy/{DEFAULT_ENTITY_ID}") @@ -220,7 +286,10 @@ async def test_unresolvable( await hass.async_block_till_done(wait_background_tasks=True) - assert "image.random_image: Media Source not loaded" in caplog.text + assert ( + "image.random_image: Unable to resolve media-source://mymedia/photo: Media Source not loaded" + in caplog.text + ) async def test_image_file_read_error(