mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Allow adding items Picnic shopping cart by searching (#102862)
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
parent
adc56b6b67
commit
62473936e2
@ -66,7 +66,7 @@ async def handle_add_product(
|
||||
product_id = call.data.get("product_id")
|
||||
if not product_id:
|
||||
product_id = await hass.async_add_executor_job(
|
||||
_product_search, api_client, cast(str, call.data["product_name"])
|
||||
product_search, api_client, cast(str, call.data["product_name"])
|
||||
)
|
||||
|
||||
if not product_id:
|
||||
@ -77,7 +77,7 @@ async def handle_add_product(
|
||||
)
|
||||
|
||||
|
||||
def _product_search(api_client: PicnicAPI, product_name: str) -> None | str:
|
||||
def product_search(api_client: PicnicAPI, product_name: str) -> None | str:
|
||||
"""Query the api client for the product name."""
|
||||
search_result = api_client.search(product_name)
|
||||
|
||||
|
@ -4,7 +4,12 @@ from __future__ import annotations
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
|
||||
from homeassistant.components.todo import TodoItem, TodoItemStatus, TodoListEntity
|
||||
from homeassistant.components.todo import (
|
||||
TodoItem,
|
||||
TodoItemStatus,
|
||||
TodoListEntity,
|
||||
TodoListEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@ -15,6 +20,7 @@ from homeassistant.helpers.update_coordinator import (
|
||||
)
|
||||
|
||||
from .const import CONF_COORDINATOR, DOMAIN
|
||||
from .services import product_search
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -27,19 +33,20 @@ async def async_setup_entry(
|
||||
"""Set up the Picnic shopping cart todo platform config entry."""
|
||||
picnic_coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
|
||||
|
||||
# Add an entity shopping card
|
||||
async_add_entities([PicnicCart(picnic_coordinator, config_entry)])
|
||||
async_add_entities([PicnicCart(hass, picnic_coordinator, config_entry)])
|
||||
|
||||
|
||||
class PicnicCart(TodoListEntity, CoordinatorEntity):
|
||||
"""A Picnic Shopping Cart TodoListEntity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "shopping_cart"
|
||||
_attr_icon = "mdi:cart"
|
||||
_attr_supported_features = TodoListEntityFeature.CREATE_TODO_ITEM
|
||||
_attr_translation_key = "shopping_cart"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
coordinator: DataUpdateCoordinator[Any],
|
||||
config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
@ -51,6 +58,7 @@ class PicnicCart(TodoListEntity, CoordinatorEntity):
|
||||
manufacturer="Picnic",
|
||||
model=config_entry.unique_id,
|
||||
)
|
||||
self.hass = hass
|
||||
self._attr_unique_id = f"{config_entry.unique_id}-cart"
|
||||
|
||||
@property
|
||||
@ -73,3 +81,18 @@ class PicnicCart(TodoListEntity, CoordinatorEntity):
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
async def async_create_todo_item(self, item: TodoItem) -> None:
|
||||
"""Add item to shopping cart."""
|
||||
product_id = await self.hass.async_add_executor_job(
|
||||
product_search, self.coordinator.picnic_api_client, item.summary
|
||||
)
|
||||
|
||||
if not product_id:
|
||||
raise ValueError("No product found or no product ID given")
|
||||
|
||||
await self.hass.async_add_executor_job(
|
||||
self.coordinator.picnic_api_client.add_product, product_id, 1
|
||||
)
|
||||
|
||||
await self.coordinator.async_refresh()
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Conftest for Picnic tests."""
|
||||
from collections.abc import Awaitable, Callable
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@ -9,6 +10,9 @@ from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
ENTITY_ID = "todo.mock_title_shopping_cart"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -50,3 +54,42 @@ async def init_integration(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_config_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws_req_id() -> Callable[[], int]:
|
||||
"""Fixture for incremental websocket requests."""
|
||||
|
||||
id = 0
|
||||
|
||||
def next_id() -> int:
|
||||
nonlocal id
|
||||
id += 1
|
||||
return id
|
||||
|
||||
return next_id
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def get_items(
|
||||
hass_ws_client: WebSocketGenerator, ws_req_id: Callable[[], int]
|
||||
) -> Callable[[], Awaitable[dict[str, str]]]:
|
||||
"""Fixture to fetch items from the todo websocket."""
|
||||
|
||||
async def get() -> list[dict[str, str]]:
|
||||
# Fetch items using To-do platform
|
||||
client = await hass_ws_client()
|
||||
id = ws_req_id()
|
||||
await client.send_json(
|
||||
{
|
||||
"id": id,
|
||||
"type": "todo/item/list",
|
||||
"entity_id": ENTITY_ID,
|
||||
}
|
||||
)
|
||||
resp = await client.receive_json()
|
||||
assert resp.get("id") == id
|
||||
assert resp.get("success")
|
||||
return resp.get("result", {}).get("items", [])
|
||||
|
||||
return get
|
||||
|
55
tests/components/picnic/snapshots/test_todo.ambr
Normal file
55
tests/components/picnic/snapshots/test_todo.ambr
Normal file
@ -0,0 +1,55 @@
|
||||
# serializer version: 1
|
||||
# name: test_cart_list_with_items
|
||||
list([
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Knoflook (2 stuks)',
|
||||
'uid': '763-s1001194',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Picnic magere melk (2 x 1 liter)',
|
||||
'uid': '765_766-s1046297',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Picnic magere melk (1 liter)',
|
||||
'uid': '767-s1010532',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Robijn wascapsules wit (40 wasbeurten)',
|
||||
'uid': '774_775-s1018253',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Robijn wascapsules kleur (15 wasbeurten)',
|
||||
'uid': '774_775-s1007025',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Chinese wokgroenten (600 gram)',
|
||||
'uid': '776_777_778_779_780-s1012699',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Picnic boerderij-eitjes (6 stuks M/L)',
|
||||
'uid': '776_777_778_779_780-s1003425',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Picnic witte snelkookrijst (400 gram)',
|
||||
'uid': '776_777_778_779_780-s1016692',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Conimex kruidenmix nasi (20 gram)',
|
||||
'uid': '776_777_778_779_780-s1012503',
|
||||
}),
|
||||
dict({
|
||||
'status': 'needs_action',
|
||||
'summary': 'Conimex satésaus mild kant & klaar (400 gram)',
|
||||
'uid': '776_777_778_779_780-s1005028',
|
||||
}),
|
||||
])
|
||||
# ---
|
@ -1,18 +1,31 @@
|
||||
"""Tests for Picnic Tasks todo platform."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, Mock
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.todo import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import ENTITY_ID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_cart_list_with_items(hass: HomeAssistant, init_integration) -> None:
|
||||
async def test_cart_list_with_items(
|
||||
hass: HomeAssistant,
|
||||
init_integration,
|
||||
get_items,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test loading of shopping cart."""
|
||||
state = hass.states.get("todo.mock_title_shopping_cart")
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state
|
||||
assert state.state == "10"
|
||||
|
||||
assert snapshot == await get_items()
|
||||
|
||||
|
||||
async def test_cart_list_empty_items(
|
||||
hass: HomeAssistant, mock_picnic_api: MagicMock, mock_config_entry: MockConfigEntry
|
||||
@ -23,7 +36,7 @@ async def test_cart_list_empty_items(
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("todo.mock_title_shopping_cart")
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state
|
||||
assert state.state == "0"
|
||||
|
||||
@ -37,7 +50,7 @@ async def test_cart_list_unexpected_response(
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("todo.mock_title_shopping_cart")
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is None
|
||||
|
||||
|
||||
@ -50,5 +63,63 @@ async def test_cart_list_null_response(
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("todo.mock_title_shopping_cart")
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is None
|
||||
|
||||
|
||||
async def test_create_todo_list_item(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry, mock_picnic_api: MagicMock
|
||||
) -> None:
|
||||
"""Test for creating a picnic cart item."""
|
||||
assert len(mock_picnic_api.get_cart.mock_calls) == 1
|
||||
|
||||
mock_picnic_api.search = Mock()
|
||||
mock_picnic_api.search.return_value = [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": 321,
|
||||
"name": "Picnic Melk",
|
||||
"unit_quantity": "2 liter",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
mock_picnic_api.add_product = Mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"add_item",
|
||||
{"item": "Melk"},
|
||||
target={"entity_id": ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
args = mock_picnic_api.search.call_args
|
||||
assert args
|
||||
assert args[0][0] == "Melk"
|
||||
|
||||
args = mock_picnic_api.add_product.call_args
|
||||
assert args
|
||||
assert args[0][0] == "321"
|
||||
assert args[0][1] == 1
|
||||
|
||||
assert len(mock_picnic_api.get_cart.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_create_todo_list_item_not_found(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry, mock_picnic_api: MagicMock
|
||||
) -> None:
|
||||
"""Test for creating a picnic cart item when ID is not found."""
|
||||
mock_picnic_api.search = Mock()
|
||||
mock_picnic_api.search.return_value = [{"items": []}]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"add_item",
|
||||
{"item": "Melk"},
|
||||
target={"entity_id": ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user