Add Python 3.10 to CI (#59729)
This commit is contained in:
parent
7012375bf1
commit
276fd4f42c
35 changed files with 99 additions and 428 deletions
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
|
@ -10,8 +10,8 @@ on:
|
||||||
pull_request: ~
|
pull_request: ~
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_VERSION: 7
|
CACHE_VERSION: 9
|
||||||
PIP_CACHE_VERSION: 1
|
PIP_CACHE_VERSION: 3
|
||||||
HA_SHORT_VERSION: 2022.3
|
HA_SHORT_VERSION: 2022.3
|
||||||
DEFAULT_PYTHON: 3.9
|
DEFAULT_PYTHON: 3.9
|
||||||
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
||||||
|
@ -524,10 +524,10 @@ jobs:
|
||||||
prepare-tests:
|
prepare-tests:
|
||||||
name: Prepare tests for Python ${{ matrix.python-version }}
|
name: Prepare tests for Python ${{ matrix.python-version }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30
|
timeout-minutes: 60
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.9]
|
python-version: ["3.9", "3.10"]
|
||||||
outputs:
|
outputs:
|
||||||
python-key: ${{ steps.generate-python-key.outputs.key }}
|
python-key: ${{ steps.generate-python-key.outputs.key }}
|
||||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||||
|
@ -721,7 +721,7 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
group: ${{ fromJson(needs.changes.outputs.test_groups) }}
|
group: ${{ fromJson(needs.changes.outputs.test_groups) }}
|
||||||
python-version: [3.9]
|
python-version: ["3.9", "3.10"]
|
||||||
name: >-
|
name: >-
|
||||||
Run tests Python ${{ matrix.python-version }} (${{ matrix.group }})
|
Run tests Python ${{ matrix.python-version }} (${{ matrix.group }})
|
||||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||||
|
|
1
.github/workflows/wheels.yml
vendored
1
.github/workflows/wheels.yml
vendored
|
@ -154,6 +154,7 @@ jobs:
|
||||||
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
|
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
|
||||||
sed -i "s|# bme680|bme680|g" ${requirement_file}
|
sed -i "s|# bme680|bme680|g" ${requirement_file}
|
||||||
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
|
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
|
||||||
|
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" ${requirement_file}
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
|
|
|
@ -15,7 +15,8 @@ RUN \
|
||||||
-r homeassistant/requirements.txt --use-deprecated=legacy-resolver
|
-r homeassistant/requirements.txt --use-deprecated=legacy-resolver
|
||||||
COPY requirements_all.txt homeassistant/
|
COPY requirements_all.txt homeassistant/
|
||||||
RUN \
|
RUN \
|
||||||
pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
|
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" homeassistant/requirements_all.txt \
|
||||||
|
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
|
||||||
-r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver
|
-r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver
|
||||||
|
|
||||||
## Setup Home Assistant Core
|
## Setup Home Assistant Core
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for APCUPSd via its Network Information Server (NIS)."""
|
"""Support for APCUPSd via its Network Information Server (NIS)."""
|
||||||
|
# pylint: disable=import-error
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"disabled": "Integration library not compatible with Python 3.10",
|
||||||
"domain": "apcupsd",
|
"domain": "apcupsd",
|
||||||
"name": "apcupsd",
|
"name": "apcupsd",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/apcupsd",
|
"documentation": "https://www.home-assistant.io/integrations/apcupsd",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for APCUPSd sensors."""
|
"""Support for APCUPSd sensors."""
|
||||||
|
# pylint: disable=import-error
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"disabled": "Integration library not compatible with Python 3.10",
|
||||||
"domain": "apns",
|
"domain": "apns",
|
||||||
"name": "Apple Push Notification Service (APNS)",
|
"name": "Apple Push Notification Service (APNS)",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/apns",
|
"documentation": "https://www.home-assistant.io/integrations/apns",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""APNS Notification platform."""
|
"""APNS Notification platform."""
|
||||||
|
# pylint: disable=import-error
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for XBee Zigbee devices."""
|
"""Support for XBee Zigbee devices."""
|
||||||
|
# pylint: disable=import-error
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"disabled": "Integration library not compatible with Python 3.10",
|
||||||
"domain": "xbee",
|
"domain": "xbee",
|
||||||
"name": "XBee",
|
"name": "XBee",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/xbee",
|
"documentation": "https://www.home-assistant.io/integrations/xbee",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for XBee Zigbee sensors."""
|
"""Support for XBee Zigbee sensors."""
|
||||||
|
# pylint: disable=import-error
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for Z-Wave."""
|
"""Support for Z-Wave."""
|
||||||
|
# pylint: disable=import-error
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
@ -355,8 +356,6 @@ async def async_setup_entry( # noqa: C901
|
||||||
from openzwave.group import ZWaveGroup
|
from openzwave.group import ZWaveGroup
|
||||||
from openzwave.network import ZWaveNetwork
|
from openzwave.network import ZWaveNetwork
|
||||||
from openzwave.option import ZWaveOption
|
from openzwave.option import ZWaveOption
|
||||||
|
|
||||||
# pylint: enable=import-error
|
|
||||||
from pydispatch import dispatcher
|
from pydispatch import dispatcher
|
||||||
|
|
||||||
if async_is_ozw_migrated(hass) or async_is_zwave_js_migrated(hass):
|
if async_is_ozw_migrated(hass) or async_is_zwave_js_migrated(hass):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Config flow to configure Z-Wave."""
|
"""Config flow to configure Z-Wave."""
|
||||||
|
# pylint: disable=import-error
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Entity class that represents Z-Wave node."""
|
"""Entity class that represents Z-Wave node."""
|
||||||
|
# pylint: disable=import-error
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,6 @@ enum34==1000000000.0.0
|
||||||
typing==1000000000.0.0
|
typing==1000000000.0.0
|
||||||
uuid==1000000000.0.0
|
uuid==1000000000.0.0
|
||||||
|
|
||||||
# Temporary constraint on pandas, to unblock 2021.7 releases
|
|
||||||
# until we have fixed the wheels builds for newer versions.
|
|
||||||
pandas==1.3.0
|
|
||||||
|
|
||||||
# regex causes segfault with version 2021.8.27
|
# regex causes segfault with version 2021.8.27
|
||||||
# https://bitbucket.org/mrabarnett/mrab-regex/issues/421/2021827-results-in-fatal-python-error
|
# https://bitbucket.org/mrabarnett/mrab-regex/issues/421/2021827-results-in-fatal-python-error
|
||||||
# This is fixed in 2021.8.28
|
# This is fixed in 2021.8.28
|
||||||
|
@ -84,6 +80,10 @@ anyio==3.5.0
|
||||||
h11==0.12.0
|
h11==0.12.0
|
||||||
httpcore==0.14.5
|
httpcore==0.14.5
|
||||||
|
|
||||||
|
# Ensure we have a hyperframe version that works in Python 3.10
|
||||||
|
# 5.2.0 fixed a collections abc deprecation
|
||||||
|
hyperframe>=5.2.0
|
||||||
|
|
||||||
# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead
|
# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead
|
||||||
pytest_asyncio==1000000000.0.0
|
pytest_asyncio==1000000000.0.0
|
||||||
|
|
||||||
|
|
|
@ -319,12 +319,6 @@ anel_pwrctrl-homeassistant==0.0.1.dev2
|
||||||
# homeassistant.components.anthemav
|
# homeassistant.components.anthemav
|
||||||
anthemav==1.2.0
|
anthemav==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.apcupsd
|
|
||||||
apcaccess==0.0.13
|
|
||||||
|
|
||||||
# homeassistant.components.apns
|
|
||||||
apns2==0.3.0
|
|
||||||
|
|
||||||
# homeassistant.components.apprise
|
# homeassistant.components.apprise
|
||||||
apprise==0.9.7
|
apprise==0.9.7
|
||||||
|
|
||||||
|
@ -842,7 +836,7 @@ holidays==0.12
|
||||||
home-assistant-frontend==20220214.0
|
home-assistant-frontend==20220214.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
# homeassistant-pyozw==0.1.10
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.6.3
|
homeconnect==0.6.3
|
||||||
|
@ -2493,9 +2487,6 @@ wled==0.13.0
|
||||||
# homeassistant.components.wolflink
|
# homeassistant.components.wolflink
|
||||||
wolf_smartset==0.1.11
|
wolf_smartset==0.1.11
|
||||||
|
|
||||||
# homeassistant.components.xbee
|
|
||||||
xbee-helper==0.0.7
|
|
||||||
|
|
||||||
# homeassistant.components.xbox
|
# homeassistant.components.xbox
|
||||||
xbox-webapi==2.0.11
|
xbox-webapi==2.0.11
|
||||||
|
|
||||||
|
|
|
@ -239,9 +239,6 @@ ambiclimate==0.2.1
|
||||||
# homeassistant.components.androidtv
|
# homeassistant.components.androidtv
|
||||||
androidtv[async]==0.0.63
|
androidtv[async]==0.0.63
|
||||||
|
|
||||||
# homeassistant.components.apns
|
|
||||||
apns2==0.3.0
|
|
||||||
|
|
||||||
# homeassistant.components.apprise
|
# homeassistant.components.apprise
|
||||||
apprise==0.9.7
|
apprise==0.9.7
|
||||||
|
|
||||||
|
@ -552,7 +549,7 @@ holidays==0.12
|
||||||
home-assistant-frontend==20220214.0
|
home-assistant-frontend==20220214.0
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
# homeassistant-pyozw==0.1.10
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.6.3
|
homeconnect==0.6.3
|
||||||
|
|
|
@ -27,6 +27,7 @@ COMMENT_REQUIREMENTS = (
|
||||||
"envirophat",
|
"envirophat",
|
||||||
"evdev",
|
"evdev",
|
||||||
"face_recognition",
|
"face_recognition",
|
||||||
|
"homeassistant-pyozw",
|
||||||
"i2csense",
|
"i2csense",
|
||||||
"opencv-python-headless",
|
"opencv-python-headless",
|
||||||
"pybluez",
|
"pybluez",
|
||||||
|
@ -94,10 +95,6 @@ enum34==1000000000.0.0
|
||||||
typing==1000000000.0.0
|
typing==1000000000.0.0
|
||||||
uuid==1000000000.0.0
|
uuid==1000000000.0.0
|
||||||
|
|
||||||
# Temporary constraint on pandas, to unblock 2021.7 releases
|
|
||||||
# until we have fixed the wheels builds for newer versions.
|
|
||||||
pandas==1.3.0
|
|
||||||
|
|
||||||
# regex causes segfault with version 2021.8.27
|
# regex causes segfault with version 2021.8.27
|
||||||
# https://bitbucket.org/mrabarnett/mrab-regex/issues/421/2021827-results-in-fatal-python-error
|
# https://bitbucket.org/mrabarnett/mrab-regex/issues/421/2021827-results-in-fatal-python-error
|
||||||
# This is fixed in 2021.8.28
|
# This is fixed in 2021.8.28
|
||||||
|
@ -111,6 +108,10 @@ anyio==3.5.0
|
||||||
h11==0.12.0
|
h11==0.12.0
|
||||||
httpcore==0.14.5
|
httpcore==0.14.5
|
||||||
|
|
||||||
|
# Ensure we have a hyperframe version that works in Python 3.10
|
||||||
|
# 5.2.0 fixed a collections abc deprecation
|
||||||
|
hyperframe>=5.2.0
|
||||||
|
|
||||||
# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead
|
# pytest_asyncio breaks our test suite. We rely on pytest-aiohttp instead
|
||||||
pytest_asyncio==1000000000.0.0
|
pytest_asyncio==1000000000.0.0
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
"""Tests for the apns component."""
|
|
|
@ -1,395 +0,0 @@
|
||||||
"""The tests for the APNS component."""
|
|
||||||
import io
|
|
||||||
from unittest.mock import Mock, mock_open, patch
|
|
||||||
|
|
||||||
from apns2.errors import Unregistered
|
|
||||||
import pytest
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
import homeassistant.components.apns.notify as apns
|
|
||||||
import homeassistant.components.notify as notify
|
|
||||||
from homeassistant.core import State
|
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
|
|
||||||
from tests.common import assert_setup_component
|
|
||||||
|
|
||||||
CONFIG = {
|
|
||||||
notify.DOMAIN: {
|
|
||||||
"platform": "apns",
|
|
||||||
"name": "test_app",
|
|
||||||
"topic": "testapp.appname",
|
|
||||||
"cert_file": "test_app.pem",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", autouse=True)
|
|
||||||
def mock_apns_notify_open():
|
|
||||||
"""Mock builtins.open for apns.notify."""
|
|
||||||
with patch("homeassistant.components.apns.notify.open", mock_open(), create=True):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", Mock(return_value=True))
|
|
||||||
@patch("os.access", Mock(return_value=True))
|
|
||||||
async def _setup_notify(hass_):
|
|
||||||
assert isinstance(apns.load_yaml_config_file, Mock), "Found unmocked load_yaml"
|
|
||||||
|
|
||||||
with assert_setup_component(1) as handle_config:
|
|
||||||
assert await async_setup_component(hass_, notify.DOMAIN, CONFIG)
|
|
||||||
assert handle_config[notify.DOMAIN]
|
|
||||||
|
|
||||||
|
|
||||||
@patch("os.path.isfile", return_value=True)
|
|
||||||
@patch("os.access", return_value=True)
|
|
||||||
async def test_apns_setup_full(mock_access, mock_isfile, hass):
|
|
||||||
"""Test setup with all data."""
|
|
||||||
config = {
|
|
||||||
"notify": {
|
|
||||||
"platform": "apns",
|
|
||||||
"name": "test_app",
|
|
||||||
"sandbox": "True",
|
|
||||||
"topic": "testapp.appname",
|
|
||||||
"cert_file": "test_app.pem",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with assert_setup_component(1) as handle_config:
|
|
||||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
|
||||||
assert handle_config[notify.DOMAIN]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_apns_setup_missing_name(hass):
|
|
||||||
"""Test setup with missing name."""
|
|
||||||
config = {
|
|
||||||
"notify": {
|
|
||||||
"platform": "apns",
|
|
||||||
"topic": "testapp.appname",
|
|
||||||
"cert_file": "test_app.pem",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with assert_setup_component(0) as handle_config:
|
|
||||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
|
||||||
assert not handle_config[notify.DOMAIN]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_apns_setup_missing_certificate(hass):
|
|
||||||
"""Test setup with missing certificate."""
|
|
||||||
config = {
|
|
||||||
"notify": {
|
|
||||||
"platform": "apns",
|
|
||||||
"name": "test_app",
|
|
||||||
"topic": "testapp.appname",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with assert_setup_component(0) as handle_config:
|
|
||||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
|
||||||
assert not handle_config[notify.DOMAIN]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_apns_setup_missing_topic(hass):
|
|
||||||
"""Test setup with missing topic."""
|
|
||||||
config = {
|
|
||||||
"notify": {
|
|
||||||
"platform": "apns",
|
|
||||||
"name": "test_app",
|
|
||||||
"cert_file": "test_app.pem",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with assert_setup_component(0) as handle_config:
|
|
||||||
assert await async_setup_component(hass, notify.DOMAIN, config)
|
|
||||||
assert not handle_config[notify.DOMAIN]
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify._write_device")
|
|
||||||
async def test_register_new_device(mock_write, hass):
|
|
||||||
"""Test registering a new device with a name."""
|
|
||||||
yaml_file = {5678: {"name": "test device 2"}}
|
|
||||||
|
|
||||||
written_devices = []
|
|
||||||
|
|
||||||
def fake_write(_out, device):
|
|
||||||
"""Fake write_device."""
|
|
||||||
written_devices.append(device)
|
|
||||||
|
|
||||||
mock_write.side_effect = fake_write
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
apns.DOMAIN,
|
|
||||||
"apns_test_app",
|
|
||||||
{"push_id": "1234", "name": "test device"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(written_devices) == 1
|
|
||||||
assert written_devices[0].name == "test device"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify._write_device")
|
|
||||||
async def test_register_device_without_name(mock_write, hass):
|
|
||||||
"""Test registering a without a name."""
|
|
||||||
yaml_file = {
|
|
||||||
1234: {"name": "test device 1", "tracking_device_id": "tracking123"},
|
|
||||||
5678: {"name": "test device 2", "tracking_device_id": "tracking456"},
|
|
||||||
}
|
|
||||||
|
|
||||||
written_devices = []
|
|
||||||
|
|
||||||
def fake_write(_out, device):
|
|
||||||
"""Fake write_device."""
|
|
||||||
written_devices.append(device)
|
|
||||||
|
|
||||||
mock_write.side_effect = fake_write
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
apns.DOMAIN, "apns_test_app", {"push_id": "1234"}, blocking=True
|
|
||||||
)
|
|
||||||
|
|
||||||
devices = {dev.push_id: dev for dev in written_devices}
|
|
||||||
|
|
||||||
test_device = devices.get("1234")
|
|
||||||
|
|
||||||
assert test_device is not None
|
|
||||||
assert test_device.name is None
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify._write_device")
|
|
||||||
async def test_update_existing_device(mock_write, hass):
|
|
||||||
"""Test updating an existing device."""
|
|
||||||
yaml_file = {1234: {"name": "test device 1"}, 5678: {"name": "test device 2"}}
|
|
||||||
|
|
||||||
written_devices = []
|
|
||||||
|
|
||||||
def fake_write(_out, device):
|
|
||||||
"""Fake write_device."""
|
|
||||||
written_devices.append(device)
|
|
||||||
|
|
||||||
mock_write.side_effect = fake_write
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
apns.DOMAIN,
|
|
||||||
"apns_test_app",
|
|
||||||
{"push_id": "1234", "name": "updated device 1"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
devices = {dev.push_id: dev for dev in written_devices}
|
|
||||||
|
|
||||||
test_device_1 = devices.get("1234")
|
|
||||||
test_device_2 = devices.get("5678")
|
|
||||||
|
|
||||||
assert test_device_1 is not None
|
|
||||||
assert test_device_2 is not None
|
|
||||||
|
|
||||||
assert test_device_1.name == "updated device 1"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify._write_device")
|
|
||||||
async def test_update_existing_device_with_tracking_id(mock_write, hass):
|
|
||||||
"""Test updating an existing device that has a tracking id."""
|
|
||||||
yaml_file = {
|
|
||||||
1234: {"name": "test device 1", "tracking_device_id": "tracking123"},
|
|
||||||
5678: {"name": "test device 2", "tracking_device_id": "tracking456"},
|
|
||||||
}
|
|
||||||
|
|
||||||
written_devices = []
|
|
||||||
|
|
||||||
def fake_write(_out, device):
|
|
||||||
"""Fake write_device."""
|
|
||||||
written_devices.append(device)
|
|
||||||
|
|
||||||
mock_write.side_effect = fake_write
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
apns.DOMAIN,
|
|
||||||
"apns_test_app",
|
|
||||||
{"push_id": "1234", "name": "updated device 1"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
devices = {dev.push_id: dev for dev in written_devices}
|
|
||||||
|
|
||||||
test_device_1 = devices.get("1234")
|
|
||||||
test_device_2 = devices.get("5678")
|
|
||||||
|
|
||||||
assert test_device_1 is not None
|
|
||||||
assert test_device_2 is not None
|
|
||||||
|
|
||||||
assert test_device_1.tracking_device_id == "tracking123"
|
|
||||||
assert test_device_2.tracking_device_id == "tracking456"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify.APNsClient")
|
|
||||||
async def test_send(mock_client, hass):
|
|
||||||
"""Test updating an existing device."""
|
|
||||||
send = mock_client.return_value.send_notification
|
|
||||||
|
|
||||||
yaml_file = {1234: {"name": "test device 1"}}
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
"notify",
|
|
||||||
"test_app",
|
|
||||||
{
|
|
||||||
"message": "Hello",
|
|
||||||
"data": {"badge": 1, "sound": "test.mp3", "category": "testing"},
|
|
||||||
},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert send.called
|
|
||||||
assert len(send.mock_calls) == 1
|
|
||||||
|
|
||||||
target = send.mock_calls[0][1][0]
|
|
||||||
payload = send.mock_calls[0][1][1]
|
|
||||||
|
|
||||||
assert target == "1234"
|
|
||||||
assert payload.alert == "Hello"
|
|
||||||
assert payload.badge == 1
|
|
||||||
assert payload.sound == "test.mp3"
|
|
||||||
assert payload.category == "testing"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify.APNsClient")
|
|
||||||
async def test_send_when_disabled(mock_client, hass):
|
|
||||||
"""Test updating an existing device."""
|
|
||||||
send = mock_client.return_value.send_notification
|
|
||||||
|
|
||||||
yaml_file = {1234: {"name": "test device 1", "disabled": True}}
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
"notify",
|
|
||||||
"test_app",
|
|
||||||
{
|
|
||||||
"message": "Hello",
|
|
||||||
"data": {"badge": 1, "sound": "test.mp3", "category": "testing"},
|
|
||||||
},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert not send.called
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify.APNsClient")
|
|
||||||
async def test_send_with_state(mock_client, hass):
|
|
||||||
"""Test updating an existing device."""
|
|
||||||
send = mock_client.return_value.send_notification
|
|
||||||
|
|
||||||
yaml_file = {
|
|
||||||
1234: {"name": "test device 1", "tracking_device_id": "tracking123"},
|
|
||||||
5678: {"name": "test device 2", "tracking_device_id": "tracking456"},
|
|
||||||
}
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
), patch("os.path.isfile", Mock(return_value=True)):
|
|
||||||
notify_service = await hass.async_add_executor_job(
|
|
||||||
apns.ApnsNotificationService,
|
|
||||||
hass,
|
|
||||||
"test_app",
|
|
||||||
"testapp.appname",
|
|
||||||
False,
|
|
||||||
"test_app.pem",
|
|
||||||
)
|
|
||||||
|
|
||||||
notify_service.device_state_changed_listener(
|
|
||||||
"device_tracker.tracking456",
|
|
||||||
State("device_tracker.tracking456", None),
|
|
||||||
State("device_tracker.tracking456", "home"),
|
|
||||||
)
|
|
||||||
|
|
||||||
notify_service.send_message(message="Hello", target="home")
|
|
||||||
|
|
||||||
assert send.called
|
|
||||||
assert len(send.mock_calls) == 1
|
|
||||||
|
|
||||||
target = send.mock_calls[0][1][0]
|
|
||||||
payload = send.mock_calls[0][1][1]
|
|
||||||
|
|
||||||
assert target == "5678"
|
|
||||||
assert payload.alert == "Hello"
|
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.apns.notify.APNsClient")
|
|
||||||
@patch("homeassistant.components.apns.notify._write_device")
|
|
||||||
async def test_disable_when_unregistered(mock_write, mock_client, hass):
|
|
||||||
"""Test disabling a device when it is unregistered."""
|
|
||||||
send = mock_client.return_value.send_notification
|
|
||||||
send.side_effect = Unregistered()
|
|
||||||
|
|
||||||
yaml_file = {
|
|
||||||
1234: {"name": "test device 1", "tracking_device_id": "tracking123"},
|
|
||||||
5678: {"name": "test device 2", "tracking_device_id": "tracking456"},
|
|
||||||
}
|
|
||||||
|
|
||||||
written_devices = []
|
|
||||||
|
|
||||||
def fake_write(_out, device):
|
|
||||||
"""Fake write_device."""
|
|
||||||
written_devices.append(device)
|
|
||||||
|
|
||||||
mock_write.side_effect = fake_write
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.apns.notify.load_yaml_config_file",
|
|
||||||
Mock(return_value=yaml_file),
|
|
||||||
):
|
|
||||||
await _setup_notify(hass)
|
|
||||||
|
|
||||||
assert await hass.services.async_call(
|
|
||||||
"notify", "test_app", {"message": "Hello"}, blocking=True
|
|
||||||
)
|
|
||||||
|
|
||||||
devices = {dev.push_id: dev for dev in written_devices}
|
|
||||||
|
|
||||||
test_device_1 = devices.get("1234")
|
|
||||||
assert test_device_1 is not None
|
|
||||||
assert test_device_1.disabled is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_write_device():
|
|
||||||
"""Test writing device."""
|
|
||||||
out = io.StringIO()
|
|
||||||
device = apns.ApnsDevice("123", "name", "track_id", True)
|
|
||||||
|
|
||||||
apns._write_device(out, device)
|
|
||||||
data = yaml.safe_load(out.getvalue())
|
|
||||||
assert data == {
|
|
||||||
123: {"name": "name", "tracking_device_id": "track_id", "disabled": True}
|
|
||||||
}
|
|
|
@ -1,4 +1,11 @@
|
||||||
"""Tests for the iCloud config flow."""
|
"""Tests for the iCloud config flow.
|
||||||
|
|
||||||
|
This integration is temporary disabled, as the library is incompatible
|
||||||
|
with the Python versions we currently support.
|
||||||
|
|
||||||
|
This file has been renamed (instead of skipped), simply because its easier
|
||||||
|
to prevent library imports from happening that way.
|
||||||
|
"""
|
||||||
from unittest.mock import MagicMock, Mock, patch
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
from pyicloud.exceptions import PyiCloudFailedLoginException
|
from pyicloud.exceptions import PyiCloudFailedLoginException
|
|
@ -2,10 +2,15 @@
|
||||||
import datetime
|
import datetime
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.zwave import binary_sensor, const
|
from homeassistant.components.zwave import binary_sensor, const
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(mock_openzwave):
|
def test_get_device_detects_none(mock_openzwave):
|
||||||
"""Test device is not returned."""
|
"""Test device is not returned."""
|
||||||
|
|
|
@ -31,6 +31,9 @@ from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def device(hass, mock_openzwave):
|
def device(hass, mock_openzwave):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Test Z-Wave cover devices."""
|
"""Test Z-Wave cover devices."""
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN
|
from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN
|
||||||
from homeassistant.components.zwave import (
|
from homeassistant.components.zwave import (
|
||||||
CONF_INVERT_OPENCLOSE_BUTTONS,
|
CONF_INVERT_OPENCLOSE_BUTTONS,
|
||||||
|
@ -11,6 +13,9 @@ from homeassistant.components.zwave import (
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(hass, mock_openzwave):
|
def test_get_device_detects_none(hass, mock_openzwave):
|
||||||
"""Test device returns none."""
|
"""Test device returns none."""
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Test Z-Wave fans."""
|
"""Test Z-Wave fans."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
SPEED_HIGH,
|
SPEED_HIGH,
|
||||||
SPEED_LOW,
|
SPEED_LOW,
|
||||||
|
@ -10,6 +12,9 @@ from homeassistant.components.zwave import fan
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_fan(mock_openzwave):
|
def test_get_device_detects_fan(mock_openzwave):
|
||||||
"""Test get_device returns a zwave fan."""
|
"""Test get_device returns a zwave fan."""
|
||||||
|
|
|
@ -22,6 +22,9 @@ from homeassistant.util import dt as dt_util
|
||||||
from tests.common import async_fire_time_changed, mock_registry
|
from tests.common import async_fire_time_changed, mock_registry
|
||||||
from tests.mock.zwave import MockEntityValues, MockNetwork, MockNode, MockValue
|
from tests.mock.zwave import MockEntityValues, MockNetwork, MockNode, MockValue
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_storage(hass_storage):
|
def mock_storage(hass_storage):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Test Z-Wave lights."""
|
"""Test Z-Wave lights."""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import zwave
|
from homeassistant.components import zwave
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
|
@ -18,6 +20,9 @@ from homeassistant.components.zwave import const, light
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
class MockLightValues(MockEntityValues):
|
class MockLightValues(MockEntityValues):
|
||||||
"""Mock Z-Wave light values."""
|
"""Mock Z-Wave light values."""
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
"""Test Z-Wave locks."""
|
"""Test Z-Wave locks."""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.zwave import const, lock
|
from homeassistant.components.zwave import const, lock
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_lock(mock_openzwave):
|
def test_get_device_detects_lock(mock_openzwave):
|
||||||
"""Test get_device returns a Z-Wave lock."""
|
"""Test get_device returns a Z-Wave lock."""
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
"""Test Z-Wave node entity."""
|
"""Test Z-Wave node entity."""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.zwave import const, node_entity
|
from homeassistant.components.zwave import const, node_entity
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
|
|
||||||
import tests.mock.zwave as mock_zwave
|
import tests.mock.zwave as mock_zwave
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
async def test_maybe_schedule_update(hass, mock_openzwave):
|
async def test_maybe_schedule_update(hass, mock_openzwave):
|
||||||
"""Test maybe schedule update."""
|
"""Test maybe schedule update."""
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
"""Test Z-Wave sensor."""
|
"""Test Z-Wave sensor."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorDeviceClass
|
from homeassistant.components.sensor import SensorDeviceClass
|
||||||
from homeassistant.components.zwave import const, sensor
|
from homeassistant.components.zwave import const, sensor
|
||||||
import homeassistant.const
|
import homeassistant.const
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_none(mock_openzwave):
|
def test_get_device_detects_none(mock_openzwave):
|
||||||
"""Test get_device returns None."""
|
"""Test get_device returns None."""
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
"""Test Z-Wave switches."""
|
"""Test Z-Wave switches."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.zwave import switch
|
from homeassistant.components.zwave import switch
|
||||||
|
|
||||||
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
from tests.mock.zwave import MockEntityValues, MockNode, MockValue, value_changed
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_detects_switch(mock_openzwave):
|
def test_get_device_detects_switch(mock_openzwave):
|
||||||
"""Test get_device returns a Z-Wave switch."""
|
"""Test get_device returns a Z-Wave switch."""
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Test Z-Wave Websocket API."""
|
"""Test Z-Wave Websocket API."""
|
||||||
from unittest.mock import call, patch
|
from unittest.mock import call, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.bootstrap import async_setup_component
|
from homeassistant.bootstrap import async_setup_component
|
||||||
from homeassistant.components.zwave.const import (
|
from homeassistant.components.zwave.const import (
|
||||||
|
@ -14,6 +16,10 @@ from homeassistant.components.zwave.websocket_api import ID, TYPE
|
||||||
NETWORK_KEY = "0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST"
|
NETWORK_KEY = "0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST, 0xTE, 0xST"
|
||||||
|
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
async def test_zwave_ws_api(hass, mock_openzwave, hass_ws_client):
|
async def test_zwave_ws_api(hass, mock_openzwave, hass_ws_client):
|
||||||
"""Test Z-Wave websocket API."""
|
"""Test Z-Wave websocket API."""
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
"""Test Z-Wave workarounds."""
|
"""Test Z-Wave workarounds."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.zwave import const, workaround
|
from homeassistant.components.zwave import const, workaround
|
||||||
|
|
||||||
from tests.mock.zwave import MockNode, MockValue
|
from tests.mock.zwave import MockNode, MockValue
|
||||||
|
|
||||||
|
# Integration is disabled
|
||||||
|
pytest.skip("Integration has been disabled in the manifest", allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_device_no_component_mapping():
|
def test_get_device_no_component_mapping():
|
||||||
"""Test that None is returned."""
|
"""Test that None is returned."""
|
||||||
|
|
|
@ -317,6 +317,7 @@ async def test_migrate_zwave(
|
||||||
assert not await hass.config_entries.async_setup(zwave_config_entry.entry_id)
|
assert not await hass.config_entries.async_setup(zwave_config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="The old zwave integration has been disabled.")
|
||||||
async def test_migrate_zwave_dry_run(
|
async def test_migrate_zwave_dry_run(
|
||||||
hass,
|
hass,
|
||||||
zwave_integration,
|
zwave_integration,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"""Mock helpers for Z-Wave component."""
|
"""Mock helpers for Z-Wave component."""
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from pydispatch import dispatcher
|
# Integration & integration tests are disabled
|
||||||
|
# from pydispatch import dispatcher
|
||||||
|
dispatcher = MagicMock()
|
||||||
|
|
||||||
|
|
||||||
def value_changed(value):
|
def value_changed(value):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue