feat(ui): pattern modes, bundles, and zone content kind
Add profile/preset/sequence JSON import and export; map preset mode to wire n6 with a mode dropdown for multi-mode patterns; zone edit shows presets or sequences only with content_kind on save; update catalogue and tests for merged pattern names. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -50,6 +50,17 @@ def _find_id_by_field(list_resp_json: Dict[str, Any], field: str, value: str) ->
|
||||
raise AssertionError(f"Could not find id for {field}={value!r}")
|
||||
|
||||
|
||||
def _create_and_apply_profile(c: requests.Session, base_url: str) -> str:
|
||||
"""Sequences/scenes/presets need an active profile in session."""
|
||||
unique_profile_name = f"pytest-profile-{uuid.uuid4().hex[:8]}"
|
||||
resp = c.post(f"{base_url}/profiles", json={"name": unique_profile_name})
|
||||
assert resp.status_code == 201
|
||||
profile_id = next(iter(resp.json().keys()))
|
||||
resp = c.post(f"{base_url}/profiles/{profile_id}/apply")
|
||||
assert resp.status_code == 200
|
||||
return str(profile_id)
|
||||
|
||||
|
||||
def _start_microdot_server(app: Microdot, host: str, port: int):
|
||||
"""
|
||||
Start Microdot server on a background thread.
|
||||
@@ -474,6 +485,36 @@ def test_profiles_presets_zones_endpoints(server, monkeypatch):
|
||||
resp = c.delete(f"{base_url}/zones/{zone_id}")
|
||||
assert resp.status_code == 200
|
||||
|
||||
resp = c.get(f"{base_url}/profiles/{profile_id}/export")
|
||||
assert resp.status_code == 200
|
||||
bundle = resp.json()
|
||||
assert bundle.get("kind") == "profile"
|
||||
assert isinstance(bundle.get("presets"), dict)
|
||||
|
||||
import_name = f"pytest-imported-{uuid.uuid4().hex[:8]}"
|
||||
resp = c.post(
|
||||
f"{base_url}/profiles/import",
|
||||
json={"bundle": bundle, "name": import_name, "apply": False},
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
imported_profile_id = resp.json().get("id") or next(
|
||||
k for k in resp.json().keys() if k != "id"
|
||||
)
|
||||
resp = c.delete(f"{base_url}/profiles/{imported_profile_id}")
|
||||
assert resp.status_code == 200
|
||||
|
||||
resp = c.get(f"{base_url}/presets/{first_preset_id}/export")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json().get("kind") == "preset"
|
||||
resp = c.post(
|
||||
f"{base_url}/presets/import",
|
||||
json={"bundle": resp.json()},
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
imported_preset_id = next(iter(resp.json().keys()))
|
||||
resp = c.delete(f"{base_url}/presets/{imported_preset_id}")
|
||||
assert resp.status_code == 200
|
||||
|
||||
# Profile clone + update endpoints.
|
||||
clone_name = f"pytest-profile-clone-{uuid.uuid4().hex[:8]}"
|
||||
resp = c.post(f"{base_url}/profiles/{profile_id}/clone", json={"name": clone_name})
|
||||
@@ -508,6 +549,8 @@ def test_groups_sequences_scenes_palettes_patterns_endpoints(server):
|
||||
base_url: str = server["base_url"]
|
||||
sender: DummySender = server["sender"]
|
||||
|
||||
_create_and_apply_profile(c, base_url)
|
||||
|
||||
# Groups.
|
||||
unique_group_name = f"pytest-group-{uuid.uuid4().hex[:8]}"
|
||||
resp = c.post(f"{base_url}/groups", json={"name": unique_group_name})
|
||||
@@ -715,6 +758,13 @@ def test_groups_sequences_scenes_palettes_patterns_endpoints(server):
|
||||
assert resp.status_code == 200
|
||||
definitions = resp.json()
|
||||
assert isinstance(definitions, dict)
|
||||
assert "colour_cycle" in definitions
|
||||
cc_mode = definitions["colour_cycle"].get("mode")
|
||||
assert isinstance(cc_mode, dict)
|
||||
assert "0" in cc_mode and "1" in cc_mode
|
||||
assert "blink" in definitions
|
||||
blink_mode = definitions["blink"].get("mode")
|
||||
assert not isinstance(blink_mode, dict) or len(blink_mode) < 2
|
||||
|
||||
pattern_id = f"pytest_pattern_{uuid.uuid4().hex[:8]}"
|
||||
resp = c.post(
|
||||
|
||||
Reference in New Issue
Block a user