"""CRUD des éléments d'un support de formation (document_engine/elements/) — sans Flask, directement contre le support.db créé par db.create_support. Couvre : CRUD complet, rangées (parent_id), réinsertion (move_document_element vers une nouvelle rangée/position), suppression en cascade d'une rangée avec enfants, et les attributs de position/taille/rotation d'une forme.""" from typing import Any import db import document_engine as doc_engine def _new_support(tmp_support_slug_cleanup: Any, name: str = "Support de test") -> str: slug = db.create_support(name, owner_folder="90") tmp_support_slug_cleanup(slug) return slug def test_add_and_get_document_element_uses_default_attributes(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) element_id = doc_engine.add_document_element(slug, "titre") element = doc_engine.get_document_element(slug, element_id) assert element is not None assert element["kind"] == "titre" assert element["parent_id"] is None assert element["attributes"]["content"] == "Nouveau titre" def test_get_document_element_returns_none_for_missing_id(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) assert doc_engine.get_document_element(slug, 999) is None def test_list_document_elements_groups_row_children_together(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) row_id = doc_engine.add_document_element(slug, "row") top_titre_id = doc_engine.add_document_element(slug, "titre") child_a_id = doc_engine.add_document_element(slug, "paragraphe", parent_id=row_id) child_b_id = doc_engine.add_document_element(slug, "image", parent_id=row_id) elements = doc_engine.list_document_elements(slug) ids_in_order = [el["id"] for el in elements] # Les deux top-niveau (row_id, top_titre_id) groupés avant les deux # enfants de la rangée (child_a_id, child_b_id) — tri (parent_id, order_index). assert ids_in_order.index(row_id) < ids_in_order.index(child_a_id) assert ids_in_order.index(top_titre_id) < ids_in_order.index(child_a_id) assert ids_in_order.index(child_a_id) < ids_in_order.index(child_b_id) def test_update_document_element_attributes_replaces_whole_json(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) element_id = doc_engine.add_document_element(slug, "paragraphe") doc_engine.update_document_element_attributes(slug, element_id, {"content": "Texte modifié", "style": "legende"}) element = doc_engine.get_document_element(slug, element_id) assert element is not None assert element["attributes"] == {"content": "Texte modifié", "style": "legende"} def test_shape_default_attributes_carry_real_position_and_size(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) element_id = doc_engine.add_document_element(slug, "rectangle") element = doc_engine.get_document_element(slug, element_id) assert element is not None attrs = element["attributes"] assert attrs["x"] == 40 assert attrs["y"] == 40 assert attrs["width"] == 160 assert attrs["height"] == 100 assert attrs["rotation"] == 0 assert attrs["z_index"] == 1 doc_engine.update_document_element_attributes( slug, element_id, {**attrs, "x": 120, "y": 80, "width": 200, "height": 150, "rotation": 45, "z_index": 3}, ) moved = doc_engine.get_document_element(slug, element_id) assert moved is not None assert moved["attributes"]["x"] == 120 assert moved["attributes"]["rotation"] == 45 assert moved["attributes"]["z_index"] == 3 def test_move_document_element_reorders_within_same_parent(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) first_id = doc_engine.add_document_element(slug, "titre") second_id = doc_engine.add_document_element(slug, "paragraphe") third_id = doc_engine.add_document_element(slug, "image") doc_engine.move_document_element(slug, third_id, None, 0) elements = doc_engine.list_document_elements(slug) top_level = [el for el in elements if el["parent_id"] is None] ordered_ids = [el["id"] for el in sorted(top_level, key=lambda el: el["order_index"])] assert ordered_ids == [third_id, first_id, second_id] def test_move_document_element_reparents_into_a_row(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) row_id = doc_engine.add_document_element(slug, "row") lone_titre_id = doc_engine.add_document_element(slug, "titre") doc_engine.move_document_element(slug, lone_titre_id, row_id, 0) moved = doc_engine.get_document_element(slug, lone_titre_id) assert moved is not None assert moved["parent_id"] == row_id assert moved["order_index"] == 0 def test_delete_document_element_cascades_to_row_children(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) row_id = doc_engine.add_document_element(slug, "row") child_id = doc_engine.add_document_element(slug, "titre", parent_id=row_id) doc_engine.delete_document_element(slug, row_id) assert doc_engine.get_document_element(slug, row_id) is None assert doc_engine.get_document_element(slug, child_id) is None def test_delete_document_element_on_missing_id_is_a_noop(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) doc_engine.delete_document_element(slug, 999) def test_render_document_includes_row_children_and_shape_position(tmp_support_slug_cleanup: Any) -> None: slug = _new_support(tmp_support_slug_cleanup) row_id = doc_engine.add_document_element(slug, "row") doc_engine.add_document_element(slug, "titre", parent_id=row_id) rect_id = doc_engine.add_document_element(slug, "rectangle") html = doc_engine.render_document(doc_engine.list_document_elements(slug)) assert 'data-kind="row"' in html assert 'data-kind="titre"' in html assert f'data-element-id="{rect_id}"' in html assert "left:40px" in html assert "top:40px" in html