"""The tests for Home Assistant frontend.""" import asyncio import re from unittest.mock import patch import pytest from homeassistant.setup import async_setup_component from homeassistant.components.frontend import ( DOMAIN, ATTR_THEMES, ATTR_EXTRA_HTML_URL, DATA_PANELS, register_panel) @pytest.fixture def mock_http_client(hass, test_client): """Start the Hass HTTP component.""" hass.loop.run_until_complete(async_setup_component(hass, 'frontend', {})) return hass.loop.run_until_complete(test_client(hass.http.app)) @pytest.fixture def mock_http_client_with_themes(hass, test_client): """Start the Hass HTTP component.""" hass.loop.run_until_complete(async_setup_component(hass, 'frontend', { DOMAIN: { ATTR_THEMES: { 'happy': { 'primary-color': 'red' } } }})) return hass.loop.run_until_complete(test_client(hass.http.app)) @pytest.fixture def mock_http_client_with_urls(hass, test_client): """Start the Hass HTTP component.""" hass.loop.run_until_complete(async_setup_component(hass, 'frontend', { DOMAIN: { ATTR_EXTRA_HTML_URL: ["https://domain.com/my_extra_url.html"] }})) return hass.loop.run_until_complete(test_client(hass.http.app)) @asyncio.coroutine def test_frontend_and_static(mock_http_client): """Test if we can get the frontend.""" resp = yield from mock_http_client.get('') assert resp.status == 200 assert 'cache-control' not in resp.headers text = yield from resp.text() # Test we can retrieve frontend.js frontendjs = re.search( r'(?P\/static\/frontend-[A-Za-z0-9]{32}.html)', text) assert frontendjs is not None resp = yield from mock_http_client.get(frontendjs.groups(0)[0]) assert resp.status == 200 assert 'public' in resp.headers.get('cache-control') @asyncio.coroutine def test_dont_cache_service_worker(mock_http_client): """Test that we don't cache the service worker.""" resp = yield from mock_http_client.get('/service_worker.js') assert resp.status == 200 assert 'cache-control' not in resp.headers @asyncio.coroutine def test_404(mock_http_client): """Test for HTTP 404 error.""" resp = yield from mock_http_client.get('/not-existing') assert resp.status == 404 @asyncio.coroutine def test_we_cannot_POST_to_root(mock_http_client): """Test that POST is not allow to root.""" resp = yield from mock_http_client.post('/') assert resp.status == 405 @asyncio.coroutine def test_states_routes(mock_http_client): """All served by index.""" resp = yield from mock_http_client.get('/states') assert resp.status == 200 resp = yield from mock_http_client.get('/states/group.existing') assert resp.status == 200 @asyncio.coroutine def test_themes_api(mock_http_client_with_themes): """Test that /api/themes returns correct data.""" resp = yield from mock_http_client_with_themes.get('/api/themes') json = yield from resp.json() assert json['default_theme'] == 'default' assert json['themes'] == {'happy': {'primary-color': 'red'}} @asyncio.coroutine def test_themes_set_theme(hass, mock_http_client_with_themes): """Test frontend.set_theme service.""" yield from hass.services.async_call(DOMAIN, 'set_theme', {'name': 'happy'}) yield from hass.async_block_till_done() resp = yield from mock_http_client_with_themes.get('/api/themes') json = yield from resp.json() assert json['default_theme'] == 'happy' yield from hass.services.async_call( DOMAIN, 'set_theme', {'name': 'default'}) yield from hass.async_block_till_done() resp = yield from mock_http_client_with_themes.get('/api/themes') json = yield from resp.json() assert json['default_theme'] == 'default' @asyncio.coroutine def test_themes_set_theme_wrong_name(hass, mock_http_client_with_themes): """Test frontend.set_theme service called with wrong name.""" yield from hass.services.async_call(DOMAIN, 'set_theme', {'name': 'wrong'}) yield from hass.async_block_till_done() resp = yield from mock_http_client_with_themes.get('/api/themes') json = yield from resp.json() assert json['default_theme'] == 'default' @asyncio.coroutine def test_themes_reload_themes(hass, mock_http_client_with_themes): """Test frontend.reload_themes service.""" with patch('homeassistant.components.frontend.load_yaml_config_file', return_value={DOMAIN: { ATTR_THEMES: { 'sad': {'primary-color': 'blue'} }}}): yield from hass.services.async_call(DOMAIN, 'set_theme', {'name': 'happy'}) yield from hass.services.async_call(DOMAIN, 'reload_themes') yield from hass.async_block_till_done() resp = yield from mock_http_client_with_themes.get('/api/themes') json = yield from resp.json() assert json['themes'] == {'sad': {'primary-color': 'blue'}} assert json['default_theme'] == 'default' @asyncio.coroutine def test_missing_themes(mock_http_client): """Test that themes API works when themes are not defined.""" resp = yield from mock_http_client.get('/api/themes') assert resp.status == 200 json = yield from resp.json() assert json['default_theme'] == 'default' assert json['themes'] == {} @asyncio.coroutine def test_extra_urls(mock_http_client_with_urls): """Test that extra urls are loaded.""" resp = yield from mock_http_client_with_urls.get('/states') assert resp.status == 200 text = yield from resp.text() assert text.find('href=\'https://domain.com/my_extra_url.html\'') >= 0 @asyncio.coroutine def test_panel_without_path(hass): """Test panel registration without file path.""" register_panel(hass, 'test_component', 'nonexistant_file') assert hass.data[DATA_PANELS] == {} @asyncio.coroutine def test_panel_with_url(hass): """Test panel registration without file path.""" register_panel(hass, 'test_component', None, url='some_url') assert hass.data[DATA_PANELS] == { 'test_component': {'component_name': 'test_component', 'url': 'some_url', 'url_path': 'test_component'}}