ha-testcontainer 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lint Free Technology
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,432 @@
1
+ Metadata-Version: 2.4
2
+ Name: ha-testcontainer
3
+ Version: 1.0.1
4
+ Summary: Full test container for Home Assistant with demo config, custom components, and visual testing
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 Lint Free Technology
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/Lint-Free-Technology/ha-testcontainer
28
+ Project-URL: Issues, https://github.com/Lint-Free-Technology/ha-testcontainer/issues
29
+ Keywords: home-assistant,testcontainers,testing,docker,playwright
30
+ Classifier: Development Status :: 3 - Alpha
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Topic :: Software Development :: Testing
37
+ Requires-Python: >=3.11
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: testcontainers>=4.9
41
+ Requires-Dist: requests>=2.32
42
+ Requires-Dist: websocket-client>=1.6
43
+ Provides-Extra: visual
44
+ Requires-Dist: playwright>=1.50; extra == "visual"
45
+ Requires-Dist: pytest-playwright>=0.5; extra == "visual"
46
+ Requires-Dist: Pillow>=10.0; extra == "visual"
47
+ Provides-Extra: test
48
+ Requires-Dist: pytest>=8.0; extra == "test"
49
+ Requires-Dist: pytest-playwright>=0.5; extra == "test"
50
+ Requires-Dist: playwright>=1.50; extra == "test"
51
+ Requires-Dist: Pillow>=10.0; extra == "test"
52
+ Dynamic: license-file
53
+
54
+ # ha-testcontainer
55
+
56
+ > A full, reusable test container for Home Assistant.
57
+
58
+ `ha-testcontainer` is a Python library that wraps the official Home Assistant
59
+ Docker image in a [Testcontainers](https://testcontainers.com/)-based class.
60
+ It handles all the plumbing — startup, programmatic onboarding, long-lived
61
+ token creation, and custom component mounting — so your tests can focus on
62
+ what matters.
63
+
64
+ [UIX (UI eXtension)](https://github.com/Lint-Free-Technology/uix) is the
65
+ primary consumer and uses this library as a replacement for its legacy local
66
+ test stack.
67
+
68
+ ---
69
+
70
+ ## Features
71
+
72
+ | Capability | Detail |
73
+ |---|---|
74
+ | **Version flexibility** | `stable`, `beta`, `dev`, or any pinned tag (`2024.6.0`) |
75
+ | **Automatic onboarding** | Creates an admin user and mints a long-lived API token with no manual interaction |
76
+ | **Demo entities** | Built-in HA `demo` integration gives you lights, sensors, weather objects immediately |
77
+ | **Custom config** | Mount any `configuration.yaml` tree via `config_path=` |
78
+ | **Custom components** | Mount any `custom_components/` directory via `custom_components_path=` |
79
+ | **Fetch any component** | `scripts/fetch_component.py owner/repo` — downloads any GitHub-hosted HA component |
80
+ | **Fetch any frontend plugin** | `scripts/fetch_plugin.py owner/repo` — downloads any JS dashboard plugin; registers it as a Lovelace resource |
81
+ | **Storage-mode dashboard** | Default Lovelace dashboard in storage mode; REST-pushable for tests |
82
+ | **REST API helper** | `ha.api("GET", "states")` — authenticated calls with zero boilerplate |
83
+ | **Integration setup** | `ha.setup_integration("my_domain")` — drives config-flow programmatically |
84
+ | **Playwright visual tests** | Session-scoped browser context, token injection, pixel-diff snapshot comparison |
85
+ | **Boilerplate example** | `examples/test_custom_component.py` — copy, fill in TODO values, done |
86
+
87
+ ---
88
+
89
+ ## Quick start
90
+
91
+ ### 1 — Install
92
+
93
+ ```bash
94
+ pip install -e ".[test]"
95
+ playwright install chromium
96
+ ```
97
+
98
+ ### 2 — Fetch a custom component
99
+
100
+ ```bash
101
+ # Any GitHub-hosted HA custom component (Python, goes into custom_components/):
102
+ python scripts/fetch_component.py Lint-Free-Technology/uix
103
+ python scripts/fetch_component.py Lint-Free-Technology/uix 5.3.1 # pinned version
104
+
105
+ # Or via make (COMPONENT is required):
106
+ make setup COMPONENT=Lint-Free-Technology/uix
107
+ make setup COMPONENT=Lint-Free-Technology/uix VERSION=5.3.1
108
+ ```
109
+
110
+ ### 2b — Fetch a frontend plugin (optional)
111
+
112
+ Frontend plugins are JavaScript dashboard modules (Lovelace cards, etc.),
113
+ distinct from Python custom components. They are downloaded into
114
+ `ha-config/www/dashboard/` and automatically served by HA at `/local/…`.
115
+
116
+ ```bash
117
+ # Download and register a dashboard plugin:
118
+ python scripts/fetch_plugin.py custom-cards/button-card
119
+ python scripts/fetch_plugin.py thomasloven/lovelace-card-mod 3.4.4
120
+
121
+ # Or via make:
122
+ make fetch-plugin PLUGIN=custom-cards/button-card
123
+ make fetch-plugin PLUGIN=thomasloven/lovelace-card-mod VERSION=3.4.4
124
+ ```
125
+
126
+ ### 3 — Run tests
127
+
128
+ ```bash
129
+ make test # unit + integration tests (no browser)
130
+ make test-visual # Playwright visual tests (requires Docker + Playwright)
131
+ ```
132
+
133
+ See **[Testing](#testing)** below for a full breakdown of each tier.
134
+
135
+ ### 4 — Explore locally with docker compose
136
+
137
+ ```bash
138
+ make up # starts HA at http://localhost:8123
139
+ make down
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Usage in your own project
145
+
146
+ ```python
147
+ from ha_testcontainer import HATestContainer, HAVersion
148
+
149
+ with HATestContainer(
150
+ version=HAVersion.STABLE, # or "beta", "dev", "2024.6.0"
151
+ config_path="ha-config",
152
+ custom_components_path="custom_components",
153
+ ) as ha:
154
+ # Set up any custom component via config-flow
155
+ ha.setup_integration("uix")
156
+
157
+ # REST API — authenticated, zero boilerplate
158
+ states = ha.api("GET", "states").json()
159
+
160
+ print(ha.get_url()) # http://localhost:<random-port>
161
+ print(ha.get_token()) # long-lived access token
162
+ ```
163
+
164
+ ### pytest fixture example
165
+
166
+ ```python
167
+ # conftest.py
168
+ import pytest
169
+ from ha_testcontainer import HATestContainer
170
+
171
+ @pytest.fixture(scope="session")
172
+ def ha():
173
+ with HATestContainer(config_path="ha-config", custom_components_path="custom_components") as c:
174
+ c.setup_integration("uix")
175
+ yield c
176
+
177
+ # test_my_component.py
178
+ def test_api(ha):
179
+ resp = ha.api("GET", "states")
180
+ assert resp.status_code == 200
181
+ ```
182
+
183
+ ### Playwright visual test example
184
+
185
+ See [`examples/test_custom_component.py`](examples/test_custom_component.py) for a
186
+ fully annotated boilerplate that any component author can copy.
187
+
188
+ ```python
189
+ from ha_testcontainer.visual import PAGE_LOAD_TIMEOUT, assert_snapshot
190
+
191
+ def test_my_card(ha_page, ha_url):
192
+ ha_page.goto(f"{ha_url}/lovelace/0", wait_until="networkidle",
193
+ timeout=PAGE_LOAD_TIMEOUT)
194
+ assert_snapshot(ha_page, "my_card_baseline")
195
+ ```
196
+
197
+ Run `SNAPSHOT_UPDATE=1 pytest tests/visual/` to create or update baselines.
198
+
199
+ ---
200
+
201
+ ## Fetching custom components
202
+
203
+ `scripts/fetch_component.py` works with **any** GitHub repository that follows
204
+ the standard HA custom-component layout (a `custom_components/<name>/` directory
205
+ in the repository root):
206
+
207
+ ```
208
+ python scripts/fetch_component.py owner/repo # latest release
209
+ python scripts/fetch_component.py owner/repo 5.3.1 # specific version
210
+ python scripts/fetch_component.py owner/repo --list # list releases
211
+ python scripts/fetch_component.py owner/repo --target-dir /other/path
212
+ ```
213
+
214
+ The script auto-discovers all `custom_components/` sub-directories in the
215
+ release archive, so multi-component repositories are handled correctly.
216
+
217
+ ---
218
+
219
+ ## Fetching frontend plugins (dashboard cards)
220
+
221
+ `scripts/fetch_plugin.py` downloads **JavaScript dashboard modules** — Lovelace
222
+ cards and similar frontend-only plugins — from GitHub releases. These are
223
+ distinct from Python custom components: they live in `www/` rather than
224
+ `custom_components/`, and are loaded by the HA frontend via Lovelace resources.
225
+
226
+ ```
227
+ python scripts/fetch_plugin.py owner/repo # latest release
228
+ python scripts/fetch_plugin.py owner/repo 1.2.3 # specific version
229
+ python scripts/fetch_plugin.py owner/repo --list # list releases
230
+ python scripts/fetch_plugin.py owner/repo --plugin-name custom-name
231
+ python scripts/fetch_plugin.py owner/repo --resource-type js # legacy (default: module)
232
+ ```
233
+
234
+ The script:
235
+ 1. Downloads JS files from the release (assets, zip assets, or source archive fallback).
236
+ 2. Places them at `ha-config/www/dashboard/<plugin-name>/<file>.js`.
237
+ 3. Registers each file as a Lovelace resource in `ha-config/lovelace_resources.yaml`.
238
+
239
+ HA serves `ha-config/www/` at `/local/`, so the plugin is immediately available
240
+ to Lovelace dashboards as `/local/dashboard/<plugin-name>/<file>.js`.
241
+
242
+ ---
243
+
244
+ ## Configuration
245
+
246
+ ### HA version
247
+
248
+ | Value | Image tag pulled |
249
+ |---|---|
250
+ | `HAVersion.STABLE` (default) | `ghcr.io/home-assistant/home-assistant:stable` |
251
+ | `HAVersion.BETA` | `ghcr.io/home-assistant/home-assistant:beta` |
252
+ | `HAVersion.DEV` | `ghcr.io/home-assistant/home-assistant:dev` |
253
+ | `"2024.6.0"` | `ghcr.io/home-assistant/home-assistant:2024.6.0` |
254
+
255
+ Override at runtime:
256
+
257
+ ```bash
258
+ HA_VERSION=beta pytest tests/
259
+ HA_VERSION=2024.6.0 make test
260
+ ```
261
+
262
+ ### Config & custom components
263
+
264
+ | Environment variable | Default | Purpose |
265
+ |---|---|---|
266
+ | `HA_VERSION` | `stable` | Image tag |
267
+ | `HA_CONFIG_PATH` | `ha-config/` | Host dir mounted as `/config` |
268
+ | `HA_CUSTOM_COMPONENTS_PATH` | `custom_components/` | Host dir mounted as `/config/custom_components` |
269
+
270
+ ---
271
+
272
+ ## Repository layout
273
+
274
+ ```
275
+ ha-testcontainer/
276
+ ├── ha_testcontainer/
277
+ │ ├── __init__.py # public API: HATestContainer, HAVersion, visual helpers
278
+ │ ├── container.py # HATestContainer implementation
279
+ │ └── visual.py # PAGE_LOAD_TIMEOUT, assert_snapshot, inject_ha_token
280
+ ├── ha-config/
281
+ │ ├── configuration.yaml # demo HA config (default_config + demo integration)
282
+ │ ├── lovelace_resources.yaml # Lovelace resources list (managed by fetch_plugin.py)
283
+ │ ├── www/ # served at /local/ by HA; plugins downloaded here
284
+ │ └── themes/ # theme YAML files (auto-loaded)
285
+ ├── custom_components/
286
+ │ └── README.md # populated by scripts/fetch_component.py (gitignored)
287
+ ├── scripts/
288
+ │ ├── fetch_component.py # download any HA Python component from GitHub releases
289
+ │ └── fetch_plugin.py # download any JS frontend plugin; register as resource
290
+ ├── examples/
291
+ │ └── test_custom_component.py # boilerplate visual test — copy & customise
292
+ ├── tests/
293
+ │ ├── conftest.py # session-scoped ha / ha_url / ha_token fixtures
294
+ │ ├── test_container_unit.py # unit tests — no Docker needed (14 tests)
295
+ │ ├── test_container.py # integration tests — requires Docker (10 tests)
296
+ │ └── visual/
297
+ │ ├── conftest.py # Playwright fixtures (ha_page, ha_browser_context)
298
+ │ └── snapshots/ # gitignored — no baselines committed here (see below)
299
+ ├── docker-compose.yml # local dev: docker compose up
300
+ ├── Makefile # setup / test / update-snapshots targets
301
+ └── pyproject.toml
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Snapshot-based visual testing
307
+
308
+ Snapshot tests follow a two-file convention:
309
+
310
+ | File | Description |
311
+ |---|---|
312
+ | `snapshots/<name>.png` | **Committed baseline** — the ground truth, lives in the **consumer's repo** |
313
+ | `snapshots/<name>.actual.png` | Generated on every run — gitignored |
314
+
315
+ On the **first run** (or when `SNAPSHOT_UPDATE=1` is set), the actual
316
+ screenshot becomes the baseline. Subsequent runs diff the two; any pixel
317
+ difference causes the test to fail.
318
+
319
+ Baselines are placed **next to the calling test file** automatically — no
320
+ configuration needed.
321
+
322
+ > **Important — baselines are stored in the consumer's repo, not here.**
323
+ > ha-testcontainer is a reusable library; it does not commit any snapshot PNG
324
+ > files. All `*.png` files under `tests/visual/snapshots/` are gitignored in
325
+ > this repository. When you write visual tests for your own component, baselines
326
+ > are committed in *your* project's `tests/visual/snapshots/` directory.
327
+ > They will never appear in ha-testcontainer's history.
328
+
329
+ ---
330
+
331
+ ## Testing
332
+
333
+ The test suite has three tiers:
334
+
335
+ ### Tier 1 — Unit tests (no Docker required)
336
+
337
+ These run in milliseconds and cover the Python logic of `HATestContainer`
338
+ in isolation (URL construction, token handling, API path normalisation):
339
+
340
+ ```bash
341
+ pip install -e ".[test]" # install once
342
+ pytest tests/test_container_unit.py -v
343
+ ```
344
+
345
+ Or via make:
346
+
347
+ ```bash
348
+ make install
349
+ make test
350
+ ```
351
+
352
+ `make test` runs **all** non-browser tests, including both unit and
353
+ integration tests, skipping the visual tier.
354
+
355
+ ### Tier 2 — Integration tests (Docker required)
356
+
357
+ These start a real Home Assistant container and exercise the full lifecycle
358
+ (onboarding, REST API, demo entities, Lovelace push):
359
+
360
+ ```bash
361
+ # Prerequisites: Docker daemon running, HA image available
362
+ pytest tests/test_container.py -v
363
+ ```
364
+
365
+ The container is started once per pytest session (session-scoped fixture)
366
+ and reused across all tests in the file. Startup takes ~60 s the first time
367
+ while HA initialises.
368
+
369
+ Environment variables let you control which image and config directory are
370
+ used:
371
+
372
+ | Variable | Default | Purpose |
373
+ |---|---|---|
374
+ | `HA_VERSION` | `stable` | Image tag (`stable`, `beta`, `dev`, `2024.6.0`, …) |
375
+ | `HA_CONFIG_PATH` | `ha-config/` | Host dir mounted as `/config` |
376
+ | `HA_CUSTOM_COMPONENTS_PATH` | `custom_components/` | Host dir mounted as `/config/custom_components` |
377
+
378
+ ```bash
379
+ HA_VERSION=beta pytest tests/test_container.py -v
380
+ ```
381
+
382
+ ### Tier 3 — Visual (Playwright) tests (Docker + Playwright required)
383
+
384
+ ```bash
385
+ pip install -e ".[test]"
386
+ playwright install chromium
387
+ pytest tests/visual/ -v
388
+ ```
389
+
390
+ Or via make:
391
+
392
+ ```bash
393
+ make install
394
+ make test-visual
395
+ ```
396
+
397
+ Visual tests open a Chromium browser, log in to the running HA instance, and
398
+ compare screenshots against committed baselines. Baselines are stored in the
399
+ **consumer's own repository** — see [Snapshot-based visual testing](#snapshot-based-visual-testing).
400
+
401
+ ### Version smoke tests (slow, optional)
402
+
403
+ These pull the `stable`, `beta`, and `dev` images in sequence and verify that
404
+ the container starts for each:
405
+
406
+ ```bash
407
+ pytest tests/test_container.py -v -m version_smoke
408
+ # or:
409
+ make test-smoke
410
+ ```
411
+
412
+ They are skipped by default to avoid pulling large images on every run.
413
+
414
+ ---
415
+
416
+
417
+
418
+ UIX's `test/docker-compose.yaml` + `test/configuration.yaml` + `test/lovelace.yaml`
419
+ are replaced by `make up` and UIX writing its own `tests/` using `HATestContainer`.
420
+
421
+ 1. Add `ha-testcontainer` as a dev dependency.
422
+ 2. Fetch UIX: `python scripts/fetch_component.py Lint-Free-Technology/uix`.
423
+ 3. Replace `docker compose -f test/docker-compose.yaml up` with `make up`.
424
+ 4. Copy `examples/test_custom_component.py` into UIX's `tests/visual/`, fill in
425
+ UIX-specific TODO values, and add UIX-specific test cases.
426
+ 5. Delete UIX's `test/` directory.
427
+
428
+ ---
429
+
430
+ ## License
431
+
432
+ MIT — see [LICENSE](LICENSE).