I can add tables
Refactoring DbEngine Fixing unit tests Fixing unit tests Fixing unit tests Refactored DbManager for datagrid Improving front end performance I can add new table Fixed sidebar closing when clicking on it Fix drag event rebinding, improve listener options, and add debug Prevent duplicate drag event bindings with a dataset flag and ensure consistent scrollbar functionality. Change wheel event listener to passive mode for better performance. Refactor function naming for consistency, and add debug logs for event handling. Refactor Datagrid bindings and default state handling. Updated Javascript to conditionally rebind Datagrid on specific events. Improved Python components by handling empty DataFrame cases and removing redundant code. Revised default state initialization in settings for better handling of mutable fields. Added Rowindex visualisation support Working on Debugger with own implementation of JsonViewer Working on JsonViewer.py Fixed unit tests Adding unit tests I can fold and unfold fixed unit tests Adding css for debugger Added tooltip management Adding debugger functionalities Refactor serializers and improve error handling in DB engine Fixed error where tables were overwritten I can display footer menu Working on footer. Refactoring how heights are managed Refactored scrollbars management Working on footer menu I can display footer menu + fixed unit tests Fixed unit tests Updated click management I can display aggregations in footers Added docker management Refactor input handling and improve config defaults Fixed scrollbars colors Refactored tooltip management Improved tooltip management Improving FilterAll
This commit is contained in:
107
tests/helpers.py
107
tests/helpers.py
@@ -8,7 +8,7 @@ import pandas as pd
|
||||
from bs4 import BeautifulSoup
|
||||
from fastcore.basics import NotStr
|
||||
from fastcore.xml import to_xml
|
||||
from fasthtml.components import html2ft, Div
|
||||
from fasthtml.components import html2ft, Div, Span
|
||||
|
||||
pattern = r"""(?P<tag>\w+)(?:#(?P<id>[\w-]+))?(?P<attributes>(?:\[\w+=['"]?[\w_-]+['"]?\])*)"""
|
||||
attr_pattern = r"""\[(?P<name>\w+)=['"]?(?P<value>[\w_-]+)['"]?\]"""
|
||||
@@ -17,6 +17,7 @@ compiled_pattern = re.compile(pattern)
|
||||
compiled_attr_pattern = re.compile(attr_pattern)
|
||||
compiled_svg_pattern = re.compile(svg_pattern)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class DoNotCheck:
|
||||
desc: str = None
|
||||
@@ -33,6 +34,7 @@ class StartsWith:
|
||||
"""
|
||||
s: str
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Contains:
|
||||
"""
|
||||
@@ -40,6 +42,7 @@ class Contains:
|
||||
"""
|
||||
s: str
|
||||
|
||||
|
||||
Empty = EmptyElement()
|
||||
|
||||
|
||||
@@ -141,7 +144,10 @@ def search_elements_by_name(ft, tag: str = None, attrs: dict = None, comparison_
|
||||
# Recursive case: search through the children
|
||||
for child in _ft.children:
|
||||
result.extend(_search_elements_by_name(child))
|
||||
|
||||
elif isinstance(_ft, (list, tuple)):
|
||||
for _item in _ft:
|
||||
result.extend(_search_elements_by_name(_item))
|
||||
|
||||
return result
|
||||
|
||||
if isinstance(ft, list):
|
||||
@@ -156,7 +162,7 @@ def search_elements_by_name(ft, tag: str = None, attrs: dict = None, comparison_
|
||||
def search_elements_by_path(ft, path: str, attrs: dict = None):
|
||||
"""
|
||||
Selects elements that match a given path. The path is a dot-separated list of elements.
|
||||
One the path if found, the optional attributes are compared against the last element's
|
||||
Once the path if found, the optional attributes are compared against the last element's
|
||||
attributes.
|
||||
Note the path may not start at the root node of the tree structure.
|
||||
|
||||
@@ -188,10 +194,10 @@ def search_elements_by_path(ft, path: str, attrs: dict = None):
|
||||
return _find(ft, "")
|
||||
|
||||
|
||||
def search_first_with_attribute(ft, tag, attribute
|
||||
):
|
||||
def search_first_with_attribute(ft, tag, attribute):
|
||||
"""
|
||||
Browse ft and its children to find the first element that matches the tag and has the attribute defined
|
||||
We do not care about the value of the attribute, just the presence of it.
|
||||
if tag is None, it will return the first element with the attribute
|
||||
:param ft:
|
||||
:param tag:
|
||||
@@ -266,6 +272,83 @@ def matches(actual, expected, path=""):
|
||||
|
||||
return type(x)
|
||||
|
||||
def _debug(_actual, _expected):
|
||||
str_actual = _debug_print_actual(_actual, _expected, "", 3)
|
||||
str_expected = _debug_print_expected(_expected, "", 2)
|
||||
return f"\nactual={str_actual}\nexpected={str_expected}"
|
||||
|
||||
def _debug_value(x):
|
||||
if x in ("** NOT FOUND **", "** NONE **", "** NO MORE CHILDREN **"):
|
||||
return x
|
||||
elif isinstance(x, str):
|
||||
return f"'{x}'" if "'" not in x else f'"{x}"'
|
||||
else:
|
||||
return x
|
||||
|
||||
def _debug_print_actual(_actual, _expected, indent, max_level):
|
||||
# debug print both actual and expected, showing only expected elements
|
||||
if max_level == 0:
|
||||
return ""
|
||||
|
||||
if _actual is None:
|
||||
return f"{indent}** NONE **"
|
||||
|
||||
if not hasattr(_actual, "tag") or not hasattr(_expected, "tag"):
|
||||
return f"{indent}{_actual}"
|
||||
|
||||
str_actual = f"{indent}({_actual.tag}"
|
||||
first_attr = True
|
||||
for attr in _expected.attrs:
|
||||
comma = " " if first_attr else ", "
|
||||
str_actual += f"{comma}{attr}={_debug_value(_actual.attrs.get(attr, '** NOT FOUND **'))}"
|
||||
first_attr = False
|
||||
|
||||
if len(_expected.children) == 0 and len(_actual.children) and max_level > 1:
|
||||
# force recursion to see sub levels
|
||||
for _actual_child in _actual.children:
|
||||
str_child_a = _debug_print_actual(_actual_child, _actual_child, indent + " ", max_level - 1)
|
||||
str_actual += "\n" + str_child_a if str_child_a else ""
|
||||
|
||||
else:
|
||||
|
||||
for index, _expected_child in enumerate(_expected.children):
|
||||
if len(_actual.children) > index:
|
||||
_actual_child = _actual.children[index]
|
||||
else:
|
||||
_actual_child = "** NO MORE CHILDREN **"
|
||||
|
||||
str_child_a = _debug_print_actual(_actual_child, _expected_child, indent + " ", max_level - 1)
|
||||
str_actual += "\n" + str_child_a if str_child_a else ""
|
||||
|
||||
str_actual += ")"
|
||||
|
||||
return str_actual
|
||||
|
||||
def _debug_print_expected(_expected, indent, max_level):
|
||||
if max_level == 0:
|
||||
return ""
|
||||
|
||||
if _expected is None:
|
||||
return f"{indent}** NONE **"
|
||||
|
||||
if not hasattr(_expected, "tag"):
|
||||
return f"{indent}{_expected}"
|
||||
|
||||
str_expected = f"{indent}({_expected.tag}"
|
||||
first_attr = True
|
||||
for attr in _expected.attrs:
|
||||
comma = " " if first_attr else ", "
|
||||
str_expected += f"{comma}{attr}={_expected.attrs[attr]}"
|
||||
first_attr = False
|
||||
|
||||
for _expected_child in _expected.children:
|
||||
str_child_e = _debug_print_expected(_expected_child, indent + " ", max_level - 1)
|
||||
str_expected += "\n" + str_child_e if str_child_e else ""
|
||||
|
||||
str_expected += ")"
|
||||
|
||||
return str_expected
|
||||
|
||||
if actual is None and expected is not None:
|
||||
assert False, f"{print_path(path)}actual is None !"
|
||||
|
||||
@@ -278,11 +361,11 @@ def matches(actual, expected, path=""):
|
||||
return True
|
||||
|
||||
assert _type(actual) == _type(expected) or (hasattr(actual, "tag") and hasattr(expected, "tag")), \
|
||||
f"{print_path(path)}The types are different: {type(actual)} != {type(expected)}, ({actual} != {expected})."
|
||||
f"{print_path(path)}The types are different: {type(actual)} != {type(expected)}{_debug(actual, expected)}."
|
||||
|
||||
if isinstance(expected, (list, tuple)):
|
||||
assert len(actual) >= len(expected), \
|
||||
f"{print_path(path)}Some required elements are missing: {actual} != {expected}."
|
||||
f"{print_path(path)}Some required elements are missing: {len(actual)=} < {len(expected)}, \n{_debug(actual, expected)}."
|
||||
|
||||
for actual_child, expected_child in zip(actual, expected):
|
||||
assert matches(actual_child, expected_child)
|
||||
@@ -329,7 +412,7 @@ def matches(actual, expected, path=""):
|
||||
pass
|
||||
else:
|
||||
assert len(actual.children) >= len(expected.children), \
|
||||
f"{print_path(path)}Some required elements are missing: actual={actual.children} != expected={expected.children}."
|
||||
f"{print_path(path)}Some required elements are missing: len(actual)={len(actual.children)} < len(expected)={len(expected.children)}{_debug(actual, expected)}."
|
||||
|
||||
for actual_child, expected_child in zip(actual.children, expected.children):
|
||||
matches(actual_child, expected_child, path)
|
||||
@@ -547,3 +630,11 @@ def icon(name: str):
|
||||
|
||||
def div_icon(name: str):
|
||||
return Div(NotStr(f'<svg name="{name}"'))
|
||||
|
||||
|
||||
def span_icon(name: str):
|
||||
return Span(NotStr(f'<svg name="{name}"'))
|
||||
|
||||
|
||||
def div_ellipsis(text: str):
|
||||
return Div(text, cls="truncate", data_tooltip=text)
|
||||
|
||||
Reference in New Issue
Block a user