Added Application HolidayViewer

This commit is contained in:
Kodjo Sossouvi
2025-06-27 07:26:58 +02:00
parent 66ea45f501
commit 9f4b8ab4d0
87 changed files with 3756 additions and 212 deletions

View File

@@ -1,8 +1,7 @@
import pytest
from fasthtml.components import *
from components.debugger.components.JsonViewer import JsonViewer, DictNode, ListNode, ValueNode
from helpers import matches, span_icon, search_elements_by_name
from components.debugger.components.JsonViewer import *
from helpers import matches, span_icon, search_elements_by_name, extract_jsonviewer_node
JSON_VIEWER_INSTANCE_ID = "json_viewer"
ML_20 = "margin-left: 20px;"
@@ -19,6 +18,11 @@ def json_viewer(session):
return JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, {})
@pytest.fixture()
def helper():
return JsonViewerHelper()
def jv_id(x):
return f"{JSON_VIEWER_INSTANCE_ID}-{x}"
@@ -61,7 +65,7 @@ def test_i_can_render(json_viewer):
def test_i_can_render_simple_value(session, value, expected_inner):
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
actual = jsonv.__ft__()
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id("root")}"})[0]
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
expected = Div(
Div(
@@ -70,7 +74,7 @@ def test_i_can_render_simple_value(session, value, expected_inner):
expected_inner,
style=ML_20),
id=f"{jv_id("root")}")
id=f"{jv_id('root')}")
assert matches(to_compare, expected)
@@ -78,21 +82,25 @@ def test_i_can_render_simple_value(session, value, expected_inner):
def test_i_can_render_expanded_list_node(session):
value = [1, "hello", True]
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Force expansion of the node
jsonv.set_folding_mode("expand")
actual = jsonv.__ft__()
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id("root")}"})[0]
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
to_compare = to_compare.children[0] # I want to compare what is inside the div
expected_inner = Span("[",
Div(None, Span("0 : "), Span('1'), style=ML_20),
Div(None, Span("1 : "), Span('"hello"'), style=ML_20),
Div(None, Span("2 : "), Span('true'), style=ML_20),
Div("]")),
Div(None, Span("0 : "), Span('1', cls=f"{CLS_PREFIX}-number"), style=ML_20),
Div(None, Span("1 : "), Span('"hello"', cls=f"{CLS_PREFIX}-string"), style=ML_20),
Div(None, Span("2 : "), Span('true', cls=f"{CLS_PREFIX}-bool"), style=ML_20),
Div("]"))
expected = Div(
span_icon("expanded"),
None, # 'key :' is missing for the first node
expected_inner,
style=ML_20)
style=ML_20,
id=jv_id(0))
assert matches(to_compare, expected)
@@ -100,21 +108,25 @@ def test_i_can_render_expanded_list_node(session):
def test_i_can_render_expanded_dict_node(session):
value = {"a": 1, "b": "hello", "c": True}
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Force expansion of the node
jsonv.set_folding_mode("expand")
actual = jsonv.__ft__()
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id("root")}"})[0]
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
to_compare = to_compare.children[0] # I want to compare what is inside the div
expected_inner = Span("{",
Div(None, Span("a : "), Span('1'), style=ML_20),
Div(None, Span("b : "), Span('"hello"'), style=ML_20),
Div(None, Span("c : "), Span('true'), style=ML_20),
Div(None, Span("a : "), Span('1', cls=f"{CLS_PREFIX}-number"), style=ML_20),
Div(None, Span("b : "), Span('"hello"', cls=f"{CLS_PREFIX}-string"), style=ML_20),
Div(None, Span("c : "), Span('true', cls=f"{CLS_PREFIX}-bool"), style=ML_20),
Div("}"))
expected = Div(
span_icon("expanded"),
None, # 'key :' is missing for the first node
expected_inner,
style=ML_20)
style=ML_20,
id=jv_id(0))
assert matches(to_compare, expected)
@@ -122,8 +134,11 @@ def test_i_can_render_expanded_dict_node(session):
def test_i_can_render_expanded_list_of_dict_node(session):
value = [{"a": 1, "b": "hello"}]
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Force expansion of all nodes
jsonv.set_folding_mode("expand")
actual = jsonv.__ft__()
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id("root")}"})[0]
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
to_compare = to_compare.children[0] # I want to compare what is inside the div
expected_inner = Span("[",
@@ -131,9 +146,10 @@ def test_i_can_render_expanded_list_of_dict_node(session):
Div(span_icon("expanded"),
Span("0 : "),
Span("{",
Div(None, Span("a : "), Span('1'), style=ML_20),
Div(None, Span("b : "), Span('"hello"'), style=ML_20),
Div(None, Span("a : "), Span('1', cls=f"{CLS_PREFIX}-number"), style=ML_20),
Div(None, Span("b : "), Span('"hello"', cls=f"{CLS_PREFIX}-string"), style=ML_20),
Div("}")),
style=ML_20,
id=f"{jv_id(1)}"),
Div("]"))
@@ -142,11 +158,193 @@ def test_i_can_render_expanded_list_of_dict_node(session):
span_icon("expanded"),
None, # 'key :' is missing for the first node
expected_inner,
style=ML_20)
style=ML_20,
id=jv_id(0))
assert matches(to_compare, expected)
def test_render_with_collapse_folding_mode(session):
# Create a nested structure to test collapse rendering
value = {"a": [1, 2, 3], "b": {"x": "y", "z": True}}
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Ensure folding mode is set to collapse (should be default)
jsonv.set_folding_mode("collapse")
assert jsonv.get_folding_mode() == "collapse"
actual = jsonv.__ft__()
root_div = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
# In collapse mode, the first level should show collapsed representations
# The dict node should be rendered as "{...}"
first_level_div = root_div.children[0]
# Verify that the first level shows a collapsed view
expected_first_level = Div(
span_icon("collapsed"),
None, # No key for the root node
Span("{...}", id=jv_id(0)),
style=ML_20,
id=jv_id(0)
)
assert matches(first_level_div, expected_first_level)
def test_render_with_specific_node_expanded_in_collapse_mode(session):
# Create a nested structure to test mixed collapse/expand rendering
value = {"a": [1, 2, 3], "b": {"x": "y", "z": True}}
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Ensure folding mode is set to collapse
jsonv.set_folding_mode(FoldingMode.COLLAPSE)
# Manually expand the root node
jsonv.set_node_folding(f"{JSON_VIEWER_INSTANCE_ID}-0", "expand")
actual = jsonv.__ft__()
root_div = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
first_level_div = root_div.children[0]
as_node = extract_jsonviewer_node(first_level_div)
# The first level should now be expanded but children should be collapsed
assert as_node.is_expanded is True
# Find div with "a" key
a_node = as_node.find("a")
b_node = as_node.find("b")
# Verify that both a and b nodes show collapsed representations
assert a_node is not None
assert b_node is not None
assert a_node.is_expanded is False
assert a_node.text_value() == "[...]"
assert b_node.is_expanded is False
assert b_node.text_value() == "{...}"
def test_multiple_folding_levels_in_collapse_mode(session):
# Create a deeply nested structure
value = {"level1": {"level2": {"level3": [1, 2, 3]}}}
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Set folding mode to collapse
jsonv.set_folding_mode(FoldingMode.COLLAPSE)
# Expand the first two levels
jsonv.set_node_folding(f"{jsonv.get_id()}-0", FoldingMode.EXPAND) # top level
jsonv.set_node_folding(f"{jsonv.get_id()}-1", FoldingMode.EXPAND) # level1
jsonv.set_node_folding(f"{jsonv.get_id()}-2", FoldingMode.EXPAND) # level2
actual = jsonv.__ft__()
root_div = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
# Navigate to level3 to verify it's still collapsed
first_level_div = root_div.children[0]
first_level_node = extract_jsonviewer_node(first_level_div)
assert first_level_node.is_expanded is True
# Find level2 in the rendered structure
level2_node = first_level_node.find("level1.level2")
assert level2_node is not None
assert level2_node.is_expanded is True
# Find level3 in the rendered structure
level3_node = level2_node.find("level3")
assert level3_node is not None
assert level3_node.is_expanded is False
assert level3_node.text_value() == "[...]"
def test_toggle_between_folding_modes(session):
value = {"a": [1, 2, 3], "b": {"x": "y"}}
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, value)
# Start with collapse mode
jsonv.set_folding_mode("collapse")
# Expand specific node
jsonv.set_node_folding(f"{JSON_VIEWER_INSTANCE_ID}-0", "expand")
# Verify node is in tracked nodes (exceptions to collapse mode)
assert f"{JSON_VIEWER_INSTANCE_ID}-0" in jsonv._nodes_to_track
# Now switch to expand mode
jsonv.set_folding_mode("expand")
# Tracked nodes should be cleared
assert len(jsonv._nodes_to_track) == 0
# Collapse specific node
jsonv.set_node_folding(f"{JSON_VIEWER_INSTANCE_ID}-0", "collapse")
# Verify node is in tracked nodes (exceptions to expand mode)
assert f"{JSON_VIEWER_INSTANCE_ID}-0" in jsonv._nodes_to_track
# Render and verify the output
actual = jsonv.__ft__()
root_div = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
first_level_div = root_div.children[0]
# First level should be collapsed in an otherwise expanded tree
as_node = extract_jsonviewer_node(first_level_div)
assert as_node.is_expanded is False
assert as_node.text_value() == "{...}"
def test_custom_hook_rendering(session, helper):
# Define a custom hook for testing
def custom_predicate(key, node, h):
return isinstance(node.value, str) and node.value == "custom_hook_test"
def custom_renderer(key, node, h):
return Span("CUSTOM_HOOK_RENDER", cls="custom-hook-class")
hooks = [(custom_predicate, custom_renderer)]
# Create JsonViewer with the custom hook
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, "custom_hook_test", hooks=hooks)
actual = jsonv.__ft__()
to_compare = search_elements_by_name(actual, "div", attrs={"id": f"{jv_id('root')}"})[0]
expected = Div(
Div(
None,
None,
Span("CUSTOM_HOOK_RENDER", cls="custom-hook-class"),
style=ML_20),
id=f"{jv_id('root')}")
assert matches(to_compare, expected)
def test_folding_mode_operations(session):
jsonv = JsonViewer(session, JSON_VIEWER_INSTANCE_ID, None, USER_ID, {"a": [1, 2, 3]})
# Check default folding mode
assert jsonv.get_folding_mode() == "collapse"
# Change folding mode
jsonv.set_folding_mode("expand")
assert jsonv.get_folding_mode() == "expand"
# Set node folding
node_id = f"{JSON_VIEWER_INSTANCE_ID}-0"
jsonv.set_node_folding(node_id, "collapse")
# Node should be in tracked nodes since it differs from the default mode
assert node_id in jsonv._nodes_to_track
# Restore to match default mode
jsonv.set_node_folding(node_id, "expand")
assert node_id not in jsonv._nodes_to_track
@pytest.mark.parametrize("input_value, expected_output", [
('Hello World', '"Hello World"'), # No quotes in input
('Hello "World"', "'Hello \"World\"'"), # Contains double quotes
@@ -157,3 +355,16 @@ def test_i_can_render_expanded_list_of_dict_node(session):
def test_add_quotes(input_value, expected_output):
result = JsonViewer.add_quotes(input_value)
assert result == expected_output
def test_helper_is_sha256(helper):
# Valid SHA256
assert helper.is_sha256("a" * 64)
assert helper.is_sha256("0123456789abcdef" * 4)
assert helper.is_sha256("0123456789ABCDEF" * 4)
# Invalid cases
assert not helper.is_sha256("a" * 63) # Too short
assert not helper.is_sha256("a" * 65) # Too long
assert not helper.is_sha256("g" * 64) # Invalid character
assert not helper.is_sha256("test") # Not a hash