feat(v1.7.0+v1.8.0): Vues Améliorées + Sub-items & Dépendances
v1.7.0 — View Management:
- PUT /db/views/{id}/config (group_by, card_size, cover, visible_properties)
- POST /db/{id}/views/save-as (save current state as new view)
- GET /db/{id}/views/api (list views)
v1.8.0 — Sub-items & Dependencies:
- parent_id auto-référence: GET/POST /db/{id}/pages/{pid}/sub-items
- Status aggregation: GET .../status-aggregate
- Dependencies: POST .../dependencies, POST .../check-deps (blocking constraint)
- 56/56 tests passent (+7 tests)
- Version 1.6.0 → 1.8.0
This commit is contained in:
+130
-5
@@ -34,7 +34,7 @@ def test_health(client):
|
||||
data = resp.json()
|
||||
assert "status" in data
|
||||
assert data["db"] is True
|
||||
assert data["version"] == "1.6.0"
|
||||
assert data["version"] == "1.8.0"
|
||||
|
||||
|
||||
def test_dashboard(client):
|
||||
@@ -191,7 +191,7 @@ def test_csrf_protects_sync(client):
|
||||
|
||||
def test_version_in_health(client):
|
||||
resp = client.get("/api/health")
|
||||
assert resp.json()["version"] == "1.6.0"
|
||||
assert resp.json()["version"] == "1.8.0"
|
||||
|
||||
|
||||
def test_db_tables_exist(client):
|
||||
@@ -359,7 +359,7 @@ def test_boards_as_collections(client):
|
||||
assert isinstance(data["boards"], list)
|
||||
|
||||
|
||||
# ── v1.6.0: Collection Properties ──
|
||||
# ── v1.8.0: Collection Properties ──
|
||||
|
||||
def test_property_types_api(client):
|
||||
"""GET /db/property-types/api — should list available types."""
|
||||
@@ -469,7 +469,7 @@ def test_collection_properties_validation(client):
|
||||
client.delete(f"/db/api/{coll_id}")
|
||||
|
||||
|
||||
# ── v1.6.0: Relations, Rollups, Formulas ──
|
||||
# ── v1.8.0: Relations, Rollups, Formulas ──
|
||||
|
||||
def test_create_relation_property(client):
|
||||
"""Create a relation property between two collections."""
|
||||
@@ -607,7 +607,7 @@ def test_formula_empty_expression(client):
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
# ── v1.6.0: Views (Calendar, Gallery, List, Timeline) ──
|
||||
# ── v1.8.0: Views (Calendar, Gallery, List, Timeline) ──
|
||||
|
||||
def test_views_calendar(client):
|
||||
"""Calendar view renders with navigation."""
|
||||
@@ -671,3 +671,128 @@ def test_views_calendar_navigation(client):
|
||||
assert resp.status_code == 200
|
||||
assert "August" in resp.text or "août" in resp.text
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
# ── v1.7.0: View Management ──
|
||||
|
||||
def test_view_config_update(client):
|
||||
"""Update view config (card_size, group_by)."""
|
||||
r = client.post("/db/api", json={"name": "V Config"})
|
||||
cid = r.json()["id"]
|
||||
views = client.get(f"/db/{cid}/views/api").json()["views"]
|
||||
vid = views[0]["id"]
|
||||
|
||||
resp = client.put(f"/db/views/{vid}/config", json={
|
||||
"card_size": "large", "group_by": "Status",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["config"]["card_size"] == "large"
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
def test_save_view_as(client):
|
||||
"""Save current state as new view."""
|
||||
r = client.post("/db/api", json={"name": "Save As"})
|
||||
cid = r.json()["id"]
|
||||
|
||||
resp = client.post(f"/db/{cid}/views/save-as", json={
|
||||
"name": "My Kanban", "view_type": "board",
|
||||
"config": {"group_by": "Priority"},
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["name"] == "My Kanban"
|
||||
|
||||
views = client.get(f"/db/{cid}/views/api").json()["views"]
|
||||
assert len(views) == 2
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
def test_list_views(client):
|
||||
"""List views for a collection."""
|
||||
r = client.post("/db/api", json={"name": "V List"})
|
||||
cid = r.json()["id"]
|
||||
resp = client.get(f"/db/{cid}/views/api")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.json()["views"]) == 1 # default view
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
# ── v1.8.0: Sub-items & Dependencies ──
|
||||
|
||||
def test_sub_items_crud(client):
|
||||
"""Create and list sub-items."""
|
||||
r = client.post("/db/api", json={"name": "Sub DB"})
|
||||
cid = r.json()["id"]
|
||||
p = client.post(f"/db/{cid}/pages/api", json={"title": "Parent Task"})
|
||||
pid = p.json()["id"]
|
||||
|
||||
# Create sub-item
|
||||
resp = client.post(f"/db/{cid}/pages/{pid}/sub-items", json={"title": "Child 1"})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["parent_id"] == pid
|
||||
|
||||
resp2 = client.post(f"/db/{cid}/pages/{pid}/sub-items", json={"title": "Child 2"})
|
||||
assert resp2.status_code == 200
|
||||
|
||||
# List
|
||||
items = client.get(f"/db/{cid}/pages/{pid}/sub-items").json()["sub_items"]
|
||||
assert len(items) == 2
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
def test_status_aggregate(client):
|
||||
"""Aggregate child statuses."""
|
||||
r = client.post("/db/api", json={"name": "Agg DB"})
|
||||
cid = r.json()["id"]
|
||||
p = client.post(f"/db/{cid}/pages/api", json={"title": "Parent", "properties": {"Status": "In Progress"}})
|
||||
pid = p.json()["id"]
|
||||
|
||||
client.post(f"/db/{cid}/pages/{pid}/sub-items", json={"title": "C1", "properties": {"Status": "Done"}})
|
||||
client.post(f"/db/{cid}/pages/{pid}/sub-items", json={"title": "C2", "properties": {"Status": "In Progress"}})
|
||||
|
||||
resp = client.get(f"/db/{cid}/pages/{pid}/status-aggregate")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["total"] == 2
|
||||
assert data["done"] == 1
|
||||
assert data["all_done"] is False
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
def test_dependencies_check(client):
|
||||
"""Dependency constraint check."""
|
||||
r = client.post("/db/api", json={"name": "Dep DB"})
|
||||
cid = r.json()["id"]
|
||||
a = client.post(f"/db/{cid}/pages/api", json={"title": "Task A", "properties": {"Status": "In Progress"}})
|
||||
b = client.post(f"/db/{cid}/pages/api", json={"title": "Task B", "properties": {"Status": "Done"}})
|
||||
aid, bid = a.json()["id"], b.json()["id"]
|
||||
|
||||
# A blocks B
|
||||
client.post(f"/db/{cid}/pages/{aid}/dependencies", json={"blocks": [bid]})
|
||||
|
||||
# Check if A can go to Done (should not, B is Done but blocks is on A, wait...)
|
||||
# B is Done, so A CAN transition
|
||||
resp = client.post(f"/db/{cid}/pages/{aid}/check-deps", json={"new_status": "Done"})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["can_transition"] is True
|
||||
assert resp.json()["blocked_by"] == []
|
||||
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
|
||||
def test_dependencies_blocked(client):
|
||||
"""Dependency blocks transition when blocker is not done."""
|
||||
r = client.post("/db/api", json={"name": "Block DB"})
|
||||
cid = r.json()["id"]
|
||||
a = client.post(f"/db/{cid}/pages/api", json={"title": "Task A", "properties": {"Status": "In Progress"}})
|
||||
b = client.post(f"/db/{cid}/pages/api", json={"title": "Task B", "properties": {"Status": "Todo"}})
|
||||
aid, bid = a.json()["id"], b.json()["id"]
|
||||
|
||||
# A blocks B — A depends on B being done
|
||||
client.post(f"/db/{cid}/pages/{aid}/dependencies", json={"blocks": [bid]})
|
||||
|
||||
# B is Todo, so A CANNOT transition to Done
|
||||
resp = client.post(f"/db/{cid}/pages/{aid}/check-deps", json={"new_status": "Done"})
|
||||
assert resp.json()["can_transition"] is False
|
||||
assert len(resp.json()["blocked_by"]) == 1
|
||||
client.delete(f"/db/api/{cid}")
|
||||
|
||||
Reference in New Issue
Block a user